aoc2024/day15.py
2024-12-15 08:55:43 +01:00

116 lines
2.8 KiB
Python

from collections import deque
from enum import Enum
from tools.aoc import AOCDay
from tools.coordinate import Coordinate
from tools.grid import Grid
from typing import Any
MOVES = {
"^": (0, -1),
">": (1, 0),
"v": (0, 1),
"<": (-1, 0),
}
LARGER = {
"#": "##",
"O": "[]",
".": "..",
"@": "@.",
}
class Tile(str, Enum):
EMPTY = "."
WALL = "#"
BOX = "O"
BOX_LEFT = "["
BOX_RIGHT = "]"
def sokoban(grid: Grid, start: Coordinate, moves: str) -> Grid:
for move in moves:
dir_ = MOVES[move]
next_pos = start + dir_
if grid.get(next_pos) == Tile.WALL:
continue
elif grid.get(next_pos) == Tile.EMPTY:
start = next_pos
continue
# neither a wall, nor empty space? There must be a box in the way
to_move = set()
wall_in_the_way = False
queue = deque([next_pos])
while queue:
pos = queue.popleft()
pos_tile = grid.get(pos)
if pos_tile == Tile.EMPTY:
continue
if pos_tile == Tile.WALL:
wall_in_the_way = True
break
if (pos, pos_tile) in to_move:
continue
to_move.add((pos, pos_tile))
queue.append(pos + dir_)
if pos_tile in [Tile.BOX_LEFT, Tile.BOX_RIGHT]:
if pos_tile == Tile.BOX_LEFT:
queue.append(pos + (1, 0))
else:
queue.append(pos + (-1, 0))
if wall_in_the_way:
continue
for box, _ in to_move:
grid.set(box, Tile.EMPTY)
for box, box_tile in to_move:
grid.set(box + dir_, box_tile)
start = next_pos
return grid
class Day(AOCDay):
inputs = [
[
(2028, "input15_test2"),
(10092, "input15_test"),
(1515788, "input15"),
],
[
(9021, "input15_test"),
(1516544, "input15"),
],
]
def parse_input(self, part2: bool = False) -> tuple[Grid, Coordinate, str]:
map_, moves = self.getMultiLineInputAsArray()
if part2:
map_ = ["".join([LARGER[x] for x in y]) for y in map_]
grid = Grid.from_data(map_, default=Tile.EMPTY, translate={r"\.#O\[\]": Tile})
start = list(grid.find("@"))[0]
grid.set(start, ".")
return grid, start, "".join(moves)
def part1(self) -> Any:
grid = sokoban(*self.parse_input())
return sum(100 * c.y + c.x for c in grid.find("O"))
def part2(self) -> Any:
grid = sokoban(*self.parse_input(True))
return sum(100 * c.y + c.x for c in grid.find("["))
if __name__ == "__main__":
day = Day(2024, 15)
day.run(verbose=True)