aoc2024/day16.py

106 lines
2.9 KiB
Python

from collections import deque
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_best_score(grid: Grid) -> tuple[int, set[Coordinate]]:
target = Coordinate(grid.maxX - 1, 1)
queue = [(0, Coordinate(1, grid.maxY - 1), Coordinate(1, 0), {Coordinate(1, grid.maxY - 1)})]
visited = set()
while queue:
score, pos, facing, path = heappop(queue)
if pos == target:
return score, path
if pos in visited:
continue
visited.add(pos)
for n in grid.getNeighboursOf(pos, includeDiagonal=False):
if grid.get(n) or n in visited:
continue
add_turn = 1001 if n - pos != facing else 1
heappush(queue, (score + add_turn, n, n - pos, path | {n}))
def get_tiles_from_all_paths(grid: Grid) -> int:
target_score, target_path = get_best_score(grid)
target = Coordinate(grid.maxX - 1, 1)
path_scores = {}
score = 0
facing = Coordinate(1, 0)
pos = Coordinate(1, grid.maxY - 1)
visited = set()
while pos != target:
visited.add(pos)
for n in pos.getNeighbours(includeDiagonal=False):
if n not in target_path or n in visited:
continue
if n - pos != facing:
score += 1000
facing = n - pos
path_scores[pos] = score
pos = n
score += 1
queue = [(0, Coordinate(1, grid.maxY - 1), Coordinate(1, 0), {Coordinate(1, grid.maxY - 1)})]
visited = {}
while queue:
score, pos, facing, path = heappop(queue)
if pos in visited and score > visited[pos] + 1000:
continue
if pos in path_scores and pos in visited:
if score <= path_scores[pos]:
target_path |= path
continue
visited[pos] = score
if score >= target_score:
continue
for n in grid.getNeighboursOf(pos, includeDiagonal=False):
if grid.get(n) or n in path:
continue
add_turn = 1001 if n - pos != facing else 1
heappush(queue, (score + add_turn, n, n - pos, path | {n}))
return len(target_path)
class Day(AOCDay):
inputs = [
[
(7036, "input16_test"),
(11048, "input16_test2"),
(93436, "input16"),
],
[
(45, "input16_test"),
(64, "input16_test2"),
(486, "input16"),
],
]
def parse_input(self) -> Grid:
return Grid().from_data(self.getInput(), default=None, translate={r"[\.SE]": False, "#": True})
def part1(self) -> Any:
return get_best_score(self.parse_input())[0]
def part2(self) -> Any:
return get_tiles_from_all_paths(self.parse_input())
if __name__ == "__main__":
day = Day(2024, 16)
day.run(verbose=True)