From c853105b4bd4f4cdc2b5fa3a3cd7b0a18ca20080 Mon Sep 17 00:00:00 2001 From: Axy Date: Fri, 20 Mar 2026 21:23:39 +0100 Subject: [PATCH] Slightly broken, style remapping doesn't work properly but the rest is fine --- __main__.py | 96 +++++------------------------ amazeing/maze_class/maze_pattern.py | 39 +++++++----- amazeing/maze_display/TTYdisplay.py | 28 +++++++-- amazeing/utils/bi_map.py | 4 +- amazeing/utils/quadtree.py | 19 +++++- example.conf | 4 +- 6 files changed, 80 insertions(+), 110 deletions(-) diff --git a/__main__.py b/__main__.py index 656eb62..836e648 100644 --- a/__main__.py +++ b/__main__.py @@ -29,32 +29,14 @@ maze = Maze(dims) 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()) @@ -128,70 +110,20 @@ def poll_events(timeout_ms: int = -1) -> None: 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) diff --git a/amazeing/maze_class/maze_pattern.py b/amazeing/maze_class/maze_pattern.py index efb401a..62cd09f 100644 --- a/amazeing/maze_class/maze_pattern.py +++ b/amazeing/maze_class/maze_pattern.py @@ -1,8 +1,7 @@ -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: @@ -93,22 +92,28 @@ 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: diff --git a/amazeing/maze_display/TTYdisplay.py b/amazeing/maze_display/TTYdisplay.py index d1ede80..162e746 100644 --- a/amazeing/maze_display/TTYdisplay.py +++ b/amazeing/maze_display/TTYdisplay.py @@ -470,6 +470,8 @@ class TTYBackend: 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) @@ -485,8 +487,19 @@ class TTYBackend: 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: @@ -495,7 +508,10 @@ class TTYBackend: 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]: @@ -507,9 +523,9 @@ class TTYBackend: 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 diff --git a/amazeing/utils/bi_map.py b/amazeing/utils/bi_map.py index 5cfaf76..25ca372 100644 --- a/amazeing/utils/bi_map.py +++ b/amazeing/utils/bi_map.py @@ -25,8 +25,8 @@ class BiMap[K, R]: 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] diff --git a/amazeing/utils/quadtree.py b/amazeing/utils/quadtree.py index 47e06fd..222e11b 100644 --- a/amazeing/utils/quadtree.py +++ b/amazeing/utils/quadtree.py @@ -1,4 +1,4 @@ -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 @@ -106,6 +106,23 @@ class Tree: 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() diff --git a/example.conf b/example.conf index a0f7394..e023c67 100644 --- a/example.conf +++ b/example.conf @@ -1,5 +1,5 @@ -WIDTH=100 -HEIGHT=100 +WIDTH=1000 +HEIGHT=1000 ENTRY=0,0 EXIT=24,24 OUTPUT_FILE=test -- 2.53.0