aoc2023/day23.py
2023-12-23 10:29:55 +01:00

101 lines
2.8 KiB
Python

from __future__ import annotations
from collections import defaultdict
from tools.aoc import AOCDay
from tools.coordinate import Coordinate
from tools.grid import Grid
from typing import Any
ICE = {
'>': Coordinate(1, 0),
'v': Coordinate(0, 1)
}
class GraphNode(Coordinate):
def __init__(self, x: int, y: int, z: int = None):
self.children = {}
def build_graph(grid: Grid, part2: bool = False) -> GraphNode:
root = GraphNode(1, 0)
crossings = {}
end = Coordinate(grid.maxX, grid.maxY)
def traverse_grid(node: GraphNode, next_coord: Coordinate):
count = 1
n = set(grid.getNeighboursOf(next_coord, includeDefault=False, includeDiagonal=False))
last_coord = node
while len(n - {last_coord}) == 1:
count += 1
last_coord, next_coord = next_coord, (n - {last_coord}).pop()
n = set(grid.getNeighboursOf(next_coord, includeDefault=False, includeDiagonal=False))
if len(n) == 1: # dead end
if next_coord == end:
node.children[GraphNode(*next_coord)] = count
else: # crossing
if next_coord in crossings:
if part2:
crossings[next_coord].children[node] = count
node.children[crossings[next_coord]] = count
else:
this_node = GraphNode(*next_coord)
crossings[next_coord] = this_node
node.children[this_node] = count
for c in n:
if part2 or grid.get(c) not in ICE or ICE[grid.get(c)] == c - next_coord:
traverse_grid(this_node, c)
traverse_grid(root, Coordinate(1, 1))
return root
def get_longest_path(root: GraphNode, looking_for: GraphNode) -> int:
D = defaultdict(int)
v = set()
def calc_dist(node: GraphNode, dist: int):
if node in v:
return
v.add(node)
if D[node] < dist:
D[node] = dist
for c_node, c_dist in node.children.items():
calc_dist(c_node, dist + c_dist)
v.remove(node)
calc_dist(root, 0)
return D[looking_for]
class Day(AOCDay):
inputs = [
[
(94, "input23_test"),
(2326, "input23"),
],
[
(154, "input23_test"),
(6574, "input23"),
]
]
def parse_input(self, part2: bool = False) -> (GraphNode, Coordinate):
grid = Grid.from_data(self.getInput(), translate={'#': False})
root = build_graph(grid, part2=part2)
return root, Coordinate(grid.maxX, grid.maxY)
def part1(self) -> Any:
return get_longest_path(*self.parse_input())
def part2(self) -> Any:
return get_longest_path(*self.parse_input(part2=True))
if __name__ == '__main__':
day = Day(2023, 23)
day.run(verbose=True)