Skip to content

Commit 395463d

Browse files
committed
Implement 2024 day 20
1 parent fdaadfe commit 395463d

File tree

3 files changed

+108
-0
lines changed

3 files changed

+108
-0
lines changed

2024/src/aoc/days/day20.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import itertools
2+
3+
import numpy
4+
5+
from . import SeparateRunner
6+
7+
DIRECTIONS = [
8+
(-1, 0),
9+
(1, 0),
10+
(0, -1),
11+
(0, 1),
12+
]
13+
14+
CHEATS = [
15+
(-2, 0),
16+
(2, 0),
17+
(0, -2),
18+
(0, 2),
19+
]
20+
21+
22+
def parse_path(input: str) -> dict[tuple[int, int], int]:
23+
grid = numpy.array(list(map(list, input.strip().split("\n"))))
24+
25+
ys, xs = numpy.nonzero(grid == "S")
26+
sx, sy = int(xs[0]), int(ys[0])
27+
28+
nx, ny = sx, sy
29+
30+
path = {
31+
(sx, sy): 0,
32+
}
33+
34+
while grid[ny, nx] != "E":
35+
x, y = nx, ny
36+
37+
for dx, dy in DIRECTIONS:
38+
if grid[y + dy, x + dx] == "#" or (x + dx, y + dy) in path:
39+
continue
40+
nx = x + dx
41+
ny = y + dy
42+
break
43+
44+
path[nx, ny] = len(path)
45+
return path
46+
47+
48+
def get_savings(a: tuple[tuple[int, int], int], b: tuple[tuple[int, int], int]) -> int:
49+
(ax, ay), ad = a
50+
(bx, by), bd = b
51+
52+
dist = abs(bx - ax) + abs(by - ay)
53+
if dist <= 20:
54+
return bd - ad - dist
55+
else:
56+
return 0
57+
58+
59+
class DayRunner(SeparateRunner):
60+
@classmethod
61+
def part1(cls, input: str, limit: int = 100) -> int:
62+
path = parse_path(input)
63+
64+
total = 0
65+
66+
for (px, py), dist in path.items():
67+
for dx, dy in CHEATS:
68+
if (other := path.get((px + dx, py + dy))) is not None:
69+
savings = dist - other - 2
70+
if savings >= limit:
71+
total += 1
72+
73+
return total
74+
75+
@classmethod
76+
def part2(cls, input: str, limit: int = 100) -> int:
77+
path = parse_path(input)
78+
79+
return sum(
80+
get_savings(a, b) >= limit
81+
for a, b in itertools.combinations(path.items(), 2)
82+
)

2024/tests/samples/20.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
###############
2+
#...#...#.....#
3+
#.#.#.#.#.###.#
4+
#S#...#.#.#...#
5+
#######.#.#.###
6+
#######.#.#...#
7+
#######.#.###.#
8+
###..E#...#...#
9+
###.#######.###
10+
#...###...#...#
11+
#.#####.#.###.#
12+
#.#...#.#.#...#
13+
#.#.#.#.#.#.###
14+
#...#...#...###
15+
###############

2024/tests/test_day20.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from aoc.days.day20 import DayRunner
2+
3+
from . import get_data
4+
5+
6+
def test_sample_part1() -> None:
7+
assert DayRunner.part1(get_data(20), limit=1) == 44
8+
9+
10+
def test_sample_part2() -> None:
11+
assert DayRunner.part2(get_data(20), limit=50) == 285

0 commit comments

Comments
 (0)