**[Advent of Code 2024][]** *[Code golf style][]* ![](static/logo.png) > "Advent of Code is an Advent calendar of small programming puzzles for a > variety of skill levels that can be solved in any programming language you > like. People use them as interview prep, company training, university > coursework, practice problems, a speed contest, or to challenge each other. > -- [Eric Wastl][]" [Advent of Code 2024]: https://adventofcode.com/2024 [Code golf style]: https://en.wikipedia.org/wiki/Code_golf [Eric Wastl]: https://was.tl/ # Code (🏌️) ## Day 01 ```py f = open("test.01" if __debug__ else "input.01") ll, rl = zip(*[(int(le), int(re)) for le, re in (i.split() for i in f)]) p1 = sum(abs(le - re) for le, re in zip(sorted(ll), sorted(rl))) p2 = sum(i * rl.count(i) for i in ll) print(p1, p2, sep="\n") assert p1 == 11 and p2 == 31 ``` ## Day 02 ```py x = [list(map(int, r.split())) for r in open("test.02" if __debug__ else "input.02")] lx, P, N, it = len(x[0]), [1, 2, 3], [-1, -2, -3], __import__("itertools") D, p2 = [[b - a for a, b in c] for c in (it.pairwise(r) for r in x)], 0 for rep in x: for rc in it.combinations(rep, len(rep) - 1): d = [b - a for a, b in it.pairwise(rc)] if all(i in P for i in d) or all(i in N for i in d): p2 += 1 break p1 = sum(all(i in P for i in d) or all(i in N for i in d) for d in D) print(p1, p2, sep="\n") assert p1 == 2 and p2 == 4 ``` ## Day 03 ```py P1, P2, re = r"mul\((\d+),(\d+)\)", r"don't\(\).*?do\(\)", __import__("re") x = open("test2.03" if __debug__ else "input.03").read().replace("\n", "") p1 = sum(int(a) * int(b) for a, b in [i.groups() for i in re.finditer(P1, x)]) x = re.sub(P2, "", x) p2 = sum(int(a) * int(b) for a, b in [i.groups() for i in re.finditer(P1, x)]) print(p1, p2, sep="\n") assert p1 == 161 and p2 == 48 ``` ## Day 04 ```py x, L = open("test.04" if __debug__ else "input.04").read().splitlines(), range(4) y, z, R, C, j, G = 0, 0, len(x), len(x[0]), "".join, ("XMAS", "SAMX") for r in range(R): for c in range(C): y += 1 if c + 3 < C and x[r][c : c + 4] in G else 0 y += 1 if r + 3 < R and j(i[c] for i in x[r : r + 4]) in G else 0 y += 1 if r + 3 < R and c + 3 < C and j(x[r + i][c + i] for i in L) in G else 0 y += 1 if r - 3 >= 0 and c + 3 < C and j(x[r - i][c + i] for i in L) in G else 0 if r + 2 < R and c + 2 < C and x[r + 2][c] + x[r][c + 2] in ("MS", "SM"): z += 1 if j(x[r + i][c + i] for i in range(3)) in ("MAS", "SAM") else 0 print(y, z, sep="\n") assert y == 18 and z == 9 ``` ## Day 05 ```py F, p1, p2 = open("test.05" if __debug__ else "input.05").read().strip(), 0, 0 b, a, (por, ptp) = dict[int, set](), dict[int, set](), F.split("\n\n") for line in por.splitlines(): x, y = map(int, line.split("|")) b.setdefault(y, set()).add(x) a.setdefault(x, set()).add(y) for line in ptp.splitlines(): vs, ok = [int(x) for x in line.split(",")], True for i, x in enumerate(vs): for j, y in enumerate(vs): ok = False if i < j and y in b.get(x, set()) else ok if ok: p1 += vs[len(vs) // 2] else: oks, q, d = [], [], {v: len(b.get(v, set()) & set(vs)) for v in vs} q = [v for v in vs if d[v] == 0] while q: x = q.pop(0) oks.append(x) for y in a.get(x, set()): if y in d: d[y] -= 1 q.append(y) if d[y] == 0 else None p2 += oks[len(oks) // 2] print(p1, p2, sep="\n") assert p1 == 143 and p2 == 123 ``` ## Day 06 ```py def solve(data: list[list[str]], part2=False) -> int: r, c = next((r, c) for r in range(R) for c in range(C) if data[r][c] == "^") d, seen = 0, set() while (r, c) not in seen: seen.add(((r, c), d)) nr, nc = r + D[d][0], c + D[d][1] if not (0 <= nr < R and 0 <= nc < C): break while data[nr][nc] == "#": d = (d + 1) % 4 nr, nc = r + D[d][0], c + D[d][1] r, c = nr, nc if ((r, c), d) in seen: return 1 return 0 if part2 else len({i[0] for i in seen}) data = list(map(list, open("test.06" if __debug__ else "input.06").read().splitlines())) D, R, C = [(-1, 0), (0, 1), (1, 0), (0, -1)], len(data), len(data[0]) p1, p2 = solve(data), 0 for r in range(R): for c in range(C): if data[r][c] == ".": data[r][c] = "#" p2 += solve(data, True) data[r][c] = "." print(p1, p2, sep="\n") assert p1 == 41 and p2 == 6 ``` ## Day 07 ```py def check(test: int, ns: list[int], part2=False) -> bool: if len(ns) == 1: return ns[0] == test if check(test, [ns[0] + ns[1]] + ns[2:], part2): return True if check(test, [ns[0] * ns[1]] + ns[2:], part2): return True if part2 and check(test, [int(str(ns[0]) + str(ns[1]))] + ns[2:], part2): return True return False p1, p2 = 0, 0 for line in open("test.07" if __debug__ else "input.07").read().splitlines(): [test], ns = ([int(i) for i in x.split()] for x in line.split(":")) p1 += test if check(test, ns) else 0 p2 += test if check(test, ns, part2=True) else 0 print(p1, p2, sep="\n") assert p1 == 3749 and p2 == 11387 ``` ## Day 08 ```py F = open("test.08" if __debug__ else "input.08").read().splitlines() p1, p2, R, C, P = set(), set(), len(F), len(F[0]), dict[str, list]() for r in range(R): for c in range(C): P.setdefault(F[r][c], []).append((r, c)) if F[r][c] != "." else None for r in range(R): for c in range(C): for k, vs in P.items(): for r1, c1 in vs: for r2, c2 in vs: if (r1, c1) != (r2, c2): d1, d2 = abs(r - r1) + abs(c - c1), abs(r - r2) + abs(c - c2) dr1, dr2, dc1, dc2 = r - r1, r - r2, c - c1, c - c2 if 0 <= r < R and (d1 == 2 * d2 or d1 * 2 == d2): if 0 <= c < C and (dr1 * dc2 == dc1 * dr2): p1.add((r, c)) if 0 <= r < R and 0 <= c < C and (dr1 * dc2 == dc1 * dr2): p2.add((r, c)) print(len(p1), len(p2), sep="\n") assert len(p1) == 14 and len(p2) == 34 ``` ## Day 09 ```py def solve(part2=False) -> int: fs, free, res, fid, pos = [], [], list[int | None](), 0, 0 for i, c in enumerate(F): if i % 2 == 0: fs.append((pos, int(c), fid)) if part2 else None for i in range(int(c)): res.append(fid) fs.append((pos, 1, fid)) if not part2 else None pos += 1 fid += 1 else: free.append((pos, int(c))) for i in range(int(c)): res.append(None) pos += 1 for pos, sz, fid in reversed(fs): for f_i, (f_pos, f_sz) in enumerate(free): if f_pos < pos and sz <= f_sz: for i in range(sz): res[pos + i], res[f_pos + i] = None, fid free[f_i] = (f_pos + sz, f_sz - sz) break return sum(i * c for i, c in enumerate(res) if c) F = open("test.09" if __debug__ else "input.09").read().strip() p1, p2 = solve(), solve(True) print(p1, p2, sep="\n") assert p1 == 1928 and p2 == 2858 ``` ## Day 10 ```py def walk(G: dict[tuple[int, int], int], p: tuple[int, int]): if G[p] == 9: yield [p] for r, c in ((-1, 0), (0, 1), (1, 0), (0, -1)): if G.get((p[0] + r, p[1] + c), -1) == G[p] + 1: for x in walk(G, (p[0] + r, p[1] + c)): yield [p, *x] F = open("test.10" if __debug__ else "input.10").read().splitlines() G = {(i, j): int(c) for i, r in enumerate(F) for j, c in enumerate(r)} S = [p for p, c in G.items() if c == 0] p1 = sum(len({path[-1] for path in walk(G, p)}) for p in S) p2 = sum(1 for p in S for _ in walk(G, p)) print(p1, p2, sep="\n") assert p1 == 36 and p2 == 81 ``` ## Day 11 ```py def solve(x: int, s: int) -> int: sx = str(x) if (x, s) in X: return X[(x, s)] if s == 0: res = 1 elif x == 0: res = solve(1, s - 1) elif len(sx) % 2 == 0: l, r = int(sx[: len(sx) // 2]), int(sx[len(sx) // 2 :]) res = solve(l, s - 1) + solve(r, s - 1) else: res = solve(x * 2024, s - 1) X[(x, s)] = res return res F = [int(x) for x in open("test.11" if __debug__ else "input.11").read().split()] X = dict[tuple[int, int], int]() p1, p2 = sum(solve(x, 25) for x in F), sum(solve(x, 75) for x in F) print(p1, p2, sep="\n") assert p1 == 55312 and p2 == 65601038650482 ``` ## Day 12 ```py F, s = open("test.12" if __debug__ else "input.12").read().split(), set() p1, p2, R, C, D = 0, 0, len(F), len(F[0]), [(-1, 0), (0, 1), (1, 0), (0, -1)] for r in range(R): for c in range(C): if (r, c) in s: continue q, a, p, pd, ss = [(r, c)], 0, 0, dict[tuple, set](), 0 while q: r2, c2 = q.pop(0) if (r2, c2) in s: continue s.add((r2, c2)) a += 1 for dr, dc in D: rr, cc = r2 + dr, c2 + dc if 0 <= rr < R and 0 <= cc < C and F[rr][cc] == F[r2][c2]: q.append((rr, cc)) else: p += 1 pd[(dr, dc)] = set() if (dr, dc) not in pd else pd[(dr, dc)] pd[(dr, dc)].add((r2, c2)) for k, vs in pd.items(): sp = set() for pr, pc in vs: if (pr, pc) not in sp: ss, q = ss + 1, [(pr, pc)] while q: r2, c2 = q.pop(0) if (r2, c2) in sp: continue sp.add((r2, c2)) for dr, dc in D: rr, cc = r2 + dr, c2 + dc q.append((rr, cc)) if (rr, cc) in vs else None p1, p2 = p1 + a * p, p2 + a * ss print(p1, p2, sep="\n") assert p1 == 1930 and p2 == 1206 ``` ## Day 13 ```py data = open("test.13" if __debug__ else "input.13").read().split("\n\n") results, re = {0: 0, 10_000_000_000_000: 0}, __import__("re") ints = lambda x: [int(i) for i in re.findall(r"-?\d+", x)] # noqa: E731 for d in data: (ax, ay), (bx, by), (px, py) = [ints(i) for i in d.splitlines()] for part in results: b = (ax * (py + part) - ay * (px + part)) / (ax * by - ay * bx) a = (px + part - b * bx) / ax if a == int(a) and b == int(b): results[part] += int(a * 3 + b) p1, p2 = results.values() print(p1, p2, sep="\n") assert p1 == 480 and p2 == 875318608908 ``` ## Day 14 ```py (R, C), colls = (11, 7) if __debug__ else (101, 103), __import__("collections") ints = lambda x: [*map(int, __import__("re").findall(r"-?\d+", x))] # noqa: E731 data = [*map(ints, open("test.14" if __debug__ else "input.14").read().splitlines())] p1, p2, d, MR, MC = 1, (2**63, -1), colls.Counter(), R // 2, C // 2 for part, seconds in {1: 100, 2: R * C}.items(): state = data[:] for t in range(seconds): state = [[(r + dr + R) % R, (c + dc + C) % C, dr, dc] for r, c, dr, dc in state] if part == 2: new = sum((i - MR) ** 2 + (j - MC) ** 2 for i, j, _, _ in state) p2 = min((new, t + 1), p2) if part == 1: for r, c, _, _ in state: if r == R // 2 or c == C // 2: continue d[r < R // 2, c < C // 2] += 1 for i in d.values(): p1 *= i print(p1, p2[1], sep="\n") assert p1 == 12 and p2[1] == 24 ``` ## Day 15 ```py def solve(GRID: list[str], part2=False) -> int: R, C = len(GRID), len(GRID[0]) grid = [[GRID[r][c] for c in range(C)] for r in range(R)] append = lambda c, i: c.append(i) # noqa: E731 if part2: big = [] for r in range(R): row = list[str]() for c in range(C): [append(row, i) for i in "##" if grid[r][c] == "#"] [append(row, i) for i in "[]" if grid[r][c] == "O"] [append(row, i) for i in ".." if grid[r][c] == "."] [append(row, i) for i in "@." if grid[r][c] == "@"] big.append(row) grid = big C *= 2 for r in range(R): for c in range(C): if grid[r][c] == "@": sr, sc, grid[r][c] = r, c, "." r, c = sr, sc for inst in INSTRS.replace("\n", ""): dr, dc = {"^": (-1, 0), ">": (0, 1), "v": (1, 0), "<": (0, -1)}[inst] r2, c2 = r + dr, c + dc if grid[r2][c2] == "#": continue elif grid[r2][c2] == ".": r, c = r2, c2 elif grid[r2][c2] in ["[", "]", "O"]: queue, seen, ok = [(r, c)], set(), True while queue: r2, c2 = queue.pop(0) if (r2, c2) in seen: continue seen.add((r2, c2)) r3, c3 = r2 + dr, c2 + dc if grid[r3][c3] == "#": ok = False break [append(queue, (r3, c3)) for _ in range(1) if grid[r3][c3] == "O"] [append(queue, (r3, c3 + i)) for i in range(2) if grid[r3][c3] == "["] [append(queue, (r3, c3 - i)) for i in range(2) if grid[r3][c3] == "]"] if not ok: continue while len(seen) > 0: for r2, c2 in sorted(seen): r3, c3 = r2 + dr, c2 + dc if (r3, c3) not in seen: grid[r3][c3], grid[r2][c2] = grid[r2][c2], "." seen.remove((r2, c2)) r, c = r + dr, c + dc ans = 0 for r in range(R): for c in range(C): if grid[r][c] in ["[", "O"]: ans += 100 * r + c return ans GRID, INSTRS = open("test.15" if __debug__ else "input.15").read().split("\n\n") p1, p2 = solve(GRID.splitlines()), solve(GRID.splitlines(), True) print(p1, p2, sep="\n") assert p1 == 10092 and p2 == 9021 ``` ## Day 16 ```py f, heapq = "test.16" if __debug__ else "input.16", __import__("heapq") ints = lambda x: [int(i) for i in __import__("re").findall(r"-?\d+", x)] # noqa: E731 dist, maze = list[dict]([{}, {}]), [list(i) for i in open(f).read().splitlines()] p1, D, R, C = 0, [(-1, 0), (0, 1), (1, 0), (0, -1)], len(maze), len(maze[0]) sr, sc = next((r, c) for r in range(R) for c in range(C) if maze[r][c] == "S") er, ec = next((r, c) for r in range(R) for c in range(C) if maze[r][c] == "E") for part in 1, 2: queue, seen = list[int](), set() if part == 1: heapq.heappush(queue, (0, sr, sc, 1)) else: [heapq.heappush(queue, (0, er, ec, dir)) for dir in range(4)] while queue: score, r, c, dir = heapq.heappop(queue) if (r, c, dir) not in dist[part - 1]: dist[part - 1][(r, c, dir)] = score p1 = score if part == 1 and r == er and c == ec and not p1 else p1 if (r, c, dir) in seen: continue seen.add((r, c, dir)) dr, dc = D[dir] if part == 1 else D[(dir + 2) % 4] rr, cc = r + dr, c + dc if 0 <= cc < C and 0 <= rr < R and maze[rr][cc] != "#": heapq.heappush(queue, (score + 1, rr, cc, dir)) heapq.heappush(queue, (score + 1000, r, c, (dir + 1) % 4)) heapq.heappush(queue, (score + 1000, r, c, (dir + 3) % 4)) p2 = set() for r in range(R): for c in range(C): for dir in range(4): if (r, c, dir) in dist[0] and (r, c, dir) in dist[1]: if dist[0][(r, c, dir)] + dist[1][(r, c, dir)] == p1: p2.add((r, c)) print(p1, len(p2), sep="\n") assert p1 == 11048 and len(p2) == 64 ``` ## Day 17 ```py def solve(prog: list[int], a: int, b: int, c: int) -> list[int]: ip, out = 0, [] while ip < len(prog): instr, operand = prog[ip], prog[ip + 1] combo = operand, operand, operand, operand, a, b, c a = a // (2 ** combo[operand]) if instr == 0 else a b = b ^ operand if instr == 1 else b b = combo[operand] % 8 if instr == 2 else b ip = operand - 2 if instr == 3 and a != 0 else ip b = b ^ c if instr == 4 else b if instr == 5: out.append(combo[operand] % 8) b = a // (2 ** combo[operand]) if instr == 6 else b c = a // (2 ** combo[operand]) if instr == 7 else c ip += 2 return out def part2(n: int, a: int) -> int: if n == -1: return a a <<= 3 for x in range(8): if solve(prog, a + x, 0, 0) == prog[n:]: s = part2(n - 1, a + x) if s != -1: return s return -1 data = open("test.17" if __debug__ else "input.17").read().replace("\n", " ") ints = lambda x: [int(i) for i in __import__("re").findall(r"-?\d+", x)] # noqa: E731 a, b, c, *prog = ints(data) p1, p2 = ",".join(map(str, solve(prog, a, b, c))), part2(len(prog) - 1, 0) print(p1, p2, sep="\n") assert p1 == "4,6,3,5,6,3,5,2,1,0" and p2 == -1 ``` ## Day 18 ```py def solve(puzzle: set[tuple[int, ...]]) -> int: seen, queue = set([(0, 0)]), [(0, (0, 0))] while queue: dist, (x, y) = queue.pop(0) if (x, y) == m: return dist for dx, dy in [(-1, 0), (0, 1), (1, 0), (0, -1)]: if 0 <= x + dx <= m[0] and 0 <= y + dy <= m[1]: if not ((x + dx, y + dy) in puzzle or (x + dx, y + dy) in seen): seen.add((x + dx, y + dy)) queue.append((dist + 1, (x + dx, y + dy))) return 0 f, lim, m = ("test.18", 12, (6, 6)) if __debug__ else ("input.18", 1024, (70, 70)) ints = lambda x: [int(i) for i in __import__("re").findall(r"-?\d+", x)] # noqa: E731 data = [tuple(ints(i)) for i in open(f).read().splitlines()] p1, low, high = solve(set(data[:lim])), 0, len(data) while low < high: mid = (low + high) // 2 if solve(set(data[:mid])): low = mid + 1 else: high = mid print(p1, f"{data[low-1][0]},{data[low-1][1]}", sep="\n") assert p1 == 22 and data[low - 1] == (6, 1) ``` ## Day 19 ```py i = [i.strip() for i in open("test.19" if __debug__ else "input.19") if i.strip()] p1, p2, ps, ds, ft = 0, 0, i[0].split(", "), i[1:], __import__("functools") @ft.cache def solve(pattern: str) -> int: r = 0 if not pattern: return 1 for p in ps: if pattern.startswith(p): r += solve(pattern[len(p) :]) return r for d in ds: ways = solve(d) p1, p2 = p1 + bool(ways), p2 + ways print(p1, p2, solve.cache_info(), sep="\n") assert p1 == 6 and p2 == 16 ``` ## Day 20 ```py data = [i for i in open("test.20" if __debug__ else "input.20").read().splitlines()] R, C, TWO, LIMIT = len(data), len(data[0]), 1, 50 if __debug__ else 100 S = next((r, c) for r in range(R) for c in range(C) if data[r][c] == "S") E = next((r, c) for r in range(R) for c in range(C) if data[r][c] == "E") p1, p2, path = 0, 0, [dict[tuple, int](), dict[tuple, int]()] for i, x in enumerate((S, E)): path[i], seen, queue = {x: 0}, {x}, [(0, x)] while queue: d, (r, c) = queue.pop(0) path[i][r, c] = d for nr, nc in [(-1, 0), (0, 1), (1, 0), (0, -1)]: if 0 <= r + nr < R and 0 <= c + nc < C and data[r][c] != "#": if (r + nr, c + nc) not in seen: seen.add((r + nr, c + nc)) queue.append((d + 1, (r + nr, c + nc))) for part in range(2): for r in range(1, R - 1): for c in range(1, C - 1): if data[r][c] == "#": if part == TWO: continue score = [] if data[r + 1][c] != "#" and data[r - 1][c] != "#": score.append(path[0][r + 1, c] + path[1][r - 1, c] + 2) score.append(path[0][r - 1, c] + path[1][r + 1, c] + 2) if data[r][c + 1] != "#" and data[r][c - 1] != "#": score.append(path[0][r, c + 1] + path[1][r, c - 1] + 2) score.append(path[0][r, c - 1] + path[1][r, c + 1] + 2) p1 += sum(1 for s in score if path[0][E] - s >= LIMIT) if part == TWO: for er in range(max(1, r - 20), min(R - 1, r + 21)): dr = abs(er - r) for ec in range(max(1, c - 20 + dr), min(R - 1, c + 21 - dr)): if data[r][c] != "#" and data[er][ec] != "#": s = path[0][r, c] + path[1][er, ec] + dr + abs(ec - c) p2 += 1 if path[0][E] - s >= LIMIT else 0 print(p1, p2, sep="\n") assert p1 == 1 and p2 == 285 ``` ## Day 21 ```py ft, it, p1, p2 = __import__("functools"), __import__("itertools"), 0, 0 DV = lambda r, c: [(r - 1, c, "^"), (r + 1, c, "v")] # noqa: E731 DH = lambda r, c: [(r, c - 1, "<"), (r, c + 1, ">")] # noqa: E731 def sequences(keypad: list[list[str]]) -> dict[tuple[str, str], list[str]]: pos, seqs, R, C = {}, {}, len(keypad), len(keypad[0]) for r in range(R): for c in range(C): if keypad[r][c] != "X": pos[keypad[r][c]] = (r, c) for pr in pos: for pc in pos: if pr == pc: seqs[(pr, pc)] = ["A"] continue valids, queue, best = [], [(pos[pr], "")], 2**63 while queue: (r, c), moves = queue.pop(0) for nr, nc, nm in DV(r, c) + DH(r, c): if not (0 <= nr < R and 0 <= nc < C) or keypad[nr][nc] == "X": continue if keypad[nr][nc] == pc: if best < len(moves) + 1: break best = len(moves) + 1 valids.append(moves + nm + "A") else: queue.append(((nr, nc), moves + nm)) else: continue break seqs[(pr, pc)] = valids return seqs @ft.cache def lengths(seq: str, depth: int) -> int: if depth == 1: return sum(DIR_LENGTHS[(x, y)] for x, y in zip("A" + seq, seq)) length = 0 for x, y in zip("A" + seq, seq): length += min(lengths(subseq, depth - 1) for subseq in DIR_SEQS[(x, y)]) return length NUM_SEQS = sequences([list(i) for i in ["789", "456", "123", "X0A"]]) DIR_SEQS = sequences([["X", "^", "A"], ["<", "v", ">"]]) DIR_LENGTHS = {key: len(value[0]) for key, value in DIR_SEQS.items()} for line in open("test.21" if __debug__ else "input.21").read().splitlines(): OPTIONS = [NUM_SEQS[(x, y)] for x, y in zip("A" + line, line)] INPUTS = ["".join(x) for x in it.product(*OPTIONS)] p1 += min((lengths(i, 2) for i in INPUTS)) * int(line[:-1]) p2 += min((lengths(i, 25) for i in INPUTS)) * int(line[:-1]) print(p1, p2, lengths.cache_info(), sep="\n") assert p1 == 126384 and p2 == 154115708116294 ``` ## Day 22 ```py ops, p1, p2 = __import__("operator"), 0, dict[tuple[int, int, int, int], int]() for num in map(int, open("test2.22" if __debug__ else "input.22")): buyer = [num % 10] for _ in range(2000): for op, by in ((ops.mul, 64), (ops.floordiv, 32), (ops.mul, 2048)): num = (num ^ op(num, by)) % 16777216 buyer.append(num % 10) p1, seen = p1 + num, set() for i in range(len(buyer) - 4): a, b, c, d, e = buyer[i : i + 5] seq = (b - a, c - b, d - c, e - d) if seq in seen: continue seen.add(seq) p2[seq] = p2.setdefault(seq, 0) + e print(p1, max(p2.values()), sep="\n") assert p1 == 37990510 and max(p2.values()) == 23 ``` ## Day 23 ```py def part2(cur: set[str], ok: set[str], ko: set[str]) -> set[str]: if not ok and not ko: return cur res: set[str] = set() while ok: v = ok.pop() res = max(res, part2(cur | {v}, ok & data[v], ko & data[v]), key=len) ko.add(v) return res D = [line.strip().split("-") for line in open("test.23" if __debug__ else "input.23")] sols, data, it = set(), dict[str, set[str]](), __import__("itertools") for n1, n2 in D: data.setdefault(n1, set()).add(n2) data.setdefault(n2, set()).add(n1) for n, c in data.items(): if len(c) > 1: for p in it.permutations(c, 2): if p[1] in data[p[0]]: sols.add(tuple(sorted(p + (n,)))) p1 = sum(1 for x in sols if any(i[0] == "t" for i in x)) p2 = ",".join(sorted(part2(set(), set(data), set()))) print(p1, p2, sep="\n") assert p1 == 7 and p2 == "co,de,ka,ta" ``` ## Day 24 ```py vs, fs = open("test.24" if __debug__ else "input.24").read().split("\n\n") values = {k: int(v) for k, v in (i.split(": ") for i in vs.split("\n"))} formulas = {z: (o, x, y) for x, o, y, _, z in (i.split() for i in fs.split("\n") if i)} zs, ops, swaps = [], __import__("operator"), [] operators = {"OR": ops.or_, "AND": ops.and_, "XOR": ops.xor} def part1(w: str) -> int: if w not in values: op, x, y = formulas[w] values[w] = operators[op](part1(x), part1(y)) return values[w] for i in range(100): key = "z" + ("0" + str(i))[-2:] if key not in formulas: break zs.append(part1(key)) p1 = int("".join(map(str, zs[::-1])), 2) def vz(w: str, i: int, k: int) -> bool: if w not in formulas: return False op, x, y = formulas[w] if op != "XOR": return False if k == i == 0: return sorted([x, y]) == ["x00", "y00"] elif k == 0: return vz(x, i, 1) and vc(y, i) or vz(y, i, 1) and vc(x, i) return sorted([x, y]) == [f"x{i:02}", f"y{i:02}"] def vc(w: str, i: int) -> bool: if w not in formulas: return False op, x, y = formulas[w] if i == 1: if op != "AND": return False return sorted([x, y]) == ["x00", "y00"] if op != "OR": return False return vd(x, i - 1, 0) and vd(y, i - 1, 1) or vd(y, i - 1, 0) and vd(x, i - 1, 1) def vd(w: str, i: int, k: int) -> bool: if w not in formulas: return False (op, x, y), s = formulas[w], str(i) if op != "AND": return False if k: return vz(x, i, 1) and vc(y, i) or vz(y, i, 1) and vc(x, i) return sorted([x, y]) == ["x" + ("0" + s)[-2:], "y" + ("0" + s)[-2:]] def part2() -> int: for i in range(100): if not vz("z" + ("0" + str(i))[-2:], i, 0): break return i for _ in range(4): baseline = part2() for x in formulas: for y in formulas: if x == y: continue formulas[x], formulas[y] = formulas[y], formulas[x] if part2() > baseline: break formulas[x], formulas[y] = formulas[y], formulas[x] else: continue break swaps += [x, y] p2 = ",".join(sorted(swaps)) print(p1, p2, sep="\n") assert p1 == 2024 and p2 == "gnj,gnj,gnj,gnj,gnj,gnj,gnj,gnj" ``` ## Day 25 ```py def solve(key: str, lock: str) -> bool: for r in range(len(key)): for c in range(len(key[0])): if key[r][c] == "#" and lock[r][c] == "#": return False return True data = open("test.25" if __debug__ else "input.25").read().strip().split("\n\n") keys, locks = [], [] for shape in data: g, k = shape.splitlines(), True grid = [[g[r][c] for c in range(len(g[0]))] for r in range(len(g))] if all(grid[0][c] != "#" for c in range(len(grid[0]))): keys.append(shape) else: locks.append(shape) p1 = sum(1 for key in keys for lock in locks if solve(key, lock)) print(p1) assert p1 == 3 ``` # Stats (⛳) ## LOCs File|blank|comment|code :-------|-------:|-------:|-------: d24.py|14|0|69 d15.py|2|0|61 d21.py|6|0|50 d20.py|0|0|38 d12.py|0|0|37 d16.py|0|0|35 d17.py|4|0|32 d09.py|2|0|27 d05.py|0|0|26 d06.py|2|0|26 d18.py|2|0|24 d23.py|2|0|23 d08.py|0|0|20 d11.py|2|0|20 d14.py|0|0|20 d25.py|2|0|18 d07.py|2|0|17 d22.py|0|0|17 d19.py|4|0|16 d10.py|2|0|14 d13.py|0|0|13 d02.py|0|0|12 d04.py|0|0|12 d03.py|0|0|7 d01.py|0|0|6 --------|--------|--------|-------- SUM:|46|0|640 ## Perf ``` 1222801 22545250 0:00.04 0.02usr 0.02sys 100%CPU 57344kB (0) pypy -O d01.py 218 290 0:00.06 0.04usr 0.01sys 100%CPU 58624kB (0) pypy -O d02.py 182780583 90772405 0:00.04 0.02usr 0.01sys 97%CPU 58880kB (0) pypy -O d03.py 2551 1985 0:00.08 0.06usr 0.02sys 98%CPU 60160kB (0) pypy -O d04.py 5588 5331 0:00.04 0.02usr 0.02sys 100%CPU 58624kB (0) pypy -O d05.py 4776 1586 0:07.06 7.01usr 0.05sys 100%CPU 92316kB (0) pypy -O d06.py 7579994664753 438027111276610 0:00.31 0.29usr 0.01sys 99%CPU 62336kB (0) pypy -O d07.py 228 766 0:00.11 0.09usr 0.01sys 98%CPU 60032kB (0) pypy -O d08.py 6242766523059 6272188244509 0:01.10 1.07usr 0.02sys 99%CPU 72212kB (0) pypy -O d09.py 786 1722 0:00.15 0.13usr 0.02sys 100%CPU 68352kB (0) pypy -O d10.py 197357 234568186890978 0:00.12 0.08usr 0.04sys 99%CPU 92288kB (0) pypy -O d11.py 1473276 901100 0:00.12 0.10usr 0.02sys 100%CPU 63872kB (0) pypy -O d12.py 40369 72587986598368 0:00.04 0.03usr 0.01sys 100%CPU 59648kB (0) pypy -O d13.py 230172768 8087 0:00.16 0.13usr 0.03sys 100%CPU 77056kB (0) pypy -O d14.py 1509863 1548815 0:00.13 0.11usr 0.02sys 100%CPU 61952kB (0) pypy -O d15.py 102460 527 0:00.44 0.39usr 0.04sys 99%CPU 100240kB (0) pypy -O d16.py 7,3,0,5,7,1,4,0,5 202972175280682 0:00.04 0.02usr 0.01sys 97%CPU 59392kB (0) pypy -O d17.py 354 36,17 0:00.11 0.08usr 0.02sys 100%CPU 64384kB (0) pypy -O d18.py 238 635018909726691 CacheInfo(hits=21242, misses=11401, maxsize=None, currsize=11401) 0:00.07 0.05usr 0.02sys 98%CPU 61184kB (0) pypy -O d19.py 1360 1005476 0:00.53 0.51usr 0.02sys 99%CPU 68724kB (0) pypy -O d20.py 179444 223285811665866 CacheInfo(hits=2352, misses=474, maxsize=None, currsize=474) 0:00.06 0.03usr 0.02sys 100%CPU 60032kB (0) pypy -O d21.py 18317943467 2018 0:03.19 3.14usr 0.04sys 99%CPU 92888kB (0) pypy -O d22.py 1368 dd,ig,il,im,kb,kr,pe,ti,tv,vr,we,xu,zi 0:00.23 0.20usr 0.03sys 99%CPU 71168kB (0) pypy -O d23.py 59364044286798 cbj,cfk,dmn,gmt,qjj,z07,z18,z35 0:01.77 1.75usr 0.02sys 100%CPU 84864kB (0) pypy -O d24.py 3021 0:00.05 0.03usr 0.02sys 96%CPU 58496kB (0) pypy -O d25.py Total: 16.26s ``` !!!BWL: THE BEER-WARE LICENSE (Revision 42) Carlo~~@~~Miron~~.IT~~ wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. —㎝