101 lines
3.1 KiB
Python
101 lines
3.1 KiB
Python
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"),
|
|
(412, "input12_madEnrico"),
|
|
(423, "input12"),
|
|
],
|
|
[
|
|
(29, "input12_test"),
|
|
(459, "input12_dennis"),
|
|
(402, "input12_madEnrico"),
|
|
(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)
|