from heapq import heappush, heappop from tools.aoc import AOCDay from tools.coordinate import Coordinate from tools.grid import Grid from typing import Any def get_dijkstra_path(height_map: Grid, start: Coordinate, end: Coordinate) -> list | None: f_costs = [] openNodes = {} closedNodes = {} openNodes[start] = (0, start.getDistanceTo(end), None) heappush(f_costs, (0, start)) while f_costs: _, currentCoord = heappop(f_costs) if currentCoord not in openNodes: continue currentNode = openNodes[currentCoord] closedNodes[currentCoord] = currentNode del openNodes[currentCoord] if currentCoord == end: break for neighbour in height_map.getNeighboursOf(currentCoord, includeDiagonal=False): if neighbour in closedNodes or height_map.get(neighbour) - 1 > height_map.get(currentCoord): continue neighbourDist = 1 targetDist = neighbour.getDistanceTo(end) f_cost = targetDist + neighbourDist + currentNode[1] if neighbour not in openNodes or f_cost < openNodes[neighbour][0]: openNodes[neighbour] = (f_cost, currentNode[1] + neighbourDist, currentCoord) heappush(f_costs, (f_cost, neighbour)) if end not in closedNodes: return None else: currentNode = closedNodes[end] pathCoords = [end] while currentNode[2]: pathCoords.append(currentNode[2]) currentNode = closedNodes[currentNode[2]] return pathCoords class Day(AOCDay): inputs = [ [ (31, "input12_test"), (423, "input12"), ], [ (29, "input12_test"), (416, "input12"), ] ] def get_map(self) -> (Grid, Coordinate, Coordinate): grid = Grid(30) start = None end = None for y, line in enumerate(self.getInput()): for x, char in enumerate(line): if char == 'S': start = Coordinate(x, y) grid.set(start, 0) elif char == 'E': end = Coordinate(x, y) grid.set(end, 25) else: grid.set(Coordinate(x, y), ord(char) - ord('a')) return grid, start, end def part1(self) -> Any: return len(get_dijkstra_path(*self.get_map())) - 1 def part2(self) -> Any: min_steps = float("inf") height_map, start, end = self.get_map() for c in height_map.getActiveCells(): if height_map.get(c) == 0: steps_from_here = get_dijkstra_path(height_map, c, end) if steps_from_here is not None and len(steps_from_here) < min_steps: min_steps = len(steps_from_here) return min_steps - 1 if __name__ == '__main__': day = Day(2022, 12) day.run(verbose=True)