diff --git a/src/holt59/aoc/2024/day16.py b/src/holt59/aoc/2024/day16.py index 59603ea..686bb37 100644 --- a/src/holt59/aoc/2024/day16.py +++ b/src/holt59/aoc/2024/day16.py @@ -1,7 +1,7 @@ +import heapq +from collections import defaultdict from typing import Any, Iterator, TypeAlias -import networkx as nx - from ..base import BaseSolver Position: TypeAlias = tuple[int, int] @@ -18,41 +18,45 @@ class Solver(BaseSolver): target = next( (i, j) for i in range(n_rows) for j in range(n_cols) if grid[i][j] == "E" ) - facing = (0, 1) - G = nx.DiGraph() + queue: list[tuple[int, Position, Direction, tuple[Position, ...]]] = [ + (0, rein, (0, 1), (rein,)) + ] - for i in range(1, n_rows - 1): - for j in range(1, n_cols - 1): - if grid[i][j] == "#": - continue + max_score = n_rows * n_cols * 1000 + target_score: int = max_score + scores: dict[tuple[Position, Direction], int] = defaultdict(lambda: max_score) + visited: set[Position] = set() - for di, dj in ((-1, 0), (0, 1), (1, 0), (0, -1)): - if grid[i + di][j + dj] != "#": - G.add_edge( - ((i, j), (di, dj)), ((i + di, j + dj), (di, dj)), weight=1 - ) + while queue: + score, pos, dir, path = heapq.heappop(queue) - if di == 0: - G.add_edge(((i, j), (di, dj)), ((i, j), (1, 0)), weight=1000) - G.add_edge(((i, j), (di, dj)), ((i, j), (-1, 0)), weight=1000) - else: - G.add_edge(((i, j), (di, dj)), ((i, j), (0, 1)), weight=1000) - G.add_edge(((i, j), (di, dj)), ((i, j), (0, -1)), weight=1000) + if target_score < score: + break - for di, dj in ((-1, 0), (0, 1), (1, 0), (0, -1)): - G.add_edge((target, (di, dj)), (target, (-1, -1)), weight=0) + if pos == target: + target_score = score + visited |= set(path) + continue - yield nx.shortest_path_length( - G, - source=(rein, facing), - target=(target, (-1, -1)), - weight="weight", - ) + scores[pos, dir] = score - nodes: set[tuple[int, int]] = set() - for path in nx.all_shortest_paths( - G, source=(rein, facing), target=(target, (-1, -1)), weight="weight" - ): - nodes.update(position for position, _ in path) - yield len(nodes) + row, col = pos + d_row, d_col = dir + + for cost, n_pos, n_dir in ( + (1, (row + d_row, col + d_col), dir), + (1000, pos, (1, 0) if d_row == 0 else (0, 1)), + (1000, pos, (-1, 0) if d_row == 0 else (0, -1)), + ): + n_row, n_col = n_pos + score_n = score + cost + if grid[n_row][n_col] != "#" and score_n < scores[n_pos, n_dir]: + heapq.heappush( + queue, + (score_n, n_pos, n_dir, path + (n_pos,)), + ) + + assert target_score is not None + yield target_score + yield len(visited)