aoc2022/day12.py
2022-12-14 19:35:58 +01:00

121 lines
3.3 KiB
Python

from collections import deque
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
def get_bfs_path(height_map: Grid, end: Coordinate) -> int:
v = {end: None}
q = deque()
q.append(end)
while q:
cur = q.popleft()
cur_v = height_map.get(cur)
if cur_v == 0:
end = cur
break
for c in height_map.getNeighboursOf(cur, includeDiagonal=False):
if c in v or height_map.get(c) + 1 < cur_v:
continue
v[c] = cur
q.append(c)
steps = 0
while end in v:
steps += 1
end = v[end]
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:
height_map, _, end = self.get_map()
return get_bfs_path(height_map, end) - 1
if __name__ == '__main__':
day = Day(2022, 12)
day.run(verbose=True)