@@ 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:
+ node.neighbors.add(neighbor_key)
+ nodes[neighbor_key].neighbors.add(key)
+
+ 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))