From 4afc887dbfbd532301173d08a5ac3d5818d08de9 Mon Sep 17 00:00:00 2001 From: Axy Date: Tue, 10 Mar 2026 20:04:51 +0100 Subject: [PATCH] pathfinding --- __main__.py | 47 ++++++++++++++++++++--- amazeing/config/config_parser.py | 5 +++ amazeing/maze_class/maze_walls.py | 15 ++++++-- amazeing/maze_display/TTYdisplay.py | 59 +++++++++++++++++++++++++---- example.conf | 12 ++++-- tmp | 0 6 files changed, 117 insertions(+), 21 deletions(-) create mode 100644 tmp diff --git a/__main__.py b/__main__.py index 1830248..0a8ab19 100644 --- a/__main__.py +++ b/__main__.py @@ -10,7 +10,7 @@ from amazeing import ( import random from amazeing.config.config_parser import Config -from amazeing.maze_class.maze_walls import CellCoord +from amazeing.maze_class.maze_walls import Cardinal, CellCoord from amazeing.maze_display.TTYdisplay import TileCycle, TileMaps, extract_pairs from amazeing.maze_display.backend import CloseRequested, IVec2 @@ -41,16 +41,21 @@ pair_map = extract_pairs(config) tilemaps = TileMaps(config, pair_map, backend) filler = TileCycle(tilemaps.filler, backend.set_filler) -backend.set_style(tilemaps.empty[0]) +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()) +full = TileCycle(tilemaps.full, backend.map_style_cb()) + +path = TileCycle(tilemaps.path, backend.map_style_cb()) + def clear_backend() -> None: - backend.set_style(tilemaps.empty[0]) + backend.set_style(empty.curr_style()) for wall in maze.walls_dirty(): if maze.get_wall(wall).is_full(): continue @@ -60,7 +65,8 @@ def clear_backend() -> None: def display_maze(maze: Maze) -> None: clear_backend() - backend.set_style(tilemaps.full[0]) + pathfind() + backend.set_style(full.curr_style()) rewrites = { wall for wall in maze.walls_dirty() if maze.get_wall(wall).is_full() @@ -95,15 +101,44 @@ def poll_events(timeout_ms: int = -1) -> None: exit(0) if event.sym == "c": filler.cycle() + full.cycle() + path.cycle() + empty.cycle() else: continue backend.present() +prev_path: list[IVec2] = [] + + +def pathfind() -> None: + if config.entry is None or config.exit is None: + return + solution = maze.pathfind(CellCoord(config.entry), CellCoord(config.exit)) + if solution is None: + return + tiles = Cardinal.path_to_tiles(solution, CellCoord(config.entry)) + if prev_path == tiles: + return + backend.set_style(empty.curr_style()) + for tile in prev_path: + backend.draw_tile(tile) + prev_path.clear() + prev_path.extend(tiles) + backend.set_style(path.curr_style()) + for tile in tiles: + backend.draw_tile(tile) + backend.present() + + maze_make_perfect(maze, callback=display_maze) maze_make_pacman(maze, walls_const, callback=display_maze) -if config.entry is not None and config.exit is not None: - path = maze.pathfind(CellCoord(config.entry), CellCoord(config.exit)) + + +pathfind() + + while False: maze_make_perfect(maze, callback=display_maze) # poll_events(200) diff --git a/amazeing/config/config_parser.py b/amazeing/config/config_parser.py index 167699e..e9b7041 100644 --- a/amazeing/config/config_parser.py +++ b/amazeing/config/config_parser.py @@ -380,6 +380,7 @@ class Config: tilemap_cell_size: IVec2 tilemap_full: list[list[ColoredLine]] tilemap_empty: list[list[ColoredLine]] + tilemap_path: list[list[ColoredLine]] tilemap_background_size: IVec2 tilemap_background: list[list[ColoredLine]] maze_pattern: list[str] @@ -418,6 +419,10 @@ class Config: ColoredLineField, ['"{BLACK:BLACK} "', '"{BLACK:BLACK} "'], ), + "TILEMAP_PATH": DefaultedStrField( + ColoredLineField, + ['"{BLUE:BLUE} "', '"{BLUE:BLUE} "'], + ), "TILEMAP_BACKGROUND_SIZE": DefaultedField( CoordField, IVec2(4, 2) ), diff --git a/amazeing/maze_class/maze_walls.py b/amazeing/maze_class/maze_walls.py index ff63362..9baf9a9 100644 --- a/amazeing/maze_class/maze_walls.py +++ b/amazeing/maze_class/maze_walls.py @@ -80,6 +80,16 @@ class Cardinal(Enum): def all() -> list["Cardinal"]: return [Cardinal.NORTH, Cardinal.SOUTH, Cardinal.EAST, Cardinal.WEST] + @staticmethod + def path_to_tiles(path: list["Cardinal"], src: "CellCoord") -> list[IVec2]: + res = [src.tile_coords()] + for card in path: + nxt = src.get_neighbour(card) + res.append((src.tile_coords() + nxt.tile_coords()) // 2) + res.append(nxt.tile_coords()) + src = nxt + return res + class WallCoord: def __init__(self, orientation: Orientation, a: int, b: int) -> None: @@ -154,10 +164,7 @@ class CellCoord(IVec2): super().__init__(a.x, a.y) def walls(self) -> Iterable[WallCoord]: - return map( - self.get_wall, - [Cardinal.NORTH, Cardinal.SOUTH, Cardinal.EAST, Cardinal.WEST], - ) + return map(self.get_wall, Cardinal.all()) def get_wall(self, cardinal: Cardinal) -> WallCoord: match cardinal: diff --git a/amazeing/maze_display/TTYdisplay.py b/amazeing/maze_display/TTYdisplay.py index a79bcdc..e84af68 100644 --- a/amazeing/maze_display/TTYdisplay.py +++ b/amazeing/maze_display/TTYdisplay.py @@ -209,6 +209,7 @@ def extract_pairs( for tilemaps in ( config.tilemap_empty, config.tilemap_full, + config.tilemap_path, config.tilemap_background, ) for e in tilemaps @@ -289,6 +290,7 @@ class TileMaps: self.empty: list[int] = list(map(add_style, config.tilemap_empty)) self.full: list[int] = list(map(add_style, config.tilemap_full)) + self.path: list[int] = list(map(add_style, config.tilemap_path)) self.filler: list[int] = list( map( lambda e: add_style(e, config.tilemap_background_size), @@ -297,19 +299,23 @@ class TileMaps: ) -class TileCycle: - def __init__[T]( - self, styles: list[T], cb: Callable[[T], None], i=0 - ) -> None: +class TileCycle[T]: + def __init__(self, styles: list[T], cb: Callable[[T], None], i=0) -> None: + if len(styles) == 0: + raise BackendException("No styles provided in tilecycle") self.__styles = styles self.__cb = cb self.__i = i cb(styles[i]) def cycle(self, by: int = 1): - self.__i += by - self.__i %= len(self.__styles) - self.__cb(self.__styles[self.__i]) + new = (self.__i + by) % len(self.__styles) + if new != self.__i: + self.__cb(self.__styles[new]) + self.__i = new + + def curr_style(self) -> T: + return self.__styles[self.__i] class TTYBackend(Backend[int]): @@ -380,6 +386,9 @@ class TTYBackend(Backend[int]): self.__filler: None | int = None + self.__style_mapping: dict[int, set[IVec2]] = {} + self.__style_revmapping: dict[IVec2, int] = {} + def __del__(self): curses.curs_set(1) curses.nocbreak() @@ -394,6 +403,29 @@ class TTYBackend(Backend[int]): for box in self.__filler_boxes: box.mark_dirty() + def get_styled(self, style: int) -> Iterable[IVec2]: + return set( + self.__style_mapping[style] + if style in self.__style_mapping + else [] + ) + + def map_style_cb(self) -> Callable[[int], None]: + curr: int | None = None + + def inner(new: int) -> None: + nonlocal curr + if curr == None: + curr = new + if curr == new: + return + self.set_style(new) + for tile in self.get_styled(curr): + self.draw_tile(tile) + curr = new + + return inner + def add_style(self, style: Tile) -> int: return self.__tilemap.add_tile(style) @@ -401,7 +433,18 @@ class TTYBackend(Backend[int]): return self.__dims def draw_tile(self, pos: IVec2) -> None: - self.__tilemap.draw_at(pos, self.__style, self.__pad.pad) + style = self.__style + mapping = self.__style_mapping + revmapping = self.__style_revmapping + + if pos in revmapping: + mapping[revmapping[pos]].remove(pos) + revmapping[pos] = style + if style not in mapping: + mapping[style] = set() + mapping[style].add(pos) + + self.__tilemap.draw_at(pos, style, self.__pad.pad) def set_style(self, style: int) -> None: self.__style = style diff --git a/example.conf b/example.conf index 242625a..fbec71b 100644 --- a/example.conf +++ b/example.conf @@ -1,7 +1,7 @@ -WIDTH=20 -HEIGHT=20 +WIDTH=40 +HEIGHT=40 ENTRY=5,5 -EXIT=10,10 +EXIT=25,25 OUTPUT_FILE=test PERFECT=False SEED=111 @@ -10,6 +10,12 @@ TILEMAP_CELL_SIZE=4,2 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_FULL=1"{100,1000,1000:1000,1000,1000}######" +TILEMAP_FULL=1"{100,1000,1000:1000,1000,1000}######" +TILEMAP_FULL=1"{100,1000,1000:1000,1000,1000}######" +TILEMAP_PATH="{100,100,1000:100,100,1000} " +TILEMAP_PATH="{100,100,1000:100,100,1000} " +TILEMAP_PATH="{100,100,1000:100,100,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} " diff --git a/tmp b/tmp new file mode 100644 index 0000000..e69de29 -- 2.53.0