from heapq import heappush, heappop from tools.aoc import AOCDay from tools.coordinate import Coordinate, DistanceAlgorithm from tools.grid import Grid from typing import Any def get_dijkstra_path(height_map: Grid, start: Coordinate, end: Coordinate) -> int | None: f_costs = [] openNodes = {} closedNodes = {} openNodes[start] = (0, start.getDistanceTo(end, algorithm=DistanceAlgorithm.MANHATTAN), 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 targetDist = neighbour.getDistanceTo(end, algorithm=DistanceAlgorithm.MANHATTAN) f_cost = targetDist + 1 + currentNode[1] if neighbour not in openNodes or f_cost < openNodes[neighbour][0]: openNodes[neighbour] = (f_cost, currentNode[1] + 1, currentCoord) heappush(f_costs, (f_cost, neighbour)) if end not in closedNodes: return None else: steps = 0 currentNode = closedNodes[end] while currentNode[2]: steps += 1 currentNode = closedNodes[currentNode[2]] return steps class Day(AOCDay): inputs = [ [ (31, "input12_test"), (468, "input12_dennis"), (423, "input12"), ], [ (29, "input12_test"), (459, "input12_dennis"), (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 get_dijkstra_path(*self.get_map()) 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 steps_from_here + 1 < min_steps: min_steps = steps_from_here + 1 return min_steps - 1 if __name__ == '__main__': day = Day(2022, 12) day.run(verbose=True)