dirty_tracker = MazeDirtyTracker(maze)
pacman_tracker = MazePacmanTracker(maze)
-
-maze.outline()
-
-excluded = set()
-if config.entry is not None:
- excluded.add(CellCoord(config.entry))
-if config.exit is not None:
- excluded.add(CellCoord(config.exit))
-
-pattern = Pattern(config.maze_pattern).centered_for(dims, excluded)
-pattern.fill(maze)
-
-walls_const = set(maze.walls_full())
+network_tracker = MazeNetworkTracker(maze)
backend = TTYBackend(dims, config.tilemap_wall_size, config.tilemap_cell_size)
pair_map = extract_pairs(config)
tilemaps = TileMaps(config, pair_map, backend)
filler = TileCycle(tilemaps.filler, backend.set_filler)
-
empty = TileCycle(tilemaps.empty, backend.map_style_cb())
-backend.set_style(empty.curr_style())
-for wall in maze.all_walls():
- for tile in wall.tile_coords():
- backend.draw_tile(tile)
-for cell in CellCoord(dims).all_up_to():
- backend.draw_tile(cell.tile_coords())
+backend.set_bg_init(lambda _: empty.curr_style())
full = TileCycle(tilemaps.full, backend.map_style_cb())
continue
-prev_solution: list[Cardinal] = []
-
-
-def manhattan_distance(a: IVec2, b: IVec2) -> int:
- return sum(map(abs, (a - b).xy()))
-
-
-def elipse_manhattan(a: IVec2, b: IVec2, a2: IVec2, b2: IVec2) -> int:
- return min(
- manhattan_distance(a1, a2) + manhattan_distance(b1, b2)
- for a1, b1 in ((a, b), (b, a))
- )
-
-
-# def pathfind_necessary() -> bool:
-# entry = config.entry
-# exit = config.exit
-# if entry is None or exit is None:
-# return False
-# if len(prev_solution) == 0:
-# return True
-# if any(
-# map(
-# lambda e: e in dirty_tracker.curr_dirty() and maze.get_wall(e),
-# Cardinal.path_to_walls(prev_solution, CellCoord(entry)),
-# )
-# ):
-# return True
-# if any(
-# map(
-# lambda e: elipse_manhattan(entry, exit, *e.neighbour_cells())
-# < len(prev_solution),
-# filter(
-# lambda wall: not maze.get_wall(wall),
-# dirty_tracker.curr_dirty(),
-# ),
-# )
-# ):
-# return True
-# return False
-#
-#
-# def pathfind() -> None:
-# if not pathfind_necessary():
-# return
-# if config.entry is None or config.exit is None:
-# return
-# solution = maze.pathfind(CellCoord(config.entry), CellCoord(config.exit))
-# if solution is None or prev_solution == solution:
-# return
-# prev_tiles = Cardinal.path_to_tiles(prev_solution, CellCoord(config.entry))
-# tiles = Cardinal.path_to_tiles(solution, CellCoord(config.entry))
-# backend.set_style(empty.curr_style())
-# for tile in prev_tiles:
-# backend.draw_tile(tile)
-# backend.set_style(path.curr_style())
-# for tile in tiles:
-# backend.draw_tile(tile)
-# backend.present()
-# prev_solution.clear()
-# prev_solution.extend(solution)
+excluded = set()
+if config.entry is not None:
+ excluded.add(CellCoord(config.entry))
+if config.exit is not None:
+ excluded.add(CellCoord(config.exit))
+
+pattern = Pattern(config.maze_pattern).centered_for(dims, excluded)
+pattern.fill(maze)
+
+maze.outline()
+
+walls_const = set(maze.walls_full())
-network_tracker = MazeNetworkTracker(maze)
maze_make_perfect(maze, network_tracker, callback=display_maze)
maze_make_pacman(maze, walls_const, pacman_tracker, callback=display_maze)
-from collections.abc import Iterable
+from collections.abc import Iterable, Generator, Callable
from amazeing.maze_display.backend import IVec2
from .maze import Maze
from .maze_coords import CellCoord
-from typing import Callable
class Pattern:
normalized: Pattern = self.normalized()
negative = normalized.flood_filled().mirrored()
dims = normalized.dims()
- slots = set(
- map(
- lambda e: CellCoord(e + IVec2.splat(1)),
- CellCoord(canvas - dims - IVec2.splat(1)).all_up_to(),
- )
- )
- for excluded in excluding:
- slots -= negative.offset(excluded).__cells
- if len(slots) == 0:
- return Pattern([])
- ideal = (canvas - dims) // IVec2.splat(2)
- slot = min(
- slots, key=lambda c: int.__add__(*((e := c - ideal) * e).xy())
- )
- return normalized.offset(slot)
+ def middle_range_iter(start: int, end: int) -> Generator[int]:
+ r1 = range(end // 2, start - 1, -1)
+ r2 = range(end // 2 + 1, end)
+ for a, b in zip(r1, r2):
+ yield a
+ yield b
+ for e in r1:
+ yield e
+ for e in r2:
+ yield e
+
+ blacklist = set()
+ for excluded in excluding:
+ blacklist |= negative.offset(excluded).__cells
+ slots = canvas - dims
+ for x in middle_range_iter(1, slots.x):
+ for y in middle_range_iter(1, slots.y):
+ pos = CellCoord(x, y)
+ if pos not in blacklist:
+ return normalized.offset(pos)
+ return Pattern([])
def fill(self, maze: "Maze") -> None:
for cell in self.__cells:
self.__filler: None | int = None
self.__style_bimap: BiMap[int, IVec2] = BiMap()
+ self.__style_rename_bimap: BiMap[int, int] = BiMap()
+ self.__bg_init: Callable[[IVec2], int] | None = None
def __del__(self) -> None:
curses.curs_set(1)
self.__tilemap.dst_coord_rev(end_excl - IVec2.splat(1))
+ IVec2.splat(1),
)
- self.__drawn += QuadTree.rectangle(drawn_rect)
- pass
+ drawn_tree = QuadTree.rectangle(drawn_rect)
+ redrawn = drawn_tree - self.__drawn
+ for tile in redrawn.tiles():
+ if self.__style_bimap.revcontains(tile):
+ style = self.__style_bimap.revget(tile)
+ if self.__style_rename_bimap.revcontains(style):
+ style = self.__style_rename_bimap.revget(style)
+ self.set_style(style)
+ self.draw_tile(tile)
+ elif self.__bg_init is not None:
+ self.set_style(self.__bg_init(tile))
+ self.draw_tile(tile)
+ self.__drawn += drawn_tree
def set_filler(self, style: int) -> None:
if self.__filler == style:
for box in self.__filler_boxes:
box.mark_dirty()
- def get_styled(self, style: int) -> Iterable[IVec2]:
+ def set_bg_init(self, bg_init: Callable[[IVec2], int]) -> None:
+ self.__bg_init = bg_init
+
+ def get_styled(self, style: int) -> set[IVec2]:
return self.__style_bimap.get(style)
def map_style_cb(self) -> Callable[[int], None]:
curr = new
if curr == new:
return
- self.set_style(new)
- for tile in self.get_styled(curr):
- self.draw_tile(tile)
+ if len(self.get_styled(curr)) != 0:
+ self.__drawn = QuadTree()
+ self.__style_rename_bimap.add(new, curr)
curr = new
return inner
if len(self.__map[key]) == 0:
self.__map.pop(key)
- def get(self, key: K) -> Iterable[R]:
- return list(self.__map[key] if self.contains(key) else [])
+ def get(self, key: K) -> set[R]:
+ return self.__map[key] if self.contains(key) else set()
def revget(self, revkey: R) -> K:
return self.__revmap[revkey]
-from collections.abc import Callable
+from collections.abc import Callable, Generator, Iterable
from typing import cast
from amazeing.maze_display.backend import IVec2
from functools import partial
def __sub__(self, other: "Tree") -> "Tree":
return self.shared_layer_apply(Tree.node_sub, other)
+ def tiles(self) -> Generator[IVec2]:
+ return Tree.node_tiles(self.__root, IVec2.splat(0), self.__height)
+
+ @staticmethod
+ def node_tiles(
+ node: MaybeNode, pos: IVec2, height: int
+ ) -> Generator[IVec2]:
+ if height == 0 and node == True:
+ yield pos
+ if height == 0 or node == False:
+ return
+ for pos, node in zip4(
+ Tree.node_starts(pos, height), Tree.node_split(node)
+ ):
+ for pos in Tree.node_tiles(node, pos, height - 1):
+ yield pos
+
@staticmethod
def rectangle(rect: Rect) -> "Tree":
res = Tree()
-WIDTH=100
-HEIGHT=100
+WIDTH=1000
+HEIGHT=1000
ENTRY=0,0
EXIT=24,24
OUTPUT_FILE=test