maze.outline()
-pattern = Pattern(config.maze_pattern).centered_for(dims)
+pattern = Pattern(config.maze_pattern).centered_for(
+ dims, {CellCoord(5, 5), CellCoord(10, 10)}
+)
pattern.fill(maze)
walls_const = set(maze.walls_full())
for wall in maze.all_walls():
for tile in wall.tile_coords():
backend.draw_tile(tile)
-for cell in CellCoord(*dims.xy()).all_up_to():
+for cell in CellCoord(dims).all_up_to():
backend.draw_tile(cell.tile_coords())
backend.draw_tile(pixel)
maze.clear_dirty()
backend.present()
- poll_events(2)
+ poll_events(0)
+ # poll_events(0)
def poll_events(timeout_ms: int = -1) -> None:
maze_make_perfect(maze, callback=display_maze)
maze_make_pacman(maze, walls_const, callback=display_maze)
-while True:
+while False:
maze_make_perfect(maze, callback=display_maze)
- poll_events(200)
- # maze_make_pacman(maze, walls_const, callback=display_maze)
- maze_make_empty(maze, walls_const, callback=display_maze)
- poll_events(200)
+ # poll_events(200)
+ maze_make_pacman(maze, walls_const, callback=display_maze)
+ # maze_make_empty(maze, walls_const, callback=display_maze)
+ # poll_events(200)
maze._rebuild()
poll_events()
many,
many_count,
none_of,
+ null_parser,
one_of,
pair,
parser_complete,
+from collections.abc import Iterable
from amazeing.maze_display.backend import IVec2
from .maze import Maze
from .maze_walls import CellCoord
" # ###",
]
- def __init__(self, pat: list[str]) -> None:
- self.cells: set[CellCoord] = {
+ def __init__(self, pat: list[str] | set[CellCoord]) -> None:
+ if isinstance(pat, set):
+ self.__cells: set[CellCoord] = pat
+ return
+ self.__cells: set[CellCoord] = {
CellCoord(x, y)
for y, line in enumerate(pat)
for x, char in enumerate(line)
}
def offset(self, by: IVec2) -> "Pattern":
- pattern: Pattern = Pattern([])
- pattern.cells = {cell.offset(by) for cell in self.cells}
- return pattern
+ return Pattern({CellCoord(cell + by) for cell in self.__cells})
+
+ def flood_filled(self) -> "Pattern":
+ dims = self.dims()
+ border = {CellCoord(-1, -1)}
+ reachable = set()
+ full = {
+ CellCoord(x, y) for x in range(0, dims.x) for y in range(0, dims.y)
+ }
+
+ def coord_propagate(coord: CellCoord) -> Iterable[CellCoord]:
+ return (
+ cell
+ for cell in coord.neighbours_unchecked()
+ if cell not in self.__cells
+ and cell not in reachable
+ and cell not in border
+ and cell.x >= -1
+ and cell.x <= dims.x
+ and cell.y >= -1
+ and cell.y <= dims.y
+ )
+
+ while len(border) != 0:
+ for e in border:
+ reachable.add(e)
+ border.remove(e)
+ for cell in coord_propagate(e):
+ reachable.add(cell)
+ border.add(cell)
+ break
+ return Pattern(full - reachable)
+
+ def add_cell(self, cell: CellCoord) -> None:
+ self.__cells.add(cell)
+
+ def remove_cell(self, cell: CellCoord) -> None:
+ self.__cells.discard(cell)
def dims(self) -> IVec2:
dim_by: Callable[[Callable[[CellCoord], int]], int] = lambda f: (
- max(map(lambda c: f(c) + 1, self.cells), default=0)
- - min(map(f, self.cells), default=0)
+ max(map(lambda c: f(c) + 1, self.__cells), default=0)
+ - min(map(f, self.__cells), default=0)
)
- return IVec2(dim_by(CellCoord.x), dim_by(CellCoord.y))
+ return IVec2(dim_by(lambda e: e.x), dim_by(lambda e: e.y))
def normalized(self) -> "Pattern":
min_by: Callable[[Callable[[CellCoord], int]], int] = lambda f: min(
- map(f, self.cells), default=0
+ map(f, self.__cells), default=0
)
- offset = IVec2(-min_by(CellCoord.x), -min_by(CellCoord.y))
+ offset = IVec2(-min_by(lambda e: e.x), -min_by(lambda e: e.y))
return self.offset(offset)
- def centered_for(self, canvas: IVec2) -> "Pattern":
+ def mirrored(self) -> "Pattern":
+ return Pattern({CellCoord(IVec2.splat(0) - e) for e in self.__cells})
+
+ def centered_for(
+ self, canvas: IVec2, excluding: set[CellCoord] = set()
+ ) -> "Pattern":
normalized: Pattern = self.normalized()
+ negative = normalized.flood_filled().mirrored()
dims = normalized.dims()
- offset = (canvas - dims) // 2
- return normalized.offset(offset)
+ slots = set(CellCoord(canvas - dims + 1).all_up_to())
+ for excluded in excluding:
+ slots -= negative.offset(excluded).__cells
+ if len(slots) == 0:
+ return Pattern([])
+ ideal = (canvas - dims) // 2
+ slot = min(slots, key=lambda e: sum(((e := e - ideal) * e).xy()))
+
+ return normalized.offset(slot)
def fill(self, maze: "Maze") -> None:
- for cell in self.cells:
+ for cell in self.__cells:
for wall in cell.walls():
maze.fill_wall(wall)
from enum import Enum, auto
-from typing import Iterable, Optional, cast
+from typing import Iterable, Optional, cast, overload
from ..maze_display import IVec2
]
-class CellCoord:
- def __init__(self, x: int, y: int) -> None:
- self.__x: int = x
- self.__y: int = y
+class CellCoord(IVec2):
+ @overload
+ def __init__(self, val: IVec2, /) -> None: ...
- def __members(self) -> tuple[int, int]:
- return (self.__x, self.__y)
+ @overload
+ def __init__(self, x: int, y: int, /) -> None: ...
- def __eq__(self, value: object, /) -> bool:
- return (
- self.__members() == cast(CellCoord, value).__members()
- if type(self) is type(value)
- else False
- )
-
- def __hash__(self) -> int:
- return hash(self.__members())
+ def __init__(self, a: IVec2 | int, b: int = 0) -> None:
+ if isinstance(a, int):
+ super().__init__(a, b)
+ else:
+ super().__init__(a.x, a.y)
def walls(self) -> Iterable[WallCoord]:
return map(
def get_wall(self, cardinal: Cardinal) -> WallCoord:
match cardinal:
case Cardinal.NORTH:
- return WallCoord(Orientation.HORIZONTAL, self.__y, self.__x)
+ return WallCoord(Orientation.HORIZONTAL, self.y, self.x)
case Cardinal.SOUTH:
- return WallCoord(
- Orientation.HORIZONTAL, self.__y + 1, self.__x
- )
+ return WallCoord(Orientation.HORIZONTAL, self.y + 1, self.x)
case Cardinal.EAST:
- return WallCoord(Orientation.VERTICAL, self.__x, self.__y)
+ return WallCoord(Orientation.VERTICAL, self.x, self.y)
case Cardinal.WEST:
- return WallCoord(Orientation.VERTICAL, self.__x + 1, self.__y)
+ return WallCoord(Orientation.VERTICAL, self.x + 1, self.y)
def tile_coords(self) -> IVec2:
- return IVec2(self.__x * 2 + 1, self.__y * 2 + 1)
+ return IVec2(self.x * 2 + 1, self.y * 2 + 1)
def offset(self, by: IVec2) -> "CellCoord":
- return CellCoord(self.__x + by.x, self.__y + by.y)
-
- def x(self) -> int:
- return self.__x
-
- def y(self) -> int:
- return self.__y
+ return CellCoord(self + by)
def all_up_to(self) -> Iterable["CellCoord"]:
- for x in range(0, self.__x):
- for y in range(0, self.__y):
+ for x in range(0, self.x):
+ for y in range(0, self.y):
yield CellCoord(x, y)
+
+ def neighbours_unchecked(self) -> Iterable["CellCoord"]:
+ return map(
+ self.offset, [IVec2(-1, 0), IVec2(1, 0), IVec2(0, -1), IVec2(0, 1)]
+ )
return f"{self.x, self.y}"
@staticmethod
- def with_op(
- op: Callable[[T, T], T],
- ) -> Callable[["IVec2[T]", "T | IVec2[T]"], "IVec2[T]"]:
+ def with_op[T2](
+ op: Callable[[T, T], T2],
+ ) -> Callable[["IVec2[T]", "T | IVec2[T]"], "IVec2[T2]"]:
return lambda self, other: IVec2(
op(
self.x,
(
other
- if isinstance(other, type(self))
+ if isinstance(other, IVec2)
else (other := type(self).splat(cast(T, other)))
).x,
),
return False
return self.x == value.x and self.y == value.y
+ def __hash__(self) -> int:
+ return hash((self.x, self.y))
+
def xy(self) -> tuple[T, T]:
return (self.x, self.y)
-WIDTH=25
-HEIGHT=25
+WIDTH=20
+HEIGHT=20
ENTRY=2,5
#EXIT=100,100
OUTPUT_FILE=test
SEED=111
TILEMAP_WALL_SIZE=2,1
TILEMAP_CELL_SIZE=4,2
-TILEMAP_FULL="{1000,100,100:1000,1000,1000}###{1000,1000,1000:1000,1000,1000}###"
-TILEMAP_FULL="{100,1000,100:1000,1000,1000}###{1000,1000,1000:1000,1000,1000}###"
-TILEMAP_FULL="{100,100,1000:1000,1000,1000}###{1000,1000,1000:1000,1000,1000}###"
+TILEMAP_FULL="{1000,1000,1000:1000,1000,1000}######"
+TILEMAP_FULL="{1000,1000,1000:1000,1000,1000}######"
+TILEMAP_FULL="{1000,1000,1000:1000,1000,1000}######"
TILEMAP_EMPTY="{0,0,0:0,0,0} "
TILEMAP_EMPTY="{0,0,0:0,0,0} "
TILEMAP_EMPTY="{0,0,0:0,0,0} "
TILEMAP_BACKGROUND="{1000,1000,1000:0,0,0}###### "
TILEMAP_BACKGROUND="{1000,1000,1000:0,0,0} ## "
TILEMAP_BACKGROUND="{1000,1000,1000:0,0,0}## ## "
-MAZE_PATTERN=" # # "
-MAZE_PATTERN=" # # "
-MAZE_PATTERN=" # "
-MAZE_PATTERN=" "
-MAZE_PATTERN=" # # "
-MAZE_PATTERN=" "
+#MAZE_PATTERN=" # # "
+#MAZE_PATTERN=" # # "
+#MAZE_PATTERN=" # "
+#MAZE_PATTERN=" "
+#MAZE_PATTERN=" # # "
+#MAZE_PATTERN=" "
+#MAZE_PATTERN="# # #"
+#MAZE_PATTERN=" ## ## "
+MAZE_PATTERN="#######"
+MAZE_PATTERN="# # # #"
MAZE_PATTERN="# # #"
-MAZE_PATTERN=" ## ## "
+MAZE_PATTERN="# #"
+MAZE_PATTERN="## ####"
+MAZE_PATTERN="# #"
+MAZE_PATTERN="# # #"
+MAZE_PATTERN="#######"