Skip to content

Commit 3f337d5

Browse files
committed
Day 21: Step Counter (non-general solution)
1 parent 6b029e1 commit 3f337d5

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ Development occurs in language-specific directories:
2525
|[Day18.hs](hs/src/Day18.hs)|[Day18.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day18.kt)|[day18.py](py/aoc2023/day18.py)|[day18.rs](rs/src/day18.rs)|
2626
|[Day19.hs](hs/src/Day19.hs)|[Day19.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day19.kt)|[day19.py](py/aoc2023/day19.py)|[day19.rs](rs/src/day19.rs)|
2727
|[Day20.hs](hs/src/Day20.hs)|[Day20.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day20.kt)|[day20.py](py/aoc2023/day20.py)|[day20.rs](rs/src/day20.rs)|
28-
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day21.kt)|||
28+
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day21.kt)|[day21.py](py/aoc2023/day21.py)||
2929
|[Day22.hs](hs/src/Day22.hs)|[Day22.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day22.kt)|||

py/aoc2023/day21.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
"""
2+
Day 21: Step Counter
3+
"""
4+
5+
SAMPLE_INPUT = """
6+
...........
7+
.....###.#.
8+
.###.##..#.
9+
..#.#...#..
10+
....#.#....
11+
.##..S####.
12+
.##..#...#.
13+
.......##..
14+
.##.#.####.
15+
.##..##.##.
16+
...........
17+
"""
18+
19+
20+
def _count(grid, start, n):
21+
frontier, visited, acc = {start}, set(), 0
22+
for d in range(n):
23+
if not (d ^ n) & 1:
24+
acc += len(frontier)
25+
visited |= frontier
26+
frontier = {
27+
(y1, x1)
28+
for y0, x0 in frontier
29+
for y1, x1 in [(y0 - 1, x0), (y0, x0 - 1), (y0, x0 + 1), (y0 + 1, x0)]
30+
if 0 <= y1 < len(grid)
31+
and 0 <= x1 < len(grid[y1])
32+
and grid[y1][x1] != "#"
33+
and (y1, x1) not in visited
34+
}
35+
return acc + len(frontier)
36+
37+
38+
def part1(data, n=64):
39+
"""
40+
>>> part1(SAMPLE_INPUT, n=1)
41+
2
42+
>>> part1(SAMPLE_INPUT, n=2)
43+
4
44+
>>> part1(SAMPLE_INPUT, n=3)
45+
6
46+
>>> part1(SAMPLE_INPUT, n=6)
47+
16
48+
"""
49+
grid = [line for line in data.splitlines() if line]
50+
(start,) = (
51+
(y, x) for y, line in enumerate(grid) for x, c in enumerate(line) if c == "S"
52+
)
53+
return _count(grid, start, n)
54+
55+
56+
def part2(data, n=26501365):
57+
"""
58+
>>> part2(SAMPLE_INPUT, n=6) # doctest: +SKIP
59+
16
60+
>>> part2(SAMPLE_INPUT, n=10) # doctest: +SKIP
61+
50
62+
>>> part2(SAMPLE_INPUT, n=50) # doctest: +SKIP
63+
1594
64+
>>> part2(SAMPLE_INPUT, n=100) # doctest: +SKIP
65+
6536
66+
>>> part2(SAMPLE_INPUT, n=500) # doctest: +SKIP
67+
167004
68+
>>> part2(SAMPLE_INPUT, n=1000) # doctest: +SKIP
69+
668697
70+
>>> part2(SAMPLE_INPUT, n=5000) # doctest: +SKIP
71+
16733044
72+
"""
73+
grid = [line for line in data.splitlines() if line]
74+
m = len(grid)
75+
q, r = n // m, n % m
76+
((y0, x0),) = (
77+
(y, x) for y, line in enumerate(grid) for x, c in enumerate(line) if c == "S"
78+
)
79+
a, b, c, d = (
80+
_count(
81+
[line * (2 * i + 1) for line in grid] * (2 * i + 1),
82+
(y0 + i * m, x0 + i * m),
83+
r + i * m,
84+
)
85+
for i in range(4)
86+
)
87+
assert d == a - 3 * b + 3 * c
88+
return a + (b - a) * q + (c - 2 * b + a) * (q * (q - 1) // 2)
89+
90+
91+
parts = (part1, part2)

py/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ day17 = "aoc2023.day17:parts"
4242
day18 = "aoc2023.day18:parts"
4343
day19 = "aoc2023.day19:parts"
4444
day20 = "aoc2023.day20:parts"
45+
day21 = "aoc2023.day21:parts"
4546

4647
[tool.black]
4748
target_version = ["py312"]

0 commit comments

Comments
 (0)