~noelle/aoc-2021

5831f64748d61ecd2b3d4a67720250c5ce83a76f — Noelle Leigh 1 year, 11 months ago 6075cf0
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:
                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))