generated from public/aoc_template
129 lines
3.3 KiB
Python
129 lines
3.3 KiB
Python
from collections import deque
|
|
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": "[]",
|
|
".": "..",
|
|
"@": "@.",
|
|
}
|
|
|
|
|
|
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) == ".":
|
|
start += dir_
|
|
continue
|
|
elif grid.get(next_pos) == "#":
|
|
continue
|
|
elif grid.get(next_pos) == "O":
|
|
pos_behind_box = next_pos + dir_
|
|
while grid.get(pos_behind_box) == "O":
|
|
pos_behind_box += dir_
|
|
|
|
if grid.get(pos_behind_box) == "#":
|
|
continue
|
|
|
|
grid.set(pos_behind_box, "O")
|
|
start = next_pos
|
|
grid.set(start, ".")
|
|
continue
|
|
|
|
if move in ["<", ">"]:
|
|
# easy mode
|
|
pos_behind_box = next_pos + dir_
|
|
while grid.get(pos_behind_box) in ["[", "]"]:
|
|
pos_behind_box += dir_ * 2
|
|
|
|
if grid.get(pos_behind_box) == "#":
|
|
continue
|
|
|
|
while pos_behind_box != start:
|
|
grid.set(pos_behind_box, grid.get(pos_behind_box - dir_))
|
|
pos_behind_box -= dir_
|
|
else:
|
|
# recursive mode
|
|
wall_in_the_way = False
|
|
Q = deque([next_pos])
|
|
boxes = set()
|
|
while Q:
|
|
pos = Q.popleft()
|
|
if grid.get(pos) == "#":
|
|
wall_in_the_way = True
|
|
break
|
|
elif grid.get(pos) == ".":
|
|
continue
|
|
|
|
Q.append(pos + dir_)
|
|
if grid.get(pos) == "[":
|
|
boxes.add((pos, pos + (1, 0)))
|
|
Q.append(pos + dir_ + (1, 0))
|
|
else:
|
|
boxes.add((pos + (-1, 0), pos))
|
|
Q.append(pos + dir_ + (-1, 0))
|
|
|
|
if wall_in_the_way:
|
|
continue
|
|
|
|
for box_l, box_r in boxes:
|
|
grid.set(box_l, ".")
|
|
grid.set(box_r, ".")
|
|
|
|
for box_l, box_r in boxes:
|
|
grid.set(box_l + dir_, "[")
|
|
grid.set(box_r + dir_, "]")
|
|
|
|
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=".")
|
|
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)
|