diff --git a/day15.py b/day15.py index 924c414..d1092f2 100644 --- a/day15.py +++ b/day15.py @@ -1,4 +1,5 @@ from collections import deque +from enum import Enum from tools.aoc import AOCDay from tools.coordinate import Coordinate from tools.grid import Grid @@ -20,71 +21,57 @@ LARGER = { } +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) == ".": - start += dir_ + if grid.get(next_pos) == Tile.WALL: 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") + elif grid.get(next_pos) == Tile.EMPTY: 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) == "#": + # 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 - 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 + if pos_tile == Tile.WALL: + wall_in_the_way = True + break - Q.append(pos + dir_) - if grid.get(pos) == "[": - boxes.add((pos, pos + (1, 0))) - Q.append(pos + dir_ + (1, 0)) + 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: - boxes.add((pos + (-1, 0), pos)) - Q.append(pos + dir_ + (-1, 0)) + queue.append(pos + (-1, 0)) - if wall_in_the_way: - continue + if wall_in_the_way: + continue - for box_l, box_r in boxes: - grid.set(box_l, ".") - grid.set(box_r, ".") + for box, _ in to_move: + grid.set(box, Tile.EMPTY) - for box_l, box_r in boxes: - grid.set(box_l + dir_, "[") - grid.set(box_r + dir_, "]") + for box, box_tile in to_move: + grid.set(box + dir_, box_tile) start = next_pos @@ -108,7 +95,7 @@ class Day(AOCDay): map_, moves = self.getMultiLineInputAsArray() if part2: map_ = ["".join([LARGER[x] for x in y]) for y in map_] - grid = Grid.from_data(map_, default=".") + grid = Grid.from_data(map_, default=Tile.EMPTY, translate={r"\.#O\[\]": Tile}) start = list(grid.find("@"))[0] grid.set(start, ".")