| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- #!/usr/bin/env python3
- depth = 6084
- tx, ty = target = 14, 709
- def scan(pad):
- def erode(geo_index):
- return (geo_index + depth) % 20183
- w = tx + pad + 1
- h = ty + pad + 1
- grid = [erode(x * 16807) for x in range(w)]
- for y in range(1, h):
- grid.append(erode(y * 48271))
- for x in range(1, w):
- if x == tx and y == ty:
- idx = 0
- else:
- top = grid[(y - 1) * w + x]
- left = grid[y * w + x - 1]
- idx = top * left
- grid.append(erode(idx))
- for i in range(len(grid)):
- grid[i] %= 3
- return grid, w
- def shortest_path(graph, source, target):
- Q = set(i for i, nb in enumerate(graph) if nb)
- inf = 1 << 32
- dist = len(graph) * [inf]
- dist[source] = 0
- while Q:
- u = min(Q, key=dist.__getitem__)
- if u == target:
- return dist[u]
- Q.remove(u)
- for v, weight in graph[u]:
- if v in Q:
- alt = dist[u] + weight
- if alt < dist[v]:
- dist[v] = alt
- def rescue(grid, w):
- # approach:
- # - build graph with (x, y, tool) tuples as vertices
- # - record weighted edges including tolao witching between vertices where
- # switching is allowed
- # - do Dijkstra on the result
- h = len(grid) // w
- def neighbours(i):
- y, x = divmod(i, w)
- if y > 0: yield i - w
- if x > 0: yield i - 1
- if x < w - 1: yield i + 1
- if y < h - 1: yield i + w
- TORCH, GEAR, NEITHER = range(3)
- tools = [
- (GEAR, TORCH), # rocky
- (GEAR, NEITHER), # wet
- (TORCH, NEITHER), # narrow
- ]
- # for efficiency, graph is encoded as 3 concatenated lists of (y * w + x)
- # vertices, one for each tool
- off = len(grid)
- graph = [[] for i in range(3 * off)]
- for i, sty in enumerate(grid):
- for j in neighbours(i):
- tty = grid[j]
- for stool in tools[sty]:
- for ttool in tools[tty]:
- if ttool in tools[sty]:
- cost = 1 if ttool == stool else 8
- graph[i + off * stool].append((j + off * ttool, cost))
- # we start and end with a torch so in the first of 3 lists
- return shortest_path(graph, 0, ty * w + tx)
- grid, w = scan(15)
- # part 1
- print(sum(sum(grid[y * w:y * w + tx + 1]) for y in range(ty + 1)))
- # part 2
- print(rescue(grid, w))
|