def path_to_coords(path): if path == '': return Vec(0, 0) return path_to_coords(path[:-1]) + { 'U': Vec(0, -1), 'D': Vec(0, 1), 'L': Vec(-1, 0), 'R': Vec(1, 0) }[path[-1]]
def part2(inp): y_candidates = [] for i in range(min(0, inp.lower[1] - 1), max(abs(y) for _, y in [inp.lower, inp.upper]) + 1): if check_y_hit(Vec(0, i), inp): y_candidates.append(i) x_candidates = [] for i in range(min(0, inp.lower[0] - 1), max(0, inp.upper[0] + 1)): if check_x_hit(Vec(i, 0), inp): x_candidates.append(i) return sum( check_hit(Vec(x, y), inp) for x in x_candidates for y in y_candidates)
def part1(inp): y_vel = 0 for i in range(min(0, inp.lower[1] - 1), max(abs(y) for _, y in [inp.lower, inp.upper]) + 1): if check_y_hit(Vec(0, i), inp): y_vel = i max_y = 0 pos = Vec(0, 0) vel = Vec(0, y_vel) while True: pos, vel = step(pos, vel) if pos[1] < max_y: return max_y max_y = pos[1]
def enhance(img, alg): xmin = min(x for x, _ in img) - 1 xmax = max(x for x, _ in img) + 1 ymin = min(y for _, y in img) - 1 ymax = max(y for _, y in img) + 1 if img.default_factory(): default = alg[-1] else: default = alg[0] return defaultdict( lambda: default, { Vec(x, y): alg[int( ''.join('1' if img[(Vec(x, y) + c)] else '0' for c in square), 2)] for x in range(xmin, xmax + 1) for y in range(ymin, ymax + 1) })
def part2(inp): mapp = {} lx, ly = max(x for x, _ in inp) + 1, max(y for _, y in inp) + 1 for (x, y), v in inp.items(): for a, b in product(range(5), repeat=2): nv = (v + a + b) % 9 mapp[Vec(x + a * lx, y + b * ly)] = 9 if nv == 0 else nv return find_shortest_path(mapp)
def check_hit(vel, target): pos = Vec(0, 0) while True: pos, vel = step(pos, vel) if pos in target: return True elif pos[1] < target.lower[1] and vel[1] < 0: return False
def check_x_hit(vel, target): pos = Vec(0, 0) while True: pos, vel = step(pos, vel) if target.lower[0] <= pos[0] <= target.upper[0]: return True elif vel[0] == 0: return False
def part2(inp): return reduce( operator.mul, reduce( operator.add, map( lambda x: Vec(x[1], x[0] * x[1]), zip(accumulate(dirs[d][1] * n for d, n in inp), (dirs[d][0] * n for d, n in inp)))))
def part2(inp): boundary = {Vec(1, 1)} visited = boundary.copy() for _ in range(50): nb = set() for b in boundary: for d in dirs4: nc = b + d if nc not in visited and is_non_negative(nc) and not is_wall( nc, inp): nb.add(nc) visited.add(nc) boundary = nb return len(visited)
def part1(inp): boundary = {Vec(1, 1)} visited = boundary.copy() for i in count(1): nb = set() for b in boundary: for d in dirs4: nc = b + d if nc == (31, 39): return i if nc not in visited and is_non_negative(nc) and not is_wall( nc, inp): nb.add(nc) visited.add(nc) boundary = nb
def part2(inp): mapp = { Vec(x, y) for y in range(128) for x, c in enumerate(knothash(f'{inp}-{y}')) if c == '1' } regions = 0 while mapp: regions += 1 boundary = {mapp.pop()} while boundary: current = boundary.pop() for d in dirs4: if d + current in mapp: mapp.remove(d + current) boundary.add(d + current) return regions
def find_shortest_path(mapp): start = Vec(0, 0) goal = max(mapp, key=sum) open_set = PriorityQueue() open_set.put(start, 0) g_score = defaultdict(lambda: math.inf, {start: 0}) while open_set: current = open_set.get() if current == goal: return g_score[current] for neighbor in (current + d for d in dirs4 if (current + d) in mapp): tentative_g_score = g_score[current] + mapp[neighbor] if tentative_g_score < g_score[neighbor]: g_score[neighbor] = tentative_g_score f_score = tentative_g_score + (neighbor - goal).manhatten() open_set.put(neighbor, f_score)
def part2(inp): mapp = inp.copy() hole, holecap = next((c, a) for c, (u, a) in mapp.items() if u == 0) boundary = {(hole, Vec(max(x for x, y in mapp if y == 0), 0))} visited = boundary.copy() for i in count(1): nb = set() for h, t in boundary: for d in dirs4: if h == (0, 0) and t == h + d: return i if mapp.get(h + d, (holecap + 1, 0))[0] <= holecap: if h + d == t: ns = (h + d, h) else: ns = (h + d, t) if ns not in visited: nb.add(ns) visited.add(ns) boundary = nb
def shortest_distance(pos, collected): try: return min(d + shortest_distance(p, collected.union({p})) for p, d in distances_from_point(find(pos)).items() if p not in collected and p != '0') except ValueError: return 0 def part1(): return shortest_distance('0', frozenset()) @cache def shortest_distance_return(pos, collected): try: return min(d + shortest_distance_return(p, collected.union({p})) for p, d in distances_from_point(find(pos)).items() if p not in collected and p != '0') except ValueError: return distances_from_point(find(pos))['0'] def part2(): return shortest_distance_return('0', frozenset()) if __name__ == '__main__': data = get_data(day=24, year=2016) mapp = {Vec(x, y): c for y, l in enumerate(data.splitlines()) for x, c in enumerate(l)} print(part1()) print(part2())
def part1(inp): return max(map(abs, sum(map(dirs.get, inp), start=Vec(0, 0, 0))))
from itertools import accumulate from aoc_utils import Vec from aocd import get_data # Use Hexagonal Cube Coordinates: https://www.redblobgames.com/grids/hexagons/ dirs = { 'n': Vec(0, -1, 1), 'ne': Vec(1, -1, 0), 'se': Vec(1, 0, -1), 's': Vec(0, 1, -1), 'sw': Vec(-1, 1, 0), 'nw': Vec(-1, 0, 1) } def part1(inp): return max(map(abs, sum(map(dirs.get, inp), start=Vec(0, 0, 0)))) def part2(inp): return max( map(lambda x: max(abs(i) for i in x), accumulate(map(dirs.get, inp)))) if __name__ == '__main__': data = get_data(day=11, year=2017) inp = data.split(',') print(part1(inp)) print(part2(inp))
for i in count(1): nb = set() for h, t in boundary: for d in dirs4: if h == (0, 0) and t == h + d: return i if mapp.get(h + d, (holecap + 1, 0))[0] <= holecap: if h + d == t: ns = (h + d, h) else: ns = (h + d, t) if ns not in visited: nb.add(ns) visited.add(ns) boundary = nb if __name__ == '__main__': data = get_data(day=22, year=2016) inp = { Vec(x, y): (u, a) for x, y, u, a in (tuple( map( int, re.match( r'^/dev/grid/node-x(\d+)-y(\d+)\s+\d+T\s+(\d+)T\s+(\d+)T\s+\d+%$', l).groups())) for l in data.splitlines()[2:]) } print(part1(inp)) print(part2(inp))
from aoc_utils import Vec from aocd import get_data def part1(inp): return min(((a.manhatten(), v.manhatten(), p.manhatten(), n) for n, (p, v, a) in enumerate(inp)))[3] def move(p): pos, vel, acc = p vel += acc pos += vel return pos, vel, acc def part2(inp): parts = inp for _ in range(100): collisions = [p for p, n in Counter(p for p, _, _ in parts).items() if n > 1] parts = [move(p) for p in parts if p[0] not in collisions] return len(parts) if __name__ == '__main__': data = get_data(day=20, year=2017) inp = [tuple(map(lambda x: Vec(*map(int, x.split(','))), re.match(r'^p=<([\d,-]+)>, v=<([\d,-]+)>, a=<([\d,-]+)>$', l).groups())) for l in data.splitlines()] print(part1(inp)) print(part2(inp))
if c.isalpha(): res += c def part2(mapp): pos = next(c for c, v in mapp.items() if c[1] == 0 and v == '|') d = Dir.DOWN res = 1 while True: if mapp.get(pos + d.value, None): pass elif mapp.get(pos + d.turn_left().value, None): d = d.turn_left() elif mapp.get(pos + d.turn_right().value, None): d = d.turn_right() else: return res pos += d.value res += 1 if __name__ == '__main__': data = get_data(day=19, year=2017) inp = { Vec(x, y): c for y, l in enumerate(data.splitlines()) for x, c in enumerate(l) if c != ' ' } print(part1(inp)) print(part2(inp))
pos, vel = step(pos, vel) if pos[1] < max_y: return max_y max_y = pos[1] def part2(inp): y_candidates = [] for i in range(min(0, inp.lower[1] - 1), max(abs(y) for _, y in [inp.lower, inp.upper]) + 1): if check_y_hit(Vec(0, i), inp): y_candidates.append(i) x_candidates = [] for i in range(min(0, inp.lower[0] - 1), max(0, inp.upper[0] + 1)): if check_x_hit(Vec(i, 0), inp): x_candidates.append(i) return sum( check_hit(Vec(x, y), inp) for x in x_candidates for y in y_candidates) if __name__ == '__main__': data = get_data(day=17, year=2021) inp = tuple( map( int, re.match(r'^target area: x=(-?\d+)..(-?\d+), y=(-?\d+)..(-?\d+)$', data).groups())) inp = Rect(Vec(inp[0], inp[2]), Vec(inp[1], inp[3])) print(part1(inp)) print(part2(inp))
for neighbor in (current + d for d in dirs4 if (current + d) in mapp): tentative_g_score = g_score[current] + mapp[neighbor] if tentative_g_score < g_score[neighbor]: g_score[neighbor] = tentative_g_score f_score = tentative_g_score + (neighbor - goal).manhatten() open_set.put(neighbor, f_score) def part1(inp): return find_shortest_path(inp) def part2(inp): mapp = {} lx, ly = max(x for x, _ in inp) + 1, max(y for _, y in inp) + 1 for (x, y), v in inp.items(): for a, b in product(range(5), repeat=2): nv = (v + a + b) % 9 mapp[Vec(x + a * lx, y + b * ly)] = 9 if nv == 0 else nv return find_shortest_path(mapp) if __name__ == '__main__': data = get_data(day=15, year=2021) inp = { Vec(x, y): int(v) for y, l in enumerate(data.splitlines()) for x, v in enumerate(l) } print(part1(inp)) print(part2(inp))
import math from collections import defaultdict from aoc_utils import Vec from aocd import get_data square = [ Vec(-1, -1), Vec(0, -1), Vec(1, -1), Vec(-1, 0), Vec(0, 0), Vec(1, 0), Vec(-1, 1), Vec(0, 1), Vec(1, 1), ] def enhance(img, alg): xmin = min(x for x, _ in img) - 1 xmax = max(x for x, _ in img) + 1 ymin = min(y for _, y in img) - 1 ymax = max(y for _, y in img) + 1 if img.default_factory(): default = alg[-1] else: default = alg[0] return defaultdict( lambda: default, { Vec(x, y): alg[int(
def count_overlaps(inp, include_diagonals): lines_per_point = defaultdict(lambda: 0) for start, end in inp: d = simplify(end - start) if all(c != 0 for c in d) and not include_diagonals: continue lines_per_point[start] += 1 pos = start while pos != end: pos += d lines_per_point[pos] += 1 return sum(n >= 2 for n in lines_per_point.values()) def part1(inp): return count_overlaps(inp, False) def part2(inp): return count_overlaps(inp, True) if __name__ == '__main__': data = get_data(day=5, year=2021) inp = [(Vec(a, b), Vec(c, d)) for a, b, c, d in ((tuple(map(int, a))) for a in (re.match(r'(\d+),(\d+) -> (\d+),(\d+)', l).groups() for l in data.splitlines()))] print(part1(inp)) print(part2(inp))
boundary = {lowpoint} while boundary: nb = set() for p in boundary: for d in dirs4: if (p + d) not in basin and mapp.get(p + d, 9) < 9: basin.add(p + d) nb.add(p + d) boundary = nb return len(basin) def part2(inp): return reduce( operator.mul, sorted( (calculate_basin_size(inp, p) for p in (p for p, v in inp.items() if v < min(inp.get(p + d, math.inf) for d in dirs4))), reverse=True)[:3]) if __name__ == '__main__': data = get_data(day=9, year=2021) inp = { Vec(x, y): int(n) for y, l in enumerate(data.splitlines()) for x, n in enumerate(l) } print(part1(inp)) print(part2(inp))
import operator from functools import reduce from itertools import accumulate from aoc_utils import Vec from aocd import get_data dirs = { "forward": Vec(1, 0), "down": Vec(0, 1), "up": Vec(0, -1), } def part1(inp): return reduce( operator.mul, reduce(lambda a, b: tuple(map(operator.add, a, b)), (dirs[d] * n for d, n in inp))) def part2(inp): return reduce( operator.mul, reduce( operator.add, map( lambda x: Vec(x[1], x[0] * x[1]), zip(accumulate(dirs[d][1] * n for d, n in inp), (dirs[d][0] * n for d, n in inp)))))