浏览代码

Solve day 22

Taddeus Kroes 7 年之前
父节点
当前提交
784ae18336
共有 1 个文件被更改,包括 89 次插入0 次删除
  1. 89 0
      22_cave.py

+ 89 - 0
22_cave.py

@@ -0,0 +1,89 @@
+#!/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))