## ~noelle/aoc-2021

5831f64748d61ecd2b3d4a67720250c5ce83a76f — Noelle Leigh 2 years ago
```15_1
```
```1 files changed, 61 insertions(+), 55 deletions(-)

M 15/puzzle_1.py
```
`M 15/puzzle_1.py => 15/puzzle_1.py +61 -55`
```@@ 4,68 4,76 @@ Solution for AoC 2021 15 Puzzle 1
cat input.txt | python puzzle_1.py
"""
import sys
+from dataclasses import dataclass, field
from typing import NamedTuple

+Coords = tuple[int, int]
+
+@dataclass
+class Node:
+    x: int
+    y: int
+    risk: int
+    risk_from_start: int = sys.maxsize
+    neighbors: set[Coords] = field(default_factory=set)
+
+Graph = dict[Coords, Node]
+
+def make_graph(cave_map: list[list[int]]) -> Graph:
+    # Create disconnected set of nodes
+    nodes: Graph = {}
+    for row_index, row in enumerate(cave_map):
+        for col_index, risk in enumerate(row):
+            nodes[(row_index, col_index)] = Node(x=col_index, y=row_index, risk=risk)
+
+    # Connect nodes
+    for key, node in nodes.items():
+        row_index, col_index = key
+        neighbor_keys = [
+            # North
+            (row_index - 1, col_index),
+            # East
+            (row_index, col_index + 1),
+            # South
+            (row_index + 1, col_index),
+            # West
+            (row_index, col_index - 1),
+        ]
+        for neighbor_key in neighbor_keys:
+            if neighbor_key in nodes:
+
+    return nodes
+

class Point(NamedTuple):
x: int
y: int

-def dijkstra(cave_map: list[list[int]], start: Point, end: Point) -> tuple[list[Point], int]:
+def dijkstra(graph: Graph, start: Coords, end: Coords) -> int:
"""
Return the most risk-averse path from start to end on a map along with its cost.

-    Ref: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Algorithm
+    Refs:
+    - https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm#Algorithm
+    - https://www.udacity.com/blog/2021/10/implementing-dijkstras-algorithm-in-python.html
"""
-    map_height = len(cave_map)
-    map_width = len(cave_map[0])
+    unvisited_coords = set(graph)
+    graph[start].risk = 0
+    graph[start].risk_from_start = 0

-    unvisited_set = set(
-        [Point(x, y) for y in range(map_height) for x in range(map_width)]
-    )
-    unvisited_set.remove(start)
-
-    distances = dict(map(lambda point: (point, sys.maxsize), unvisited_set))
-    distances[start] = 0
-
-    current = start
-
-    # Get neighbor's distances
-    # N
-    if current.y > 0:
-        neighbor = Point(current.x, current.y - 1)
-        existing_distance = distances[neighbor]
-        new_distance = cave_map[neighbor.y][neighbor.x]
-        distances[neighbor] = (
-            new_distance if new_distance > existing_distance else existing_distance
-        )
-    # E
-    if current.x < (map_width - 1):
-        neighbor = Point(current.x + 1, current.y)
-        existing_distance = distances[neighbor]
-        new_distance = cave_map[neighbor.y][neighbor.x]
-        distances[neighbor] = (
-            new_distance if new_distance > existing_distance else existing_distance
-        )
-    # S
-    if current.y < (map_height - 1):
-        neighbor = Point(current.x, current.y + 1)
-        existing_distance = distances[neighbor]
-        new_distance = cave_map[neighbor.y][neighbor.x]
-        distances[neighbor] = (
-            new_distance if new_distance > existing_distance else existing_distance
-        )
-    # W
-    if current.x > 0:
-        neighbor = Point(current.x - 1, current.y)
-        existing_distance = distances[neighbor]
-        new_distance = cave_map[neighbor.y][neighbor.x]
-        distances[neighbor] = (
-            new_distance if new_distance > existing_distance else existing_distance
-        )
+    while unvisited_coords:
+        current_coords = min(unvisited_coords, key=lambda coords: graph[coords].risk_from_start)
+        current_node = graph[current_coords]
+        for neighbor_coords in current_node.neighbors & unvisited_coords:
+            new_risk_from_start = graph[neighbor_coords].risk + graph[current_coords].risk_from_start
+            if new_risk_from_start < graph[neighbor_coords].risk_from_start:
+                graph[neighbor_coords].risk_from_start = new_risk_from_start
+        unvisited_coords.remove(current_coords)

-    return [], 0
+    return graph[end].risk_from_start

if __name__ == "__main__":

@@ 80,10 88,8 @@ if __name__ == "__main__":
sys.stdin,
)
)
-    map_height = len(cave_map)
-    map_width = len(cave_map[0])
-    print(map_width, "x", map_height)
-    start = Point(0, 0)
-    end = Point(map_width - 1, map_height - 1)
-    path, risk = dijkstra(cave_map, start, end)
-    sys.stdout.write(str(risk))
+    graph = make_graph(cave_map)
+    start = min(graph, key=lambda key: sum(key))
+    end = max(graph, key=lambda key: sum(key))
+    total_risk = dijkstra(graph, start, end)
+    sys.stdout.write(str(total_risk))

```