-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday_20.py
113 lines (93 loc) · 3.24 KB
/
day_20.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from collections import Counter, defaultdict, deque
from itertools import product
import aoc_helper
from aoc_helper import (
Grid,
PrioQueue,
SparseGrid,
decode_text,
extract_ints,
extract_iranges,
extract_ranges,
extract_uints,
frange,
irange,
iter,
list,
map,
multirange,
range,
search,
tail_call,
)
raw = aoc_helper.fetch(20, 2024)
def parse_raw(raw: str):
grid = Grid.from_string(raw, lambda x: {".":0,"#":1,"S":2,"E":3}[x])
start = list(grid.find_all(SparseGrid.from_string("2", default_factory=int)))[0]
end = list(grid.find_all(SparseGrid.from_string("3", default_factory=int)))[0]
grid[start[1]][start[0]] = 0
grid[end[1]][end[0]] = 0
return (start, end, grid)
data = parse_raw(raw)
# providing this default is somewhat of a hack - there isn't any other way to
# force type inference to happen, AFAIK - but this won't work with standard
# collections (list, set, dict, tuple)
def part_one(data=data):
start, end, grid = data
distances = {}
qu = deque([(0, start)])
while qu:
d, (x,y) = qu.popleft()
# if distances[(x,y)] < d: assert False, "There exist multiple paths, Eric lied"
if (x,y) in distances: continue
distances[(x,y)] = d
for (nx, ny), v in grid.orthogonal_neighbours(x,y):
if v != 1 and ((nx, ny) not in distances):
qu.append((d+1, (nx,ny)))
diamond2 = {
(0,2), (1,1), (2,0),
(0,-2), (1,-1), (-2,0),
(-1,-1), (-1,1),
}
c = 0
for (x,y) in distances:
cur = distances[(x,y)]
for dx, dy in diamond2:
nx, ny = x + dx, y + dy
if (nx, ny) in distances:
nei = distances[(nx,ny)]
gain = (nei - cur - 2)
if gain >= 100:
c += 1
return c
aoc_helper.lazy_test(day=20, year=2024, parse=parse_raw, solution=part_one)
# providing this default is somewhat of a hack - there isn't any other way to
# force type inference to happen, AFAIK - but this won't work with standard
# collections (list, set, dict, tuple)
def part_two(data=data):
start, end, grid = data
distances = {}
qu = deque([(0, start)])
while qu:
d, (x,y) = qu.popleft()
# if distances[(x,y)] < d: assert False, "There exist multiple paths, Eric lied"
if (x,y) in distances: continue
distances[(x,y)] = d
for (nx, ny), v in grid.orthogonal_neighbours(x,y):
if v != 1 and ((nx, ny) not in distances):
qu.append((d+1, (nx,ny)))
good_cheat = set()
for (x,y) in distances:
cur = distances[(x,y)]
for dx, dy in product(range(-20, 21),repeat=2):
if abs(dx) + abs(dy) > 20: continue
nx, ny = x + dx, y + dy
if (nx, ny) in distances:
nei = distances[(nx,ny)]
gain = (nei - cur - (abs(dx) + abs(dy)))
if gain >= 100:
good_cheat.add((cur, (nx,ny)))
return len(good_cheat)
aoc_helper.lazy_test(day=20, year=2024, parse=parse_raw, solution=part_two)
aoc_helper.lazy_submit(day=20, year=2024, solution=part_one, data=data)
aoc_helper.lazy_submit(day=20, year=2024, solution=part_two, data=data)