def make_naive_line(a, b, diag=False): import pdb pdb.set_trace() def anglebtw(a, b): # atan2 gives angle between origin and point; to get angle between two # points, move one to the origin and the other by the corresponding # amount, then use atan2 on the result. Subtracting one from the other # is the same as moving one to the origin and then moving the other the # same amount. c = b - a return math.degrees(math.atan2(c.y, c.x)) def is_naive(a, b): return anglebtw(a, b) % 45 == 0 if not is_naive(a, b): raise UserWarning("Not a naive line", a, b) points = set() diff = lambda a, b: (abs(b - a), get_sign(b - a)) valx, sigx = diff(a.x, b.x) valy, sigy = diff(a.y, b.y) if valx == 0 or valy == 0 or diag: for i in range(max(valx, valy) + 1): new_point = aoc.Point(x=a.x + (i * sigx), y=a.y + (i * sigy)) points.add(new_point) return points
def explore(gridd, start, future, basin): dirs = [ aoc.Point(x=start.x, y=start.y - 1), aoc.Point(x=start.x, y=start.y + 1), aoc.Point(x=start.x - 1, y=start.y), aoc.Point(x=start.x + 1, y=start.y), ] near = keyfilter(lambda x: x in dirs and x not in basin, gridd) for pt, data in near.items(): if data and int(data[0]) not in (None, 9): if pt not in future: future.append(pt) if pt not in basin: basin.append(pt) for pt in future: basin = explore(gridd, pt, [], basin) return basin
def make_line_diag(instr): a, b = instr points = set() if a.x == b.x: if a.y < b.y: fills = range(a.y, b.y + 1) for n in fills: points.add(aoc.Point(x=a.x, y=n)) elif a.y > b.y: fills = range(b.y, a.y + 1) for n in fills: points.add(aoc.Point(x=a.x, y=n)) else: points.add(aoc.Point(x=a.x, y=a.y)) elif a.y == b.y: if a.x < b.x: fills = range(a.x, b.x + 1) for n in fills: points.add(aoc.Point(x=n, y=a.y)) elif a.x > b.x: fills = range(b.x, a.x + 1) for n in fills: points.add(aoc.Point(x=n, y=a.y)) else: points.add(aoc.Point(x=a.x, y=a.y)) else: curr = a points.add(curr) while curr != b: if curr.x < b.x: curr = curr + aoc.Point(1, 0) elif curr.x > b.x: curr = curr - aoc.Point(1, 0) if curr.y < b.y: curr = curr + aoc.Point(0, 1) elif curr.y > b.y: curr = curr - aoc.Point(0, 1) points.add(curr) return points
def make_line(instr): a, b = instr points = set() if a.x == b.x: if a.y < b.y: fills = range(a.y, b.y + 1) for n in fills: points.add(aoc.Point(x=a.x, y=n)) elif a.y > b.y: fills = range(b.y, a.y + 1) for n in fills: points.add(aoc.Point(x=a.x, y=n)) else: points.add(aoc.Point(x=a.x, y=a.y)) elif a.y == b.y: if a.x < b.x: fills = range(a.x, b.x + 1) for n in fills: points.add(aoc.Point(x=n, y=a.y)) elif a.x > b.x: fills = range(b.x, a.x + 1) for n in fills: points.add(aoc.Point(x=n, y=a.y)) else: points.add(aoc.Point(x=a.x, y=a.y)) return points
def get_lows(data): grid = [] gridd = {} for y, row in enumerate(data): for x, char in enumerate(row): grid.append(aoc.Point(x=x, y=y)) gridd[aoc.Point(x=x, y=y)] = (char, None) for pt in gridd: above = gridd.get(aoc.Point(x=pt.x, y=pt.y - 1)) below = gridd.get(aoc.Point(x=pt.x, y=pt.y + 1)) left = gridd.get(aoc.Point(x=pt.x - 1, y=pt.y)) right = gridd.get(aoc.Point(x=pt.x + 1, y=pt.y)) adjs = lcompact([above, below, left, right]) curr = int(gridd[pt][0]) low = True for adj in adjs: if curr >= int(adj[0]): low = False if low: gridd[pt] = (gridd[pt][0], curr + 1) lows = lmap(lambda x: x, lfilter(lambda x: x[1][1] is not None, gridd.items())) return lows
def process_two(data): grid = {} for y, row in enumerate(data): for x, char in enumerate(row): grid[aoc.Point(x=x, y=y)] = (char, None) lows = get_lows(data) basins = lmap(lambda x: explore(grid, x[0], [], [x[0]]), lows) sbasins = sorted(basins, key=len) threebasins = sbasins[-3:] return prod(map(len, threebasins)) return data
def parse_line(line): coords = splitstrip(line, "->") raw_points = [lmap(int, splitstrip(coord, ",")) for coord in coords] points = lmap(lambda x: aoc.Point(*x), raw_points) return points