This commit is contained in:
parent
7447c7b536
commit
8651884ca6
@ -1,5 +1,4 @@
|
|||||||
import itertools
|
from typing import Any, Callable, Final, Iterator, TypeAlias
|
||||||
from typing import Any, Callable, Iterator, TypeAlias
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from numpy.typing import NDArray
|
from numpy.typing import NDArray
|
||||||
@ -7,24 +6,83 @@ from numpy.typing import NDArray
|
|||||||
from ..base import BaseSolver
|
from ..base import BaseSolver
|
||||||
|
|
||||||
Robot: TypeAlias = tuple[int, int]
|
Robot: TypeAlias = tuple[int, int]
|
||||||
Grid: TypeAlias = list[list[str]]
|
Grid: TypeAlias = list[list[int]]
|
||||||
ImageGrid: TypeAlias = NDArray[np.uint8]
|
ImageGrid: TypeAlias = NDArray[np.uint8]
|
||||||
|
|
||||||
|
|
||||||
|
class BoxUtils:
|
||||||
|
FREE: Final = 0
|
||||||
|
BLOCK: Final = 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def convert(grid_s: list[str], large: bool):
|
||||||
|
grid: list[list[int]] = []
|
||||||
|
robot: tuple[int, int] | None = None
|
||||||
|
box_counter = 2 if not large else 3
|
||||||
|
for i_row, row in enumerate(grid_s):
|
||||||
|
row_u: list[int] = []
|
||||||
|
for i_col, col in enumerate(row):
|
||||||
|
if col == "." or col == "@":
|
||||||
|
row_u.extend(
|
||||||
|
(BoxUtils.FREE, BoxUtils.FREE) if large else (BoxUtils.FREE,)
|
||||||
|
)
|
||||||
|
robot = (i_row, i_col)
|
||||||
|
elif col == "#":
|
||||||
|
row_u.extend(
|
||||||
|
(BoxUtils.BLOCK, BoxUtils.BLOCK) if large else (BoxUtils.BLOCK,)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
row_u.extend(
|
||||||
|
(box_counter, -box_counter) if large else (box_counter,)
|
||||||
|
)
|
||||||
|
box_counter += 2
|
||||||
|
grid.append(row_u)
|
||||||
|
assert robot is not None
|
||||||
|
return grid, robot
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_box(c: int):
|
||||||
|
return c >= 2 and c % 2 == 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_open_or_close_box(c: int):
|
||||||
|
return abs(c) >= 2 and c % 2 == 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_open_box(c: int):
|
||||||
|
return c >= 2 and c % 2 == 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_close_box(c: int):
|
||||||
|
return c < 0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_char(c: int):
|
||||||
|
if c == BoxUtils.FREE:
|
||||||
|
return "."
|
||||||
|
elif c == BoxUtils.BLOCK:
|
||||||
|
return "#"
|
||||||
|
elif BoxUtils.is_box(c):
|
||||||
|
return "O"
|
||||||
|
elif BoxUtils.is_open_box(c):
|
||||||
|
return "["
|
||||||
|
else:
|
||||||
|
return "]"
|
||||||
|
|
||||||
|
|
||||||
class Solver(BaseSolver):
|
class Solver(BaseSolver):
|
||||||
def print_grid(self, name: str, grid: Grid, robot: Robot):
|
def print_grid(self, name: str, grid: Grid, robot: Robot):
|
||||||
if self.files:
|
if self.files:
|
||||||
grid = [[c for c in row] for row in grid]
|
grid_s = [[BoxUtils.to_char(c) for c in row] for row in grid]
|
||||||
grid[robot[0]][robot[1]] = "\033[31;1m@\033[00m"
|
grid_s[robot[0]][robot[1]] = "\033[31;1m@\033[00m"
|
||||||
self.files.create(
|
self.files.create(
|
||||||
name, "\n".join("".join(row) for row in grid).encode(), True
|
name, "\n".join("".join(row) for row in grid_s).encode(), True
|
||||||
)
|
)
|
||||||
|
|
||||||
def convert_grid(self, grid: Grid, robot: Robot) -> ImageGrid:
|
def convert_grid(self, grid: Grid, robot: Robot) -> ImageGrid:
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
grid = [[c for c in row] for row in grid]
|
# grid[robot[0]][robot[1]] = "@"
|
||||||
grid[robot[0]][robot[1]] = "@"
|
|
||||||
# return np.array(
|
# return np.array(
|
||||||
# [
|
# [
|
||||||
# [
|
# [
|
||||||
@ -43,7 +101,7 @@ class Solver(BaseSolver):
|
|||||||
# ],
|
# ],
|
||||||
# dtype=np.uint8,
|
# dtype=np.uint8,
|
||||||
# )
|
# )
|
||||||
return np.array(grid)
|
return np.abs(np.array(grid))
|
||||||
|
|
||||||
def step_part1(self, grid: Grid, move: str, robot: Robot):
|
def step_part1(self, grid: Grid, move: str, robot: Robot):
|
||||||
match move:
|
match move:
|
||||||
@ -59,20 +117,20 @@ class Solver(BaseSolver):
|
|||||||
assert False
|
assert False
|
||||||
|
|
||||||
row, col = robot
|
row, col = robot
|
||||||
if grid[row + d_row][col + d_col] == ".":
|
if grid[row + d_row][col + d_col] == BoxUtils.FREE:
|
||||||
robot = (row + d_row, col + d_col)
|
robot = (row + d_row, col + d_col)
|
||||||
elif grid[row + d_row][col + d_col] != "#":
|
elif grid[row + d_row][col + d_col] != BoxUtils.BLOCK:
|
||||||
assert grid[row + d_row][col + d_col] == "O"
|
|
||||||
|
|
||||||
n = 1
|
n = 1
|
||||||
while grid[row + n * d_row][col + n * d_col] == "O":
|
while BoxUtils.is_box(grid[row + n * d_row][col + n * d_col]):
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
if grid[row + n * d_row][col + n * d_col] == ".":
|
if grid[row + n * d_row][col + n * d_col] == BoxUtils.FREE:
|
||||||
robot = (row + d_row, col + d_col)
|
robot = (row + d_row, col + d_col)
|
||||||
grid[row + d_row][col + d_col] = "."
|
|
||||||
for k in range(2, n + 1):
|
for k in range(2, n + 1):
|
||||||
grid[row + k * d_row][col + k * d_col] = "O"
|
grid[row + k * d_row][col + k * d_col] = grid[
|
||||||
|
row + (k - 1) * d_row
|
||||||
|
][col + (k - 1) * d_col]
|
||||||
|
grid[row + d_row][col + d_col] = BoxUtils.FREE
|
||||||
|
|
||||||
return grid, robot
|
return grid, robot
|
||||||
|
|
||||||
@ -90,53 +148,47 @@ class Solver(BaseSolver):
|
|||||||
assert False
|
assert False
|
||||||
|
|
||||||
row, col = robot
|
row, col = robot
|
||||||
if grid[row + d_row][col + d_col] == ".":
|
if grid[row + d_row][col + d_col] == BoxUtils.FREE:
|
||||||
robot = (row + d_row, col + d_col)
|
robot = (row + d_row, col + d_col)
|
||||||
elif grid[row + d_row][col + d_col] == "#":
|
elif grid[row + d_row][col + d_col] == BoxUtils.BLOCK:
|
||||||
...
|
...
|
||||||
elif move in "<>":
|
elif move in "<>":
|
||||||
assert d_row == 0
|
|
||||||
assert grid[row][col + d_col] in "[]"
|
|
||||||
|
|
||||||
n = 1
|
n = 1
|
||||||
while grid[row][col + n * d_col] in "[]":
|
while BoxUtils.is_open_or_close_box(grid[row][col + n * d_col]):
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
if grid[row][col + n * d_col] == ".":
|
if grid[row][col + n * d_col] == BoxUtils.FREE:
|
||||||
robot = (row, col + d_col)
|
robot = (row, col + d_col)
|
||||||
for k in range(n, 1, -1):
|
for k in range(n, 1, -1):
|
||||||
grid[row][col + k * d_col] = grid[row][col + (k - 1) * d_col]
|
grid[row][col + k * d_col] = grid[row][col + (k - 1) * d_col]
|
||||||
grid[row + d_row][col + d_col] = "."
|
grid[row + d_row][col + d_col] = BoxUtils.FREE
|
||||||
|
|
||||||
elif move in "^v":
|
elif move in "^v":
|
||||||
assert d_col == 0
|
|
||||||
assert grid[row + d_row][col + d_col] in "[]"
|
|
||||||
|
|
||||||
n = 1
|
n = 1
|
||||||
boxes: list[set[int]] = [{col}]
|
boxes: list[set[int]] = [{col}]
|
||||||
while True:
|
while True:
|
||||||
to_move = boxes[-1]
|
to_move = boxes[-1]
|
||||||
if any(grid[row + n * d_row][c] == "#" for c in to_move):
|
if any(grid[row + n * d_row][c] == BoxUtils.BLOCK for c in to_move):
|
||||||
break
|
break
|
||||||
if all(grid[row + n * d_row][c] == "." for c in to_move):
|
if all(grid[row + n * d_row][c] == BoxUtils.FREE for c in to_move):
|
||||||
break
|
break
|
||||||
|
|
||||||
as_move: set[int] = set()
|
as_move: set[int] = set()
|
||||||
|
|
||||||
for c in to_move:
|
for c in to_move:
|
||||||
if grid[row + n * d_row][c] == "]":
|
if BoxUtils.is_close_box(grid[row + n * d_row][c]):
|
||||||
as_move.update({c - 1, c})
|
as_move.update({c - 1, c})
|
||||||
elif grid[row + n * d_row][c] == "[":
|
elif BoxUtils.is_open_box(grid[row + n * d_row][c]):
|
||||||
as_move.update({c, c + 1})
|
as_move.update({c, c + 1})
|
||||||
|
|
||||||
boxes.append(as_move)
|
boxes.append(as_move)
|
||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
if all(grid[row + n * d_row][c] == "." for c in boxes[-1]):
|
if all(grid[row + n * d_row][c] == BoxUtils.FREE for c in boxes[-1]):
|
||||||
for k, to_move in zip(range(n, 1, -1), boxes[-1:0:-1], strict=True):
|
for k, to_move in zip(range(n, 1, -1), boxes[-1:0:-1], strict=True):
|
||||||
for c in to_move:
|
for c in to_move:
|
||||||
grid[row + k * d_row][c] = grid[row + (k - 1) * d_row][c]
|
grid[row + k * d_row][c] = grid[row + (k - 1) * d_row][c]
|
||||||
grid[row + (k - 1) * d_row][c] = "."
|
grid[row + (k - 1) * d_row][c] = BoxUtils.FREE
|
||||||
robot = (row + d_row, col + d_col)
|
robot = (row + d_row, col + d_col)
|
||||||
|
|
||||||
return grid, robot
|
return grid, robot
|
||||||
@ -145,6 +197,7 @@ class Solver(BaseSolver):
|
|||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
grid: Grid,
|
grid: Grid,
|
||||||
|
robot: Robot,
|
||||||
moves: str,
|
moves: str,
|
||||||
fn: Callable[
|
fn: Callable[
|
||||||
[Grid, str, Robot],
|
[Grid, str, Robot],
|
||||||
@ -152,13 +205,6 @@ class Solver(BaseSolver):
|
|||||||
],
|
],
|
||||||
generate: bool,
|
generate: bool,
|
||||||
) -> tuple[Grid, list[ImageGrid]]:
|
) -> tuple[Grid, list[ImageGrid]]:
|
||||||
# find robot
|
|
||||||
n_rows, n_cols = len(grid), len(grid[0])
|
|
||||||
robot = next(
|
|
||||||
(i, j) for i in range(n_rows) for j in range(n_cols) if grid[i][j] == "@"
|
|
||||||
)
|
|
||||||
grid[robot[0]][robot[1]] = "."
|
|
||||||
|
|
||||||
# initialize
|
# initialize
|
||||||
images: list[ImageGrid] = []
|
images: list[ImageGrid] = []
|
||||||
|
|
||||||
@ -184,35 +230,37 @@ class Solver(BaseSolver):
|
|||||||
|
|
||||||
grid, images = self.run(
|
grid, images = self.run(
|
||||||
"part1",
|
"part1",
|
||||||
[[c for c in r] for r in grid_s.splitlines()],
|
*BoxUtils.convert(grid_s.splitlines(), False),
|
||||||
moves,
|
moves,
|
||||||
self.step_part1,
|
self.step_part1,
|
||||||
self.files is not None,
|
self.files is not None,
|
||||||
)
|
)
|
||||||
|
if self.files:
|
||||||
|
print(images[0].max())
|
||||||
|
self.files.image(
|
||||||
|
"anim_part1.gif", np.stack(images, axis=0).astype(np.uint8)
|
||||||
|
)
|
||||||
yield sum(
|
yield sum(
|
||||||
100 * i_row + i_col
|
100 * i_row + i_col
|
||||||
for i_row, row in enumerate(grid)
|
for i_row, row in enumerate(grid)
|
||||||
for i_col, col in enumerate(row)
|
for i_col, col in enumerate(row)
|
||||||
if col == "O"
|
if BoxUtils.is_box(col)
|
||||||
)
|
)
|
||||||
|
|
||||||
grid, images = self.run(
|
grid, images = self.run(
|
||||||
"part2",
|
"part2",
|
||||||
[
|
*BoxUtils.convert(grid_s.splitlines(), True),
|
||||||
list(
|
|
||||||
itertools.chain.from_iterable(
|
|
||||||
{"O": "[]", "@": "@.", "#": "##", ".": ".."}[c] for c in r
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for r in grid_s.splitlines()
|
|
||||||
],
|
|
||||||
moves,
|
moves,
|
||||||
self.step_part2,
|
self.step_part2,
|
||||||
self.files is not None,
|
self.files is not None,
|
||||||
)
|
)
|
||||||
|
if self.files:
|
||||||
|
self.files.image(
|
||||||
|
"anim_part2.gif", np.stack(images, axis=0).astype(np.uint8)
|
||||||
|
)
|
||||||
yield sum(
|
yield sum(
|
||||||
100 * i_row + i_col
|
100 * i_row + i_col
|
||||||
for i_row, row in enumerate(grid)
|
for i_row, row in enumerate(grid)
|
||||||
for i_col, col in enumerate(row)
|
for i_col, col in enumerate(row)
|
||||||
if col == "["
|
if BoxUtils.is_open_box(col)
|
||||||
)
|
)
|
||||||
|
@ -25,11 +25,8 @@ class FileHandler:
|
|||||||
|
|
||||||
import imageio.v3 as iio
|
import imageio.v3 as iio
|
||||||
|
|
||||||
io = BytesIO()
|
data = iio.imwrite("<bytes>", image, extension=Path(filename).suffix) # type: ignore
|
||||||
iio.imwrite(io, image, extension=Path(filename).suffix) # type: ignore
|
self.create(filename, data, False)
|
||||||
io.seek(0)
|
|
||||||
|
|
||||||
self.create(filename, io.read(), False)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseSolver:
|
class BaseSolver:
|
||||||
|
Loading…
Reference in New Issue
Block a user