From 6bd20c64836cd5f24e15fa5ec14f09fde0a3c28e Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Wed, 11 Dec 2024 21:08:17 -0600 Subject: [PATCH 01/23] day 21 --- aoc_wim/aoc2023/q21.py | 61 ++++++++++++++++++++++++++++++++++++++++ aoc_wim/zgrid.py | 8 ++++-- tests/2023/21/0.txt | 13 +++++++++ tests/2023/21/1.txt | 13 +++++++++ tests/2023/21/2.txt | 13 +++++++++ tests/2023/21/3.txt | 13 +++++++++ tests/2023/21/4.txt | 13 +++++++++ tests/2023/21/5.slow.txt | 13 +++++++++ tests/2023/21/6.slow.txt | 13 +++++++++ tests/test_examples.py | 47 ++++++++++++++++++++++++++----- 10 files changed, 197 insertions(+), 10 deletions(-) create mode 100644 aoc_wim/aoc2023/q21.py create mode 100644 tests/2023/21/0.txt create mode 100644 tests/2023/21/1.txt create mode 100644 tests/2023/21/2.txt create mode 100644 tests/2023/21/3.txt create mode 100644 tests/2023/21/4.txt create mode 100644 tests/2023/21/5.slow.txt create mode 100644 tests/2023/21/6.slow.txt diff --git a/aoc_wim/aoc2023/q21.py b/aoc_wim/aoc2023/q21.py new file mode 100644 index 00000000..6244522d --- /dev/null +++ b/aoc_wim/aoc2023/q21.py @@ -0,0 +1,61 @@ +""" +--- Day 21: Step Counter --- +https://adventofcode.com/2023/day/21 +""" +import json +import os +from aocd import data +from aoc_wim.search import BFS +from aoc_wim.zgrid import manhattan_distance +from aoc_wim.zgrid import ZGrid + + +grid = ZGrid(data, on=".", off="#") +z0 = grid.z("S") +grid[z0] = "." +plots = set(grid.bfs(z0=z0)) + + +def step(n): + parity = n % 2 + reachable = far = 0 + for z in plots: + d = manhattan_distance(z, z0) + if d > n: + far += 1 + else: + reachable += d % 2 == parity + return reachable, far + + +def adj(state, h=grid.height, w=grid.width): + z0, Z0 = state + for z in grid.near(z0): + if z in grid: + Z = Z0 + else: + qy, ry = divmod(z.imag, h) + qx, rx = divmod(z.real, w) + z = complex(rx, ry) + Z = Z0 + complex(qx, qy) + if (z, Z) not in bfs.seen and grid[z] == ".": + yield z, Z + + +if "AOCD_EXTRA" not in os.environ: + a, _ = step(64) + print("answer_a:", a) + N, rem = divmod(26501365, grid.width) + r, f = step(rem) + b = N*N*len(plots) + (2*N + 1)*r + N*f + print("answer_b:", b) +else: + extra = json.loads(os.environ["AOCD_EXTRA"]) + n_steps = extra["n_steps"] + state0 = z0, 0 + bfs = BFS(adj, max_depth=n_steps) + bfs(state0) + parity = n_steps % 2 + a = b = sum(v % 2 == parity for v in bfs.seen.values()) + print("answer_a:", a) + print("answer_b:", b) diff --git a/aoc_wim/zgrid.py b/aoc_wim/zgrid.py index 525ac675..34c7b807 100644 --- a/aoc_wim/zgrid.py +++ b/aoc_wim/zgrid.py @@ -245,15 +245,17 @@ def __array__(self, dtype=None, copy=True): full = full.astype(dtype) return full - def graph(self, extra=()): + def graph(self, on=None, extra=()): """connected components""" - node_glyphs = {self.on}.union(extra) + if on is None: + on = self.on + node_glyphs = {on}.union(extra) g = nx.Graph() g.extra = {} for pos, glyph in self.d.items(): if glyph in node_glyphs: g.add_node(pos) - if glyph != self.on: + if glyph != on: g.extra[glyph] = pos right = pos + 1 down = pos + 1j diff --git a/tests/2023/21/0.txt b/tests/2023/21/0.txt new file mode 100644 index 00000000..4f7906ba --- /dev/null +++ b/tests/2023/21/0.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +16 +16 # n_steps=6 diff --git a/tests/2023/21/1.txt b/tests/2023/21/1.txt new file mode 100644 index 00000000..1971ca45 --- /dev/null +++ b/tests/2023/21/1.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +- +50 # n_steps=10 diff --git a/tests/2023/21/2.txt b/tests/2023/21/2.txt new file mode 100644 index 00000000..c28f956f --- /dev/null +++ b/tests/2023/21/2.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +- +1594 # n_steps=50 diff --git a/tests/2023/21/3.txt b/tests/2023/21/3.txt new file mode 100644 index 00000000..bb9f924c --- /dev/null +++ b/tests/2023/21/3.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +- +6536 # n_steps=100 diff --git a/tests/2023/21/4.txt b/tests/2023/21/4.txt new file mode 100644 index 00000000..b5e21af9 --- /dev/null +++ b/tests/2023/21/4.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +- +167004 # n_steps=500 diff --git a/tests/2023/21/5.slow.txt b/tests/2023/21/5.slow.txt new file mode 100644 index 00000000..7967902f --- /dev/null +++ b/tests/2023/21/5.slow.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +- +668697 # n_steps=1000 diff --git a/tests/2023/21/6.slow.txt b/tests/2023/21/6.slow.txt new file mode 100644 index 00000000..0c6e0485 --- /dev/null +++ b/tests/2023/21/6.slow.txt @@ -0,0 +1,13 @@ +........... +.....###.#. +.###.##..#. +..#.#...#.. +....#.#.... +.##..S####. +.##..#...#. +.......##.. +.##.#.####. +.##..##.##. +........... +- +16733044 # n_steps=5000 diff --git a/tests/test_examples.py b/tests/test_examples.py index bbff817b..aaa319e5 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,3 +1,4 @@ +import json import pathlib import pytest @@ -13,13 +14,42 @@ def path2id(input_file): return str(input_file.relative_to(here)) -def remove_trailing_comments(lines): +def split_trailing_comments(lines): + extra = [] while lines and (not lines[-1].strip() or lines[-1].startswith("#")): - lines.pop() - if len(lines): - lines[-1] = lines[-1].split("#")[0].strip() - if len(lines) > 1: - lines[-2] = lines[-2].split("#")[0].strip() + extra.append(lines.pop()) + if len(lines) and "#" in lines[-1]: + line, comment = lines[-1].split("#", 1) + lines[-1] = line.strip() + extra.append(comment.strip()) + if len(lines) > 1 and "#" in lines[-2]: + line, comment = lines[-2].split("#", 1) + lines[-2] = line.strip() + extra.append(comment.strip()) + extra = [x.strip() for x in extra if x.strip()] + return extra + + +def parse_extra_context(extra): + result = {} + for line in extra: + equals = line.count("=") + commas = line.count(",") + if equals and equals == commas + 1: + for part in line.split(","): + k, v = part.strip().split("=") + k = k.strip() + v = v.strip() + try: + v = int(v) + except ValueError: + pass + if k in result: + raise NotImplementedError(f"Duplicate key {k!r}") + result[k] = v + else: + raise NotImplementedError(f"Unexpected {line=}") + return result @pytest.mark.parametrize("input_file", input_files, ids=path2id) @@ -32,7 +62,10 @@ def test_example(input_file, monkeypatch, request): # the last two lines are part a and part b correct answers # there may be trailing comments after the answers lines = input_file.read_text(encoding="utf-8").splitlines() - remove_trailing_comments(lines) + extra = split_trailing_comments(lines) + extra = parse_extra_context(extra) + if extra: + monkeypatch.setenv("AOCD_EXTRA", json.dumps(extra)) if len(lines) < 3: pytest.fail(f"test data {input_file} is malformed") *lines, expected_answer_a, expected_answer_b = lines From 3a3052fec534763a7d614981bdb1a5f62e11720a Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Wed, 11 Dec 2024 22:21:15 -0600 Subject: [PATCH 02/23] better example parsing --- tests/2015/07/0.d.txt | 2 +- tests/2015/07/1.e.txt | 2 +- tests/2015/07/2.f.txt | 2 +- tests/2015/07/3.g.txt | 2 +- tests/2015/07/4.h.txt | 2 +- tests/2015/07/5.i.txt | 2 +- tests/2015/07/6.x.txt | 2 +- tests/2015/07/7.y.txt | 2 +- tests/2015/14/0.txt | 2 +- tests/2015/17/0.txt | 2 +- tests/2015/18/0.txt | 2 +- tests/2015/18/1.txt | 2 +- tests/2015/18/2.txt | 2 +- tests/2015/18/3.txt | 2 +- tests/2015/18/4.txt | 2 +- tests/2015/18/5.txt | 2 +- tests/2015/18/6.txt | 2 +- tests/2015/18/7.txt | 2 +- tests/2015/18/8.txt | 2 +- tests/2016/08/0.txt | 2 +- tests/2016/10/0.txt | 2 +- tests/2016/16/0.txt | 2 +- tests/2016/16/1.txt | 2 +- tests/2016/18/0.txt | 2 +- tests/2016/18/1.txt | 2 +- tests/2016/20/0.txt | 2 +- tests/2017/16/0.txt | 2 +- tests/2017/21/0.txt | 2 +- tests/2019/08/0.txt | 2 +- tests/2019/08/1.txt | 2 +- tests/2019/12/0.txt | 2 +- tests/2019/12/1.txt | 2 +- tests/2019/24/0.txt | 2 +- tests/2023/11/0.txt | 2 +- tests/test_examples.py | 5 ++--- 35 files changed, 36 insertions(+), 37 deletions(-) diff --git a/tests/2015/07/0.d.txt b/tests/2015/07/0.d.txt index 2aecb5d5..3080099e 100644 --- a/tests/2015/07/0.d.txt +++ b/tests/2015/07/0.d.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> f y RSHIFT 2 -> g NOT x -> h NOT y -> i -72 +72 # wire=d - diff --git a/tests/2015/07/1.e.txt b/tests/2015/07/1.e.txt index 4269e3a7..f8794076 100644 --- a/tests/2015/07/1.e.txt +++ b/tests/2015/07/1.e.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> f y RSHIFT 2 -> g NOT x -> h NOT y -> i -507 +507 # wire=e - diff --git a/tests/2015/07/2.f.txt b/tests/2015/07/2.f.txt index a8e75090..50938d1a 100644 --- a/tests/2015/07/2.f.txt +++ b/tests/2015/07/2.f.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> a y RSHIFT 2 -> g NOT x -> h NOT y -> i -492 +492 # wire=f - diff --git a/tests/2015/07/3.g.txt b/tests/2015/07/3.g.txt index da6764ab..bb6ba105 100644 --- a/tests/2015/07/3.g.txt +++ b/tests/2015/07/3.g.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> f y RSHIFT 2 -> a NOT x -> h NOT y -> i -114 +114 # wire=g - diff --git a/tests/2015/07/4.h.txt b/tests/2015/07/4.h.txt index 1929c744..e0a84977 100644 --- a/tests/2015/07/4.h.txt +++ b/tests/2015/07/4.h.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> f y RSHIFT 2 -> g NOT x -> a NOT y -> i -65412 +65412 # wire=h - diff --git a/tests/2015/07/5.i.txt b/tests/2015/07/5.i.txt index 287e8b11..1bd4fc24 100644 --- a/tests/2015/07/5.i.txt +++ b/tests/2015/07/5.i.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> f y RSHIFT 2 -> g NOT x -> h NOT y -> a -65079 +65079 # wire=i - diff --git a/tests/2015/07/6.x.txt b/tests/2015/07/6.x.txt index 7e679ff8..23f040b4 100644 --- a/tests/2015/07/6.x.txt +++ b/tests/2015/07/6.x.txt @@ -6,5 +6,5 @@ a LSHIFT 2 -> f y RSHIFT 2 -> g NOT a -> h NOT y -> i -123 +123 # wire=x - diff --git a/tests/2015/07/7.y.txt b/tests/2015/07/7.y.txt index a8ff48f4..bee1878b 100644 --- a/tests/2015/07/7.y.txt +++ b/tests/2015/07/7.y.txt @@ -6,5 +6,5 @@ x LSHIFT 2 -> f a RSHIFT 2 -> g NOT x -> h NOT a -> i -456 +456 # wire=y - diff --git a/tests/2015/14/0.txt b/tests/2015/14/0.txt index d2cfad67..481f20b3 100644 --- a/tests/2015/14/0.txt +++ b/tests/2015/14/0.txt @@ -1,4 +1,4 @@ Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds. Dancer can fly 16 km/s for 11 seconds, but then must rest for 162 seconds. -1120 +1120 # t=1000 689 diff --git a/tests/2015/17/0.txt b/tests/2015/17/0.txt index ea7a4932..55bbf8d4 100644 --- a/tests/2015/17/0.txt +++ b/tests/2015/17/0.txt @@ -3,5 +3,5 @@ 10 5 5 -4 +4 # liters=25 3 diff --git a/tests/2015/18/0.txt b/tests/2015/18/0.txt index 7e9fb91a..d7fa1e32 100644 --- a/tests/2015/18/0.txt +++ b/tests/2015/18/0.txt @@ -4,5 +4,5 @@ ..#... #.#..# ####.. -11 +11 # iterations=1 - diff --git a/tests/2015/18/1.txt b/tests/2015/18/1.txt index aabb1b9b..c84c1398 100644 --- a/tests/2015/18/1.txt +++ b/tests/2015/18/1.txt @@ -4,5 +4,5 @@ ...... #..... #.##.. -8 +8 # iterations=1 - diff --git a/tests/2015/18/2.txt b/tests/2015/18/2.txt index 8799f3ab..fd2e54ef 100644 --- a/tests/2015/18/2.txt +++ b/tests/2015/18/2.txt @@ -4,5 +4,5 @@ ...... .#.... .#.... -4 +4 # iterations=1 - diff --git a/tests/2015/18/3.txt b/tests/2015/18/3.txt index 6593a9d4..e6d393d0 100644 --- a/tests/2015/18/3.txt +++ b/tests/2015/18/3.txt @@ -4,5 +4,5 @@ ..##.. ...... ...... -4 +4 # iterations=1 - diff --git a/tests/2015/18/4.txt b/tests/2015/18/4.txt index 69ca6788..5496bd21 100644 --- a/tests/2015/18/4.txt +++ b/tests/2015/18/4.txt @@ -5,4 +5,4 @@ #.#..# ####.# - -18 +18 # iterations=1 diff --git a/tests/2015/18/5.txt b/tests/2015/18/5.txt index b07031f1..22045276 100644 --- a/tests/2015/18/5.txt +++ b/tests/2015/18/5.txt @@ -5,4 +5,4 @@ #...#. #.#### - -18 +18 # iterations=1 diff --git a/tests/2015/18/6.txt b/tests/2015/18/6.txt index 20a6e808..6998713f 100644 --- a/tests/2015/18/6.txt +++ b/tests/2015/18/6.txt @@ -5,4 +5,4 @@ .#..## ##.### - -18 +18 # iterations=1 diff --git a/tests/2015/18/7.txt b/tests/2015/18/7.txt index a54f6f47..1d86af05 100644 --- a/tests/2015/18/7.txt +++ b/tests/2015/18/7.txt @@ -5,4 +5,4 @@ ##.... ####.# - -14 +14 # iterations=1 diff --git a/tests/2015/18/8.txt b/tests/2015/18/8.txt index 8132e7df..c2604cf7 100644 --- a/tests/2015/18/8.txt +++ b/tests/2015/18/8.txt @@ -5,4 +5,4 @@ #..... #.#..# - -17 +17 # iterations=1 diff --git a/tests/2016/08/0.txt b/tests/2016/08/0.txt index 1880d743..10869fc7 100644 --- a/tests/2016/08/0.txt +++ b/tests/2016/08/0.txt @@ -2,5 +2,5 @@ rect 3x2 rotate column x=1 by 1 rotate row y=0 by 4 rotate column x=1 by 1 -6 +6 # screen_height=3, screen_width=7 - diff --git a/tests/2016/10/0.txt b/tests/2016/10/0.txt index a520641a..a95a4dc5 100644 --- a/tests/2016/10/0.txt +++ b/tests/2016/10/0.txt @@ -4,5 +4,5 @@ value 3 goes to bot 1 bot 1 gives low to output 1 and high to bot 0 bot 0 gives low to output 2 and high to output 0 value 2 goes to bot 2 -2 # chips=value-5,value-2 +2 # chip1=5,chip2=2 - diff --git a/tests/2016/16/0.txt b/tests/2016/16/0.txt index 8d44b49a..dc4cda9d 100644 --- a/tests/2016/16/0.txt +++ b/tests/2016/16/0.txt @@ -1,3 +1,3 @@ 110010110100 -100 # length=12 +100 # disk_length=12 - diff --git a/tests/2016/16/1.txt b/tests/2016/16/1.txt index e85aabb4..e596b727 100644 --- a/tests/2016/16/1.txt +++ b/tests/2016/16/1.txt @@ -1,3 +1,3 @@ 10000 -01100 # length=20 +01100 # disk_length=20 - diff --git a/tests/2016/18/0.txt b/tests/2016/18/0.txt index ec1d2591..6248ce3b 100644 --- a/tests/2016/18/0.txt +++ b/tests/2016/18/0.txt @@ -1,3 +1,3 @@ ..^^. -6 # rows=3 +6 # n_rows=3 - diff --git a/tests/2016/18/1.txt b/tests/2016/18/1.txt index 39248931..7752dc99 100644 --- a/tests/2016/18/1.txt +++ b/tests/2016/18/1.txt @@ -1,3 +1,3 @@ .^^.^.^^^^ -38 # rows=10 +38 # n_rows=10 - diff --git a/tests/2016/20/0.txt b/tests/2016/20/0.txt index c07ab74f..098ab7ba 100644 --- a/tests/2016/20/0.txt +++ b/tests/2016/20/0.txt @@ -1,5 +1,5 @@ 5-8 0-2 4-7 -3 +3 # max_val=9 2 diff --git a/tests/2017/16/0.txt b/tests/2017/16/0.txt index 82805e48..fcd6e955 100644 --- a/tests/2017/16/0.txt +++ b/tests/2017/16/0.txt @@ -1,3 +1,3 @@ s1,x3/4,pe/b baedc -ceadb # n_programs=5,n_iterations=2 +ceadb # n_programs=5,iterations=2 diff --git a/tests/2017/21/0.txt b/tests/2017/21/0.txt index 0eeec58f..218b9028 100644 --- a/tests/2017/21/0.txt +++ b/tests/2017/21/0.txt @@ -1,4 +1,4 @@ ../.# => ##./#../... .#./..#/### => #..#/..../..../#..# -12 # after 2 iterations +12 # iterations=2 - diff --git a/tests/2019/08/0.txt b/tests/2019/08/0.txt index 7a6d34f4..8691e1b9 100644 --- a/tests/2019/08/0.txt +++ b/tests/2019/08/0.txt @@ -1,3 +1,3 @@ 123456789012 -1 +1 # image_width=3,image_height=2 - diff --git a/tests/2019/08/1.txt b/tests/2019/08/1.txt index 530043c7..edfedac4 100644 --- a/tests/2019/08/1.txt +++ b/tests/2019/08/1.txt @@ -1,3 +1,3 @@ 0222112222120000 -- +- # image_width=2,image_height=2 ⟋ diff --git a/tests/2019/12/0.txt b/tests/2019/12/0.txt index 21cdf4ba..6fb656f6 100644 --- a/tests/2019/12/0.txt +++ b/tests/2019/12/0.txt @@ -2,5 +2,5 @@ -179 # after 10 steps +179 # iterations=10 2772 diff --git a/tests/2019/12/1.txt b/tests/2019/12/1.txt index 3334afb4..731592ca 100644 --- a/tests/2019/12/1.txt +++ b/tests/2019/12/1.txt @@ -2,5 +2,5 @@ -1940 # after 100 steps +1940 # iterations=100 4686774924 diff --git a/tests/2019/24/0.txt b/tests/2019/24/0.txt index ced6071a..e863e371 100644 --- a/tests/2019/24/0.txt +++ b/tests/2019/24/0.txt @@ -4,4 +4,4 @@ ..#.. #.... 2129920 -99 # after 10 minutes +99 # iterations=10 diff --git a/tests/2023/11/0.txt b/tests/2023/11/0.txt index c18a03d5..d3a02258 100644 --- a/tests/2023/11/0.txt +++ b/tests/2023/11/0.txt @@ -9,4 +9,4 @@ .......#.. #...#..... 374 -8410 # expansion factor 100 +8410 # expansion_factor=100 diff --git a/tests/test_examples.py b/tests/test_examples.py index aaa319e5..6236cbdc 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,3 +1,4 @@ +import ast import json import pathlib @@ -41,14 +42,12 @@ def parse_extra_context(extra): k = k.strip() v = v.strip() try: - v = int(v) + v = ast.literal_eval(v) except ValueError: pass if k in result: raise NotImplementedError(f"Duplicate key {k!r}") result[k] = v - else: - raise NotImplementedError(f"Unexpected {line=}") return result From e770e195c52fa580cd9d5b4ee4f83ad4feee4357 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Wed, 11 Dec 2024 22:29:16 -0600 Subject: [PATCH 03/23] fix workflow --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c933fae7..375860e3 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,7 +30,7 @@ jobs: run: | uv venv uv pip install build - pyproject-build + .venv/bin/pyproject-build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1 From bbea2cfdf76368537ad211a562c2930ebfbb738c Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sat, 14 Dec 2024 15:26:09 -0600 Subject: [PATCH 04/23] better aocd mock --- aoc_wim/util.py | 2 +- tests/conftest.py | 25 +++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/aoc_wim/util.py b/aoc_wim/util.py index 1b9b81ed..3bd89c91 100644 --- a/aoc_wim/util.py +++ b/aoc_wim/util.py @@ -111,7 +111,7 @@ def start(): # import numpy as np # import networkx as nx - # from aoc_wim.zgrid import ZGrid; g = ZGrid(data) + # from aoc_wim.zgrid import ZGrid; grid = ZGrid(data) # from aoc_wim import stuff # import logging; logging.basicConfig(level=logging.DEBUG) diff --git a/tests/conftest.py b/tests/conftest.py index 0ac38019..d3810976 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,13 +1,30 @@ import datetime +import json +import os import re import sys import pytest -Module = type(sys) -mock_aocd = Module("aocd") -mock_aocd.data = "" -sys.modules["aocd"] = mock_aocd + +class MockAocd(type(sys)): + def __getattr__(self, name): + if name == "extra": + return json.loads(os.environ.get("AOCD_EXTRA", "{}")) + if name == "data": + return "" + raise AttributeError(name) + + def submit(self, *args, **kwargs): + msg = f"Submission is disabled during test" + if len(args) == 1 and not kwargs: + msg += f" ({args[0]!r})" + else: + msg += f" ({args=} {kwargs=})" + raise NotImplementedError(msg) + + +sys.modules["aocd"] = MockAocd("aocd") def pytest_addoption(parser): From 7094dada9e64b11c4eb9a0d02aaf55286ab1771c Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sat, 14 Dec 2024 15:58:41 -0600 Subject: [PATCH 05/23] PAT --- .github/workflows/tag.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 6ff88cbc..75e587e3 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -16,6 +16,8 @@ jobs: with: fetch-depth: 0 fetch-tags: true + token: ${{ secrets.AOCW_PAT }} + # use a personal access token so that pushed tags can trigger publish.yml workflow - name: Install uv uses: astral-sh/setup-uv@v3 From e6dc26f3c01923c21624492a203f8f8a1a010b07 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 16 Dec 2024 22:06:23 -0600 Subject: [PATCH 06/23] consistent naming --- aoc_wim/aoc2018/q22.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aoc_wim/aoc2018/q22.py b/aoc_wim/aoc2018/q22.py index e4c5ea82..a02b5a19 100644 --- a/aoc_wim/aoc2018/q22.py +++ b/aoc_wim/aoc2018/q22.py @@ -118,6 +118,6 @@ def adjacent(self, state): state0 = (grid.pos, "🔦") target = (grid.target, "🔦") -a_star = Q22AStar(state0=state0, target=target, grid=grid) -a_star.run() -print("answer_b:", a_star.gscore[target]) +astar = Q22AStar(state0=state0, target=target, grid=grid) +astar.run() +print("answer_b:", astar.gscore[target]) From 9299a4c7596a6721bc64ce957a54ecc21bbcbe19 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Thu, 19 Dec 2024 01:14:04 -0600 Subject: [PATCH 07/23] day 22 --- aoc_wim/aoc2023/q22.py | 112 +++++++++++++++++++++++++++++++++++++++++ tests/2023/22/0.txt | 9 ++++ tests/conftest.py | 3 +- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 aoc_wim/aoc2023/q22.py create mode 100644 tests/2023/22/0.txt diff --git a/aoc_wim/aoc2023/q22.py b/aoc_wim/aoc2023/q22.py new file mode 100644 index 00000000..f925d8f8 --- /dev/null +++ b/aoc_wim/aoc2023/q22.py @@ -0,0 +1,112 @@ +""" +--- Day 22: Sand Slabs --- +https://adventofcode.com/2023/day/22 +""" +from aocd import data +from dataclasses import dataclass +import operator as op + + +@dataclass +class Brick: + x0: int + y0: int + z0: int + x1: int + y1: int + z1: int + id: int = None + + @classmethod + def fromline(cls, line): + return cls(*map(int, line.replace("~", ",").split(","))) + + def __post_init__(self): + if self.x0 > self.x1 or self.y0 > self.y1 or self.z0 > self.z1: + raise ValueError(f"Invalid brick {self}") + + def cells(self, dz=0): + xs = range(self.x0, self.x1 + 1) + ys = range(self.y0, self.y1 + 1) + zs = range(self.z0, self.z1 + 1) + return {(x, y, z + dz) for x in xs for y in ys for z in zs} + + def above(self): + xs = range(self.x0, self.x1 + 1) + ys = range(self.y0, self.y1 + 1) + return {(x, y, self.z1 + 1) for x in xs for y in ys} + + def below(self): + xs = range(self.x0, self.x1 + 1) + ys = range(self.y0, self.y1 + 1) + return {(x, y, self.z0 - 1) for x in xs for y in ys} + + def fall(self): + self.z0 -= 1 + self.z1 -= 1 + + +tdata = """1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9""" +# data = tdata + +bricks = [Brick.fromline(x) for x in data.splitlines()] +bricks.sort(key=op.attrgetter("z0")) +space = {} +for i, brick in enumerate(bricks): + brick.id = i + xs = range(brick.x0, brick.x1 + 1) + ys = range(brick.y0, brick.y1 + 1) + zs = range(brick.z0, brick.z1 + 1) + space.update(dict.fromkeys(brick.cells(), brick.id)) + +while True: + mutated = False + for brick in bricks: + if brick.z0 == 1: + continue + if not space.keys() & brick.below(): + for cell in brick.cells(): + del space[cell] + brick.fall() + space.update() + for cell in brick.cells(): + space[cell] = brick.id + mutated = True + if not mutated: + break + +bricks_below = [set() for b in bricks] +bricks_above = [set() for b in bricks] +for brick in bricks: + for cell in brick.above(): + if cell in space: + bricks_above[brick.id].add(space[cell]) + bricks_below[space[cell]].add(brick.id) + +supports = [set() for b in bricks] +for brick in bricks: + for brick_above in bricks_above[brick.id]: + if len(bricks_below[brick_above]) == 1: + supports[brick.id].add(brick_above) + +a = sum(not s for s in supports) +print("answer_a:", a) + +b = 0 +for brick in bricks: + stack = list(supports[brick.id]) + cumulative_support = set() + while stack: + b_id = stack.pop() + cumulative_support.add(b_id) + for brick_above in bricks_above[b_id]: + if bricks_below[brick_above] <= cumulative_support: + stack.append(brick_above) + b += len(cumulative_support) +print("answer_b:", b) diff --git a/tests/2023/22/0.txt b/tests/2023/22/0.txt new file mode 100644 index 00000000..451cb7d1 --- /dev/null +++ b/tests/2023/22/0.txt @@ -0,0 +1,9 @@ +1,0,1~1,2,1 +0,0,2~2,0,2 +0,2,3~2,2,3 +0,0,4~0,2,4 +2,0,5~2,2,5 +0,1,6~2,1,6 +1,1,8~1,1,9 +5 +7 diff --git a/tests/conftest.py b/tests/conftest.py index d3810976..100994c3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ import os import re import sys +import warnings import pytest @@ -21,7 +22,7 @@ def submit(self, *args, **kwargs): msg += f" ({args[0]!r})" else: msg += f" ({args=} {kwargs=})" - raise NotImplementedError(msg) + warnings.warn(msg) sys.modules["aocd"] = MockAocd("aocd") From 15697ea9529d0a688aa7d7fef35ff593ff1b584e Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sun, 22 Dec 2024 02:10:41 -0600 Subject: [PATCH 08/23] day 23 --- aoc_wim/aoc2023/q23.py | 58 ++++++++++++++++++++++++++++++++++++++++++ tests/2023/23/0.txt | 25 ++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 aoc_wim/aoc2023/q23.py create mode 100644 tests/2023/23/0.txt diff --git a/aoc_wim/aoc2023/q23.py b/aoc_wim/aoc2023/q23.py new file mode 100644 index 00000000..613d226a --- /dev/null +++ b/aoc_wim/aoc2023/q23.py @@ -0,0 +1,58 @@ +""" +--- Day 23: A Long Walk --- +https://adventofcode.com/2023/day/23 +""" +from aocd import data +from aoc_wim.zgrid import ZGrid + + +grid = ZGrid(data) +start = 1 +end = grid.bottom_right - 1 + +nodes = {} +dzs = dict(zip("^>v<", [-1j, 1, 1j, -1])) +for z, g in grid.items(): + if g == "." and sum(grid.get(z + dz) in dzs for dz in dzs.values()) >= 2: + nodes[z] = {} + + +def find_next_node(z0, dz0): + path = [z0, z0 + dz0] + while path[-1] not in nodes: + [z1] = [z for z in grid.near(path[-1]) if z != path[-2] and grid.get(z, "#") != "#"] + path.append(z1) + return path[-1], path[-2] - path[-1], len(path) - 1 + + +nodes[start] = {1j: find_next_node(start, 1j)} +nodes[end] = {-1j: find_next_node(end, -1j)} +for z0, branches in nodes.items(): + for dz0 in dzs.values(): + g = grid.get(z0 + dz0) + if g in dzs and dz0 not in branches: + z1, dz1, dist = find_next_node(z0, dz0) + branches[dz0] = z1, dist + nodes[z1][dz1] = z0, dist + + +def longest_path(part="a"): + grid[start + 1j] = "v" + paths = [] + stack = [(start, 0)] + while stack: + path = stack.pop() + z0, d0 = path[-2:] + if z0 == end: + paths.append(path) + branches = nodes[z0] + for g, dz in dzs.items(): + if dz in branches and grid[z0 + dz] in ([g] if part == "a" else dzs): + z1, dist = branches[dz] + if z1 not in path: + stack.append(path + (z1, d0 + dist)) + return max(p[-1] for p in paths) + + +print("answer_a:", longest_path(part="a")) +print("answer_b:", longest_path(part="b")) diff --git a/tests/2023/23/0.txt b/tests/2023/23/0.txt new file mode 100644 index 00000000..ff5db972 --- /dev/null +++ b/tests/2023/23/0.txt @@ -0,0 +1,25 @@ +#.##################### +#.......#########...### +#######.#########.#.### +###.....#.>.>.###.#.### +###v#####.#v#.###.#.### +###.>...#.#.#.....#...# +###v###.#.#.#########.# +###...#.#.#.......#...# +#####.#.#.#######.#.### +#.....#.#.#.......#...# +#.#####.#.#.#########v# +#.#...#...#...###...>.# +#.#.#v#######v###.###v# +#...#.>.#...>.>.#.###.# +#####v#.#.###v#.#.###.# +#.....#...#...#.#.#...# +#.#########.###.#.#.### +#...###...#...#...#.### +###.###.#.###v#####v### +#...#...#.#.>.>.#.>.### +#.###.###.#.###.#.#v### +#.....###...###...#...# +#####################.# +94 +154 From 08f1f0542dd3f6cbef3e9d088d262e50c712e35a Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sun, 22 Dec 2024 02:14:37 -0600 Subject: [PATCH 09/23] auto-publish --- .github/workflows/publish.yml | 36 ----------------------------------- .github/workflows/tag.yml | 31 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 375860e3..00000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Upload to PyPI -on: - push: - tags: - - "[0-9]+.[0-9]+" - -jobs: - pypi-publish: - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - name: Upload release to PyPI - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/advent-of-code-wim - permissions: - id-token: write - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v3 - with: - enable-cache: true - cache-dependency-glob: pyproject.toml - - - name: Install Python 3.13 - run: uv python install 3.13 - - - name: Build package - run: | - uv venv - uv pip install build - .venv/bin/pyproject-build - - - name: Publish package distributions to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml index 75e587e3..a19c2f54 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/tag.yml @@ -62,3 +62,34 @@ jobs: ref: 'refs/tags/${{ needs.version-check.outputs.v }}', sha: context.sha }) + + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + needs: version-tag + if: needs.version-check.outputs.v + environment: + name: pypi + url: https://pypi.org/p/advent-of-code-wim + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: pyproject.toml + + - name: Install Python 3.13 + run: uv python install 3.13 + + - name: Build package + run: | + uv venv + uv pip install build + .venv/bin/pyproject-build + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 From 1d8b3aaeb289c85e62ca0b97ef998f097ffd86ad Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sun, 22 Dec 2024 02:25:29 -0600 Subject: [PATCH 10/23] trusted publishing --- .github/workflows/{tag.yml => publish.yml} | 2 -- 1 file changed, 2 deletions(-) rename .github/workflows/{tag.yml => publish.yml} (94%) diff --git a/.github/workflows/tag.yml b/.github/workflows/publish.yml similarity index 94% rename from .github/workflows/tag.yml rename to .github/workflows/publish.yml index a19c2f54..47f3ff9f 100644 --- a/.github/workflows/tag.yml +++ b/.github/workflows/publish.yml @@ -16,8 +16,6 @@ jobs: with: fetch-depth: 0 fetch-tags: true - token: ${{ secrets.AOCW_PAT }} - # use a personal access token so that pushed tags can trigger publish.yml workflow - name: Install uv uses: astral-sh/setup-uv@v3 From f986a71f2f71130d89362a06c1d22d6d20c4875b Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sun, 22 Dec 2024 22:28:26 -0600 Subject: [PATCH 11/23] day 24 --- aoc_wim/aoc2023/q24.py | 62 ++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 1 + tests/2023/24/0.txt | 7 +++++ 3 files changed, 70 insertions(+) create mode 100644 aoc_wim/aoc2023/q24.py create mode 100644 tests/2023/24/0.txt diff --git a/aoc_wim/aoc2023/q24.py b/aoc_wim/aoc2023/q24.py new file mode 100644 index 00000000..7f39de1c --- /dev/null +++ b/aoc_wim/aoc2023/q24.py @@ -0,0 +1,62 @@ +""" +--- Day 24: Never Tell Me The Odds --- +https://adventofcode.com/2023/day/24 +""" +from aocd import data +from aocd import extra +from dataclasses import dataclass +import itertools as it +import z3 + + +@dataclass +class Hailstone: + x: int + y: int + z: int + dx: int + dy: int + dz: int + + # TODO: from_line fromline naming consistency + @classmethod + def from_line(cls, line): + return cls(*map(int, line.replace("@", ",").split(","))) + + +def intersect2d(h0, h1): + denom = h0.dx * h1.dy - h0.dy * h1.dx + if denom == 0: + return float("inf"), float("inf") + f = h0.y * h0.dx - h0.x * h0.dy + g = h1.x * h1.dy - h1.y * h1.dx + px = (f * h1.dx + g * h0.dx) / denom + py = (f * h1.dy + g * h0.dy) / denom + return px, py + + +test_area_min = extra.get("test_area_min", 200_000_000_000_000) +test_area_max = extra.get("test_area_max", 400_000_000_000_000) +a = 0 +hailstones = [Hailstone.from_line(line) for line in data.splitlines()] +for h0, h1 in it.combinations(hailstones, 2): + px, py = intersect2d(h0, h1) + if test_area_min <= px <= test_area_max and test_area_min <= py <= test_area_max: + if (px > h0.x and h0.dx > 0) or (px < h0.x and h0.dx < 0): + if (px > h1.x and h1.dx > 0) or (px < h1.x and h1.dx < 0): + a += 1 +print("answer_a:", a) + + +x, y, z, dx, dy, dz = z3.Ints("x y z dx dy dz") +ts = z3.Ints("t0 t1 t2") +s = z3.Solver() +for t, h in zip(ts, hailstones): + s.add(t >= 0) + s.add(h.x + t * h.dx == x + t * dx) + s.add(h.y + t * h.dy == y + t * dy) + s.add(h.z + t * h.dz == z + t * dz) +s.check() +m = s.model() +b = m[x].as_long() + m[y].as_long() + m[z].as_long() +print("answer_b:", b) diff --git a/pyproject.toml b/pyproject.toml index 2c4f52c9..a8801bba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ dependencies = [ "marshmallow", "regex", "intervaltree", + "z3-solver", ] [[project.authors]] diff --git a/tests/2023/24/0.txt b/tests/2023/24/0.txt new file mode 100644 index 00000000..5c9b8121 --- /dev/null +++ b/tests/2023/24/0.txt @@ -0,0 +1,7 @@ +19, 13, 30 @ -2, 1, -2 +18, 19, 22 @ -1, -1, -2 +20, 25, 34 @ -2, -2, -4 +12, 31, 28 @ -1, -2, -1 +20, 19, 15 @ 1, -5, -3 +2 # test_area_min=7,test_area_max=27 +47 From ddeab61a14e8013a0a651296e7aec0f005fbccf7 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 01:40:21 -0600 Subject: [PATCH 12/23] day 25 --- aoc_wim/aoc2023/q25.py | 22 ++++++++++++++++++++++ tests/2023/25/0.txt | 15 +++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 aoc_wim/aoc2023/q25.py create mode 100644 tests/2023/25/0.txt diff --git a/aoc_wim/aoc2023/q25.py b/aoc_wim/aoc2023/q25.py new file mode 100644 index 00000000..529a4d0c --- /dev/null +++ b/aoc_wim/aoc2023/q25.py @@ -0,0 +1,22 @@ +""" +--- Day 25: Snowverload --- +https://adventofcode.com/2023/day/25 +""" +from aocd import data +import networkx as nx + + +graph = nx.Graph() +for line in data.replace(":", "").splitlines(): + left, *rights = line.split() + for right in rights: + graph.add_edge(left, right) + +ebc = nx.edge_betweenness_centrality(graph) +for i in range(3): + edge = max(ebc, key=ebc.get) + graph.remove_edge(*edge) + del ebc[edge] + +comp1, comp2 = nx.connected_components(graph) +print("answer_a:", len(comp1) * len(comp2)) diff --git a/tests/2023/25/0.txt b/tests/2023/25/0.txt new file mode 100644 index 00000000..b5bf9dad --- /dev/null +++ b/tests/2023/25/0.txt @@ -0,0 +1,15 @@ +jqt: rhn xhk nvd +rsh: frs pzl lsr +xhk: hfx +cmg: qnr nvd lhk bvb +rhn: xhk bvb hfx +bvb: xhk hfx +pzl: lsr hfx nvd +qnr: nvd +ntq: jqt hfx bvb xhk +nvd: lhk +lsr: lhk +rzs: qnr cmg lsr rsh +frs: qnr lhk lsr +54 +- From 64cff75db3ac349a0a6920c8bf93f4e29ef7216e Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:23:28 -0600 Subject: [PATCH 13/23] 2024/06 (#118) * day 06 * fix edge case --- aoc_wim/aoc2024/q06.py | 34 ++++++++++++++++++++++++++++++++++ tests/2024/06/0.txt | 12 ++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 aoc_wim/aoc2024/q06.py create mode 100644 tests/2024/06/0.txt diff --git a/aoc_wim/aoc2024/q06.py b/aoc_wim/aoc2024/q06.py new file mode 100644 index 00000000..bf24a167 --- /dev/null +++ b/aoc_wim/aoc2024/q06.py @@ -0,0 +1,34 @@ +""" +--- Day 6: Guard Gallivant --- +https://adventofcode.com/2024/day/6 +""" +from aocd import data +from aoc_wim.zgrid import ZGrid + + +def walk(): + z, dz = z0, dz0 + seen = {(z, dz)} + while z + dz in grid: + while grid[z + dz] == "#": + dz *= 1j + z += dz + if (z, dz) in seen: + return seen, True + seen.add((z, dz)) + return seen, False + + +grid = ZGrid(data) +z0, dz0 = grid.z("^"), -1j +seen, _ = walk() +path = dict(seen) +print("answer_a:", len(path)) + +b = 0 +path.pop(z0) +for z in path: + grid[z] = "#" + b += walk()[1] + grid[z] = "." +print("answer_b:", b) diff --git a/tests/2024/06/0.txt b/tests/2024/06/0.txt new file mode 100644 index 00000000..367c378f --- /dev/null +++ b/tests/2024/06/0.txt @@ -0,0 +1,12 @@ +....#..... +.........# +.......... +..#....... +.......#.. +.......... +.#..^..... +........#. +#......... +......#... +41 +6 From 10ad5f7e8f0eb685307f9926a6e2a24bee87072f Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:26:20 -0600 Subject: [PATCH 14/23] 2024/07 (#119) * day 07 --- aoc_wim/aoc2024/q07.py | 35 +++++++++++++++++++++++++++++++++++ tests/2024/07/0.txt | 11 +++++++++++ 2 files changed, 46 insertions(+) create mode 100644 aoc_wim/aoc2024/q07.py create mode 100644 tests/2024/07/0.txt diff --git a/aoc_wim/aoc2024/q07.py b/aoc_wim/aoc2024/q07.py new file mode 100644 index 00000000..bc9959e8 --- /dev/null +++ b/aoc_wim/aoc2024/q07.py @@ -0,0 +1,35 @@ +""" +--- Day 7: Bridge Repair --- +https://adventofcode.com/2024/day/7 +""" +from aocd import data +import operator as op +import itertools as it + + +def concat(n1, n2): + return int(str(n1) + str(n2)) + + +a = b = 0 +for line in data.replace(":", "").splitlines(): + result, *ns = map(int, line.split()) + for ops in it.product((op.add, op.mul), repeat=len(ns)-1): + val = ns[0] + for i, f in enumerate(ops, 1): + val = f(val, ns[i]) + if val == result: + a += result + b += result + break + else: + for ops in it.product((op.add, op.mul, concat), repeat=len(ns) - 1): + val = ns[0] + for i, f in enumerate(ops, 1): + val = f(val, ns[i]) + if val == result: + b += result + break + +print("answer_a:", a) +print("answer_b:", b) diff --git a/tests/2024/07/0.txt b/tests/2024/07/0.txt new file mode 100644 index 00000000..2a9e92d9 --- /dev/null +++ b/tests/2024/07/0.txt @@ -0,0 +1,11 @@ +190: 10 19 +3267: 81 40 27 +83: 17 5 +156: 15 6 +7290: 6 8 6 15 +161011: 16 10 13 +192: 17 8 14 +21037: 9 7 18 13 +292: 11 6 16 20 +3749 +11387 From 91b700ffa8f14f33378201b47955038cbce5b9b5 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:37:51 -0600 Subject: [PATCH 15/23] 2024/08 (#120) * day 08 --- aoc_wim/aoc2024/q08.py | 30 ++++++++++++++++++++++++++++++ tests/2024/08/0.txt | 14 ++++++++++++++ tests/2024/08/1.txt | 12 ++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 aoc_wim/aoc2024/q08.py create mode 100644 tests/2024/08/0.txt create mode 100644 tests/2024/08/1.txt diff --git a/aoc_wim/aoc2024/q08.py b/aoc_wim/aoc2024/q08.py new file mode 100644 index 00000000..a8656b8e --- /dev/null +++ b/aoc_wim/aoc2024/q08.py @@ -0,0 +1,30 @@ +""" +--- Day 8: Resonant Collinearity --- +https://adventofcode.com/2024/day/8 +""" +from aocd import data +from aoc_wim.zgrid import ZGrid +from collections import defaultdict +import itertools as it + +grid = ZGrid(data) +d = defaultdict(list) +for z, g in grid.items(): + if g not in ".#": + d[g].append(z) + +a = set() +b = set() +for zs in d.values(): + for z1, z2 in it.combinations(zs, 2): + b.update((z1, z2)) + for z, dz in ((z1, z1 - z2), (z2, z2 - z1)): + ns = [] + while z + dz in grid: + z += dz + ns.append(z) + a.update(ns[:1]) + b.update(ns) + +print("answer_a:", len(a)) +print("answer_b:", len(b)) diff --git a/tests/2024/08/0.txt b/tests/2024/08/0.txt new file mode 100644 index 00000000..9242d318 --- /dev/null +++ b/tests/2024/08/0.txt @@ -0,0 +1,14 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............ +14 +34 diff --git a/tests/2024/08/1.txt b/tests/2024/08/1.txt new file mode 100644 index 00000000..60a20a10 --- /dev/null +++ b/tests/2024/08/1.txt @@ -0,0 +1,12 @@ +T....#.... +...T...... +.T....#... +.........# +..#....... +.......... +...#...... +.......... +....#..... +.......... +- +9 From 582f6e3862ed42d31c98a0cf2ee80332ad9f68e6 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:42:01 -0600 Subject: [PATCH 16/23] 2024/09 (#121) * day 09 --- aoc_wim/aoc2024/q09.py | 61 ++++++++++++++++++++++++++++++++++++++++++ tests/2024/09/0.txt | 3 +++ 2 files changed, 64 insertions(+) create mode 100644 aoc_wim/aoc2024/q09.py create mode 100644 tests/2024/09/0.txt diff --git a/aoc_wim/aoc2024/q09.py b/aoc_wim/aoc2024/q09.py new file mode 100644 index 00000000..a3da9002 --- /dev/null +++ b/aoc_wim/aoc2024/q09.py @@ -0,0 +1,61 @@ +""" +--- Day 9: Disk Fragmenter --- +https://adventofcode.com/2024/day/9 +""" +from aocd import data + + +disk = [*map(int, data)] +offset = disk.pop(0) # ignore file 0, it can't move and I want to use id 0 for space +for i, val in enumerate(disk): + f_id = (i + 1) // 2 if i % 2 else 0 + disk[i] = f_id, val +max_f_id = f_id + + +def checksum(disk): + i = offset + result = 0 + for f_id, size in disk: + result += sum(f_id * (i + j) for j in range(size)) + i += size + return result + + +def defrag_a(disk): + disk = disk.copy() + while True: + for i, (f, space) in enumerate(disk): + if f: + continue + f_id, size = disk.pop() + if f_id == 0: + break + if size <= space: + disk[i:i+1] = (f_id, size), (0, space - size) + break + else: + disk[i] = f_id, space + disk.append((f_id, size - space)) + break + else: + return disk + + +def defrag_b(disk): + disk = disk.copy() + for f_id in range(max_f_id, 0, -1): + for i, (f, size) in enumerate(reversed(disk)): + if f == f_id: + i = len(disk) - 1 - i + break + for j, (f, space) in enumerate(disk): + if not f and space >= size and j < i: + disk[i] = 0, size + disk[j:j+1] = (f_id, size), (0, space - size) + break + return disk + + +print("answer_a:", checksum(defrag_a(disk))) +print("answer_b:", checksum(defrag_b(disk))) diff --git a/tests/2024/09/0.txt b/tests/2024/09/0.txt new file mode 100644 index 00000000..08346ddc --- /dev/null +++ b/tests/2024/09/0.txt @@ -0,0 +1,3 @@ +2333133121414131402 +1928 +2858 From c753e08d42aff53bf4160a3ec02799a97768d565 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:45:27 -0600 Subject: [PATCH 17/23] day 10 --- aoc_wim/aoc2024/q10.py | 24 ++++++++++++++++++++++++ tests/2024/10/0.txt | 10 ++++++++++ 2 files changed, 34 insertions(+) create mode 100644 aoc_wim/aoc2024/q10.py create mode 100644 tests/2024/10/0.txt diff --git a/aoc_wim/aoc2024/q10.py b/aoc_wim/aoc2024/q10.py new file mode 100644 index 00000000..8224a5b6 --- /dev/null +++ b/aoc_wim/aoc2024/q10.py @@ -0,0 +1,24 @@ +""" +--- Day 10: Hoof It --- +https://adventofcode.com/2024/day/10 +""" +from aocd import data +import networkx as nx +from aoc_wim.zgrid import ZGrid + +grid = ZGrid(data, transform=int) +graph = nx.DiGraph() +for z0, n in grid.items(): + for z in grid.near(z0): + if grid.get(z, n) - n == 1: + graph.add_edge(z0, z) + +a = b = 0 +for head in grid.z(0, first=False): + for peak in grid.z(9, first=False): + paths = list(nx.all_simple_paths(graph, head, peak)) + a += bool(paths) + b += len(paths) + +print("answer_a:", a) +print("answer_b:", b) diff --git a/tests/2024/10/0.txt b/tests/2024/10/0.txt new file mode 100644 index 00000000..5e0e5621 --- /dev/null +++ b/tests/2024/10/0.txt @@ -0,0 +1,10 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 +36 +81 From fc5daceff256e9e2e191bbf5eff6a1fc6df64e19 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:52:19 -0600 Subject: [PATCH 18/23] 2024/11 (#123) * day 11 --- aoc_wim/aoc2024/q11.py | 30 ++++++++++++++++++++++++++++++ tests/2024/11/0.txt | 3 +++ 2 files changed, 33 insertions(+) create mode 100644 aoc_wim/aoc2024/q11.py create mode 100644 tests/2024/11/0.txt diff --git a/aoc_wim/aoc2024/q11.py b/aoc_wim/aoc2024/q11.py new file mode 100644 index 00000000..e4f16578 --- /dev/null +++ b/aoc_wim/aoc2024/q11.py @@ -0,0 +1,30 @@ +""" +--- Day 11: Plutonian Pebbles --- +https://adventofcode.com/2024/day/11 +""" +from aocd import data +from collections import Counter + + +def blink(d): + result = Counter() + for k, n in d.items(): + if k == "0": + result["1"] += n + elif len(k) % 2 == 0: + i = len(k) // 2 + result[k[:i].lstrip("0") or "0"] += n + result[k[i:].lstrip("0") or "0"] += n + else: + result[str(int(k) * 2024)] += n + return result + + +stones = Counter(data.split()) +for i in range(25): + stones = blink(stones) +print("answer_a:", sum(stones.values())) + +for i in range(50): + stones = blink(stones) +print("answer_b:", sum(stones.values())) diff --git a/tests/2024/11/0.txt b/tests/2024/11/0.txt new file mode 100644 index 00000000..f75fdecb --- /dev/null +++ b/tests/2024/11/0.txt @@ -0,0 +1,3 @@ +125 17 +55312 +- From 2432a7a593136d3f37b6b6bb9a1105a942a6f160 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:54:43 -0600 Subject: [PATCH 19/23] 2024/12 (#124) * day 12 --- aoc_wim/aoc2024/q12.py | 34 ++++++++++++++++++++++++++++++++++ tests/2024/12/0.txt | 6 ++++++ tests/2024/12/1.txt | 7 +++++++ tests/2024/12/2.txt | 12 ++++++++++++ tests/2024/12/3.txt | 7 +++++++ tests/2024/12/4.txt | 8 ++++++++ 6 files changed, 74 insertions(+) create mode 100644 aoc_wim/aoc2024/q12.py create mode 100644 tests/2024/12/0.txt create mode 100644 tests/2024/12/1.txt create mode 100644 tests/2024/12/2.txt create mode 100644 tests/2024/12/3.txt create mode 100644 tests/2024/12/4.txt diff --git a/aoc_wim/aoc2024/q12.py b/aoc_wim/aoc2024/q12.py new file mode 100644 index 00000000..6b291166 --- /dev/null +++ b/aoc_wim/aoc2024/q12.py @@ -0,0 +1,34 @@ +""" +--- Day 12: Garden Groups --- +https://adventofcode.com/2024/day/12 +""" +from aocd import data +import networkx as nx +from aoc_wim.zgrid import ZGrid + + +grid = ZGrid(data) +graph = nx.Graph() +for z, g in grid.items(): + graph.add_node(z) + if grid.get(z + 1) == g: + graph.add_edge(z, z + 1) + if grid.get(z + 1j) == g: + graph.add_edge(z, z + 1j) + +regions = [frozenset(r) for r in nx.connected_components(graph)] +for r in regions: + grid.update(dict.fromkeys(r, r)) # label + +area = {r: len(r) for r in regions} +perimeter = {r: sum(4 - len(graph[z]) for z in r) for r in regions} +n_sides = dict.fromkeys(regions, 0) +for z, r in grid.items(): + dzs = -1j, 1, 1j, -1, -1j + for dz0, dz2 in zip(dzs, dzs[1:]): + rs = [grid.get(z + dz) for dz in (dz0, dz0 + dz2, dz2)] + n_sides[r] += r is not rs[0] and r is not rs[2] + n_sides[r] += r is rs[0] is rs[2] and r is not rs[1] + +print("answer_a:", sum(area[r] * perimeter[r] for r in regions)) +print("answer_b:", sum(area[r] * n_sides[r] for r in regions)) diff --git a/tests/2024/12/0.txt b/tests/2024/12/0.txt new file mode 100644 index 00000000..538a23ad --- /dev/null +++ b/tests/2024/12/0.txt @@ -0,0 +1,6 @@ +AAAA +BBCD +BBCC +EEEC +140 +80 diff --git a/tests/2024/12/1.txt b/tests/2024/12/1.txt new file mode 100644 index 00000000..f58892a1 --- /dev/null +++ b/tests/2024/12/1.txt @@ -0,0 +1,7 @@ +OOOOO +OXOXO +OOOOO +OXOXO +OOOOO +772 +436 diff --git a/tests/2024/12/2.txt b/tests/2024/12/2.txt new file mode 100644 index 00000000..d25a3f77 --- /dev/null +++ b/tests/2024/12/2.txt @@ -0,0 +1,12 @@ +RRRRIICCFF +RRRRIICCCF +VVRRRCCFFF +VVRCCCJFFF +VVVVCJJCFE +VVIVCCJJEE +VVIIICJJEE +MIIIIIJJEE +MIIISIJEEE +MMMISSJEEE +1930 +1206 diff --git a/tests/2024/12/3.txt b/tests/2024/12/3.txt new file mode 100644 index 00000000..46bb9470 --- /dev/null +++ b/tests/2024/12/3.txt @@ -0,0 +1,7 @@ +EEEEE +EXXXX +EEEEE +EXXXX +EEEEE +- +236 diff --git a/tests/2024/12/4.txt b/tests/2024/12/4.txt new file mode 100644 index 00000000..87626b4a --- /dev/null +++ b/tests/2024/12/4.txt @@ -0,0 +1,8 @@ +AAAAAA +AAABBA +AAABBA +ABBAAA +ABBAAA +AAAAAA +- +368 From 189e8b569229837b8efc9b2d1c74c0949407448f Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 02:57:09 -0600 Subject: [PATCH 20/23] 2024/13 (#125) * day 13 --- aoc_wim/aoc2024/q13.py | 24 ++++++++++++++++++++++++ tests/2024/13/0.txt | 17 +++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 aoc_wim/aoc2024/q13.py create mode 100644 tests/2024/13/0.txt diff --git a/aoc_wim/aoc2024/q13.py b/aoc_wim/aoc2024/q13.py new file mode 100644 index 00000000..f675cccf --- /dev/null +++ b/aoc_wim/aoc2024/q13.py @@ -0,0 +1,24 @@ +""" +--- Day 13: Claw Contraption --- +https://adventofcode.com/2024/day/13 +""" +from aocd import data +import parse + +template = """\ +Button A: X+{:d}, Y+{:d} +Button B: X+{:d}, Y+{:d} +Prize: X={:d}, Y={:d}""" +ab = [0, 0] +for game in parse.findall(template, data): + ax, ay, bx, by, X, Y = game.fixed + denom = ax * by - bx * ay + for o in 0, 10**13: + pa, ra = divmod(by * (X + o) - bx * (Y + o), denom) + pb, rb = divmod(ax * (Y + o) - ay * (X + o), denom) + if not ra and not rb: + ab[o > 0] += 3 * pa + pb +a, b = ab + +print("answer_a:", a) +print("answer_b:", b) diff --git a/tests/2024/13/0.txt b/tests/2024/13/0.txt new file mode 100644 index 00000000..f400b1b4 --- /dev/null +++ b/tests/2024/13/0.txt @@ -0,0 +1,17 @@ +Button A: X+94, Y+34 +Button B: X+22, Y+67 +Prize: X=8400, Y=5400 + +Button A: X+26, Y+66 +Button B: X+67, Y+21 +Prize: X=12748, Y=12176 + +Button A: X+17, Y+86 +Button B: X+84, Y+37 +Prize: X=7870, Y=6450 + +Button A: X+69, Y+23 +Button B: X+27, Y+71 +Prize: X=18641, Y=10279 +480 +- From e9732aafd2a13442760203d619dd23bb22b272e7 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Sat, 14 Dec 2024 15:25:33 -0600 Subject: [PATCH 21/23] day 14 --- aoc_wim/aoc2024/q14.py | 61 ++++++++++++++++++++++++++++++++++++++++++ tests/2024/14/0.txt | 14 ++++++++++ 2 files changed, 75 insertions(+) create mode 100644 aoc_wim/aoc2024/q14.py create mode 100644 tests/2024/14/0.txt diff --git a/aoc_wim/aoc2024/q14.py b/aoc_wim/aoc2024/q14.py new file mode 100644 index 00000000..0cde64f1 --- /dev/null +++ b/aoc_wim/aoc2024/q14.py @@ -0,0 +1,61 @@ +""" +--- Day 14: Restroom Redoubt --- +https://adventofcode.com/2024/day/14 +""" +from aocd import data +from aocd import extra +from collections import Counter +from dataclasses import dataclass +import math +import parse +import statistics + + +w = extra.get("w", 101) +h = extra.get("h", 103) + + +@dataclass +class Bot: + x: int + y: int + dx: int + dy: int + + @classmethod + def fromline(cls, line): + [parsed] = parse.findall("p={:d},{:d} v={:d},{:d}", line) + return cls(*parsed.fixed) + + def move(self, t=1): + self.x = (self.x + self.dx * t) % w + self.y = (self.y + self.dy * t) % h + + @property + def quadrant(self): + if self.x == w//2 or self.y == h//2: + # "Robots that are exactly in the middle (horizontally or vertically) don't + # count as being in any quadrant" + return + return self.x < w//2, self.y < h//2 + + +bots = [Bot.fromline(x) for x in data.splitlines()] +for bot in bots: + bot.move(100) +quadrants = [bot.quadrant for bot in bots] +quadrant_counts = Counter(quadrants) +del quadrant_counts[None] +a = math.prod(quadrant_counts.values()) +print("answer_a:", a) + +b = 100 +var0 = statistics.pvariance([bot.x + bot.y for bot in bots]) +if not extra: + while True: + for bot in bots: + bot.move() + b += 1 + if statistics.pvariance([bot.x + bot.y for bot in bots]) < var0/2: + print("answer_b:", b) + break diff --git a/tests/2024/14/0.txt b/tests/2024/14/0.txt new file mode 100644 index 00000000..67f039d9 --- /dev/null +++ b/tests/2024/14/0.txt @@ -0,0 +1,14 @@ +p=0,4 v=3,-3 +p=6,3 v=-1,-3 +p=10,3 v=-1,2 +p=2,0 v=2,-1 +p=0,0 v=1,3 +p=3,0 v=-2,-2 +p=7,6 v=-1,-3 +p=3,0 v=-1,-2 +p=9,3 v=2,3 +p=7,3 v=-1,2 +p=2,4 v=2,-3 +p=9,5 v=-3,-3 +12 # w=11,h=7 +- From 957def812cb3354b9463223c603467e49232b957 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 03:06:26 -0600 Subject: [PATCH 22/23] 2024/15 (#127) * day 06 * day 07 * day 08 * day 09 * day 10 * day 11 * day 12 * day 13 * day 14 * day 15 --- aoc_wim/aoc2024/q15.py | 109 +++++++++++++++++++++++++++++++++++++++++ tests/2024/15/0.txt | 12 +++++ tests/2024/15/1.txt | 23 +++++++++ 3 files changed, 144 insertions(+) create mode 100644 aoc_wim/aoc2024/q15.py create mode 100644 tests/2024/15/0.txt create mode 100644 tests/2024/15/1.txt diff --git a/aoc_wim/aoc2024/q15.py b/aoc_wim/aoc2024/q15.py new file mode 100644 index 00000000..4532ba71 --- /dev/null +++ b/aoc_wim/aoc2024/q15.py @@ -0,0 +1,109 @@ +""" +--- Day 15: Warehouse Woes --- +https://adventofcode.com/2024/day/15 +""" +from aocd import data +from aoc_wim.zgrid import ZGrid + + +warehouse, path = data.split("\n\n") +grid = ZGrid(warehouse) +dzs = [grid.dzs[x] for x in path.replace("\n", "")] + +z = grid.z("@") +grid[z] = "." +for dz in dzs: + val = grid[z + dz] + if val == "#": + continue + elif val == ".": + z += dz + elif val == "O": + zs = [z + dz] + while grid.get(zs[-1] + dz) == "O": + zs.append(zs[-1] + dz) + if grid.get(zs[-1] + dz) == "#": + continue + zs.append(zs[-1] + dz) + grid[zs[0]] = "." + grid.update(dict.fromkeys(zs[1:], "O")) + z += dz + +a = int(sum(z.real + 100*z.imag for z in grid.z("O", first=False))) +print("answer_a:", a) + + +class Box: + def __init__(self, z): + self.z = z + + def __contains__(self, z): + return z == self.z or z - 1 == self.z + + def can_move(self, dz): + if dz == -1 and grid[self.z + dz] == "#": + return False + if dz == 1 and grid[self.z + 1 + dz] == "#": + return False + if dz in (-1j, 1j) and (grid[self.z + dz] == "#" or grid[self.z + 1 + dz] == "#"): + return False + if dz == -1: + for box in boxes: + if self.z + dz in box: + return box.can_move(dz) + return True + if dz == 1: + for box in boxes: + if self.z + 1 + dz in box: + return box.can_move(dz) + return True + box2 = [x for x in boxes if self.z + dz in x or self.z + 1 + dz in x] + return all(b.can_move(dz) for b in box2) + + def move(self, dz): + if dz == -1: + for box in boxes: + if self.z + dz in box: + box.move(dz) + elif dz == 1: + for box in boxes: + if self.z + 1 + dz in box: + box.move(dz) + else: + for box in boxes: + if self.z + dz in box or self.z + 1 + dz in box: + box.move(dz) + self.z += dz + + +remap = { + "#": "##", + "O": "[]", + ".": "..", + "@": "@.", +} +grid = ZGrid(warehouse.translate(str.maketrans(remap))) +boxes = [] +for z_, g in grid.items(): + if g == "[": + boxes.append(Box(z_)) + grid[z_] = "." + elif g == "]": + grid[z_] = "." + elif g == "@": + z = z_ + grid[z_] = "." + +for dz in dzs: + if grid[z + dz] == "#": + continue + box = [x for x in boxes if z + dz in x] + if box: + [box] = box + if not box.can_move(dz): + continue + box.move(dz) + z += dz + +b = int(sum(box.z.real + 100*box.z.imag for box in boxes)) +print("answer_b:", b) diff --git a/tests/2024/15/0.txt b/tests/2024/15/0.txt new file mode 100644 index 00000000..460074b7 --- /dev/null +++ b/tests/2024/15/0.txt @@ -0,0 +1,12 @@ +######## +#..O.O.# +##@.O..# +#...O..# +#.#.O..# +#...O..# +#......# +######## + +<^^>>>vv>v<< +2028 +- diff --git a/tests/2024/15/1.txt b/tests/2024/15/1.txt new file mode 100644 index 00000000..097c6cf2 --- /dev/null +++ b/tests/2024/15/1.txt @@ -0,0 +1,23 @@ +########## +#..O..O.O# +#......O.# +#.OO..O.O# +#..O@..O.# +#O#..O...# +#O..O..O.# +#.OO.O.OO# +#....O...# +########## + +^v>^vv^v>v<>v^v<<><>>v^v^>^<<<><^ +vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<^<^^>>>^<>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^v^^<^^vv< +<>^^^^>>>v^<>vvv^>^^^vv^^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ +^><^><>>><>^^<<^^v>>><^^>v>>>^v><>^v><<<>vvvv>^<><<>^>< +^>><>^v<><^vvv<^^<><^v<<<><<<^^<^>>^<<<^>>^v^>>^v>vv>^<<^v<>><<><<>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ +<><^^>^^^<>^vv<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> +^^>vv<^v^v^<>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<>< +v^^>>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^ +10092 +9021 From 15b6dcb58477564556d78de772add2ec6d6d7081 Mon Sep 17 00:00:00 2001 From: Wim Jeantine-Glenn Date: Mon, 23 Dec 2024 03:08:31 -0600 Subject: [PATCH 23/23] 2024/16 (#128) * day 16 --- aoc_wim/aoc2024/q16.py | 38 ++++++++++++++++++++++++++++++++++++++ tests/2024/16/0.txt | 17 +++++++++++++++++ tests/2024/16/1.txt | 19 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 aoc_wim/aoc2024/q16.py create mode 100644 tests/2024/16/0.txt create mode 100644 tests/2024/16/1.txt diff --git a/aoc_wim/aoc2024/q16.py b/aoc_wim/aoc2024/q16.py new file mode 100644 index 00000000..a0fd1ba8 --- /dev/null +++ b/aoc_wim/aoc2024/q16.py @@ -0,0 +1,38 @@ +""" +--- Day 16: Reindeer Maze --- +https://adventofcode.com/2024/day/16 +""" +from aocd import data +from aoc_wim.zgrid import ZGrid +from queue import PriorityQueue +import itertools as it + + +grid = ZGrid(data) +q = PriorityQueue() +i = it.count() +S, E = grid.z("S"), grid.z("E") +state = (0, next(i), S, 1, [S]) +q.put(state) +a = None +score = {} +paths = [] +while not q.empty(): + s0, _, z0, dz0, path = q.get() + if s0 > score.get((z0, dz0), float("inf")): + continue + score[z0, dz0] = s0 + if z0 == E: + if a is None: + a = s0 + if a == s0: + paths.append(path) + for z in grid.near(z0): + if grid[z] == "#": + continue + dz = z - z0 + s = s0 + [1001, 1][dz == dz0] + q.put((s, next(i), z, dz, path + [z])) + +print("answer_a:", a) +print("answer_b:", len(set().union(*paths))) diff --git a/tests/2024/16/0.txt b/tests/2024/16/0.txt new file mode 100644 index 00000000..988a105f --- /dev/null +++ b/tests/2024/16/0.txt @@ -0,0 +1,17 @@ +############### +#.......#....E# +#.#.###.#.###.# +#.....#.#...#.# +#.###.#####.#.# +#.#.#.......#.# +#.#.#####.###.# +#...........#.# +###.#.#####.#.# +#...#.....#.#.# +#.#.#.###.#.#.# +#.....#...#.#.# +#.###.#.#.#.#.# +#S..#.....#...# +############### +7036 +45 diff --git a/tests/2024/16/1.txt b/tests/2024/16/1.txt new file mode 100644 index 00000000..0859095c --- /dev/null +++ b/tests/2024/16/1.txt @@ -0,0 +1,19 @@ +################# +#...#...#...#..E# +#.#.#.#.#.#.#.#.# +#.#.#.#...#...#.# +#.#.#.#.###.#.#.# +#...#.#.#.....#.# +#.#.#.#.#.#####.# +#.#...#.#.#.....# +#.#.#####.#.###.# +#.#.#.......#...# +#.#.###.#####.### +#.#.#...#.....#.# +#.#.#.#####.###.# +#.#.#.........#.# +#.#.#.#########.# +#S#.............# +################# +11048 +64