-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathday22.py
77 lines (57 loc) · 2.34 KB
/
day22.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
""" algorithm from korylprince """
import networkx as nx
from typing import Tuple
def generate_grid(depth: int, target: Tuple[int, int]) -> dict:
# (x, y) -> geo, erosion, risk
grid = {}
tx, ty = target
for y in range(ty + 1):
for x in range(tx + 1):
if (x, y) in [(0, 0), target]:
geo = 0
elif x == 0:
geo = y * 48271
elif y == 0:
geo = x * 16807
else:
geo = grid[(x - 1, y)][1] * grid[(x, y - 1)][1]
erosion = (geo + depth) % 20183
risk = erosion % 3
grid[(x, y)] = (geo, erosion, risk)
return grid
def part1(depth: int, target: Tuple[int, int]) -> int:
grid = generate_grid(depth, target)
return sum(risk for _, _, risk in grid.values())
def dijkstra(grid: dict, corner: Tuple[int, int], target: Tuple[int, int]):
rocky, wet, narrow = 0, 1, 2
torch, gear, neither = 0, 1, 2
valid_items = {rocky: (torch, gear), wet: (gear, neither), narrow: (torch, neither)}
graph = nx.Graph()
max_x, max_y = corner
target_x, target_y = target
for y in range(max_y + 1):
for x in range(max_x + 1):
items = valid_items[grid[(x, y)]]
graph.add_edge((x, y, items[0]), (x, y, items[1]), weight=7)
for dx, dy in ((0, 1), (0, -1), (1, 0), (-1, 0)):
new_x, new_y = x + dx, y + dy
if 0 <= new_x <= max_x and 0 <= new_y <= max_y:
new_items = valid_items[grid[(new_x, new_y)]]
for item in set(items).intersection(set(new_items)):
graph.add_edge((x, y, item), (new_x, new_y, item), weight=1)
return nx.dijkstra_path_length(graph, (0, 0, torch), (target_x, target_y, torch))
def part2(depth: int, target: Tuple[int, int], extra: int) -> int:
corner = (target[0] + extra, target[1] + extra)
# generate a grid a bit larger than the target square
grid = generate_grid(depth, corner)
# extract only the region types
grid = {c: v[2] for c, v in grid.items()}
#
return dijkstra(grid, corner, target)
def main():
# depth, target = 510, (10, 10)
depth, target = 10647, (7, 770)
print(f'Part 1: {part1(depth, target)}')
print(f'Part 2: {part2(depth, target, extra=100)}')
if __name__ == "__main__":
main()