]> Untitled Git - axy/ft/a-maze-ing.git/commitdiff
pathfinding
authorAxy <gilliardmarthey.axel@gmail.com>
Tue, 10 Mar 2026 19:04:51 +0000 (20:04 +0100)
committerAxy <gilliardmarthey.axel@gmail.com>
Tue, 10 Mar 2026 19:04:51 +0000 (20:04 +0100)
__main__.py
amazeing/config/config_parser.py
amazeing/maze_class/maze_walls.py
amazeing/maze_display/TTYdisplay.py
example.conf
tmp [new file with mode: 0644]

index 18302487907176281db49182a7d14a6d28a063f3..0a8ab19648b60c906a89d445a526a8026733b268 100644 (file)
@@ -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)
index 167699e93830dcc6ebef535d3f69bb8a8457e79b..e9b70419663f8f944891b4e37f878726db004fca 100644 (file)
@@ -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)
                     ),
index ff633624def71b215436e99836726e0326521d42..9baf9a964f4ee2eca4d29c32c80be2cb3ce2f801 100644 (file)
@@ -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:
index a79bcdcd66543581eaa10c4ad1e360067bd201a2..e84af6898af519df2f36f203d7fd5d99715d0bc0 100644 (file)
@@ -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
index 242625ab6f318ee1abbc9ef44128fdb1aed1bee4..fbec71b677ae8ef1d1ce2a2e9742e2a120c2a58b 100644 (file)
@@ -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 (file)
index 0000000..e69de29