]> Untitled Git - axy/ft/a-maze-ing.git/commitdiff
Pathfinding
authorAxy <gilliardmarthey.axel@gmail.com>
Mon, 9 Mar 2026 18:48:23 +0000 (19:48 +0100)
committerAxy <gilliardmarthey.axel@gmail.com>
Mon, 9 Mar 2026 18:48:23 +0000 (19:48 +0100)
__main__.py
amazeing/config/config_parser.py
amazeing/maze_class/maze.py
amazeing/maze_class/maze_pattern.py
amazeing/maze_class/maze_walls.py
example.conf

index 0e23b76243acec7bb42200e4d461c57d8ddc4ed4..22d26e940c1b826767ef470f16bca1dbe6a0ffa0 100644 (file)
@@ -27,9 +27,13 @@ maze = Maze(dims)
 
 maze.outline()
 
-pattern = Pattern(config.maze_pattern).centered_for(
-    dims, {CellCoord(5, 5), CellCoord(10, 10)}
-)
+excluded = set()
+if config.entry is not None:
+    excluded.add(config.entry)
+if config.exit is not None:
+    excluded.add(config.exit)
+
+pattern = Pattern(config.maze_pattern).centered_for(dims, excluded)
 pattern.fill(maze)
 
 walls_const = set(maze.walls_full())
@@ -54,7 +58,6 @@ def clear_backend() -> None:
             continue
         for tile in wall.tile_coords():
             backend.draw_tile(tile)
-            pass
 
 
 def display_maze(maze: Maze) -> None:
@@ -76,7 +79,6 @@ def display_maze(maze: Maze) -> None:
     maze.clear_dirty()
     backend.present()
     poll_events(0)
-    # poll_events(0)
 
 
 def poll_events(timeout_ms: int = -1) -> None:
@@ -97,6 +99,9 @@ def poll_events(timeout_ms: int = -1) -> None:
 
 maze_make_perfect(maze, callback=display_maze)
 maze_make_pacman(maze, walls_const, callback=display_maze)
+print(
+    maze.pathfind(CellCoord(config.entry), CellCoord(config.exit)), file=stderr
+)
 while False:
     maze_make_perfect(maze, callback=display_maze)
     # poll_events(200)
index 0b20ed7f1cd76cf9043451cfa1d80dec479b46b1..b6bd56c77afe6e8195fef750c7c82dba85ccb76d 100644 (file)
@@ -44,6 +44,10 @@ def parse_comment(s: str) -> ParseResult[str]:
     return recognize(seq(tag("#"), many_count(none_of("\n"))))(s)
 
 
+def parse_empty_line(s: str) -> ParseResult[None]:
+    return (None, s) if s.startswith("\n") else None
+
+
 def spaced[T](parser: Parser[T]) -> Parser[T]:
     return delimited(multispace0, parser, multispace0)
 
@@ -291,6 +295,7 @@ def line_parser[T](
             )
             for name, field in fields.items()
         ),
+        parser_map(lambda _: None, parse_empty_line),
     )
 
 
index a01e07d6a314d576a91a608f4c3eb4174cc6134f..28804cee9c93a596db57a61ae8f05ecb93fa2dee 100644 (file)
@@ -1,7 +1,10 @@
+from sys import stderr
 from typing import Callable, Generator, Iterable, cast
 
 from amazeing.maze_display.backend import IVec2
 from .maze_walls import (
+    Cardinal,
+    CellCoord,
     MazeWall,
     NetworkID,
     Orientation,
@@ -191,3 +194,43 @@ class Maze:
 
     def clear_dirty(self) -> None:
         self.__dirty = set()
+
+    def pathfind(
+        self, src: CellCoord, dst: CellCoord
+    ) -> list[Cardinal] | None:
+        class Path:
+            def __init__(self, prev: tuple["Path", Cardinal] | None) -> None:
+                self.prev: tuple["Path", Cardinal] | None = prev
+
+            def to_list(self) -> list[Cardinal]:
+                if self.prev is None:
+                    return []
+                prev, direction = self.prev
+                prev_list = prev.to_list()
+                prev_list.append(direction)
+                return prev_list
+
+            def __add__(self, value: Cardinal) -> "Path":
+                return Path((self, value))
+
+        walls_empty = set(self.walls_empty())
+        visited = set()
+        border = {src: Path(None)}
+        while len(border) != 0:
+            border_next = {}
+            for pos, path in border.items():
+                if pos == dst:
+                    return path.to_list()
+                visited.add(pos)
+                for direction in Cardinal.all():
+                    if pos.get_wall(direction) not in walls_empty:
+                        continue
+                    neighbour = pos.get_neighbour(direction)
+                    if neighbour in visited:
+                        continue
+                    if neighbour in border or neighbour in border_next:
+                        continue
+                    border_next[neighbour] = path + direction
+            border = border_next
+
+        return None
index ceaaa219c47f97ced4051e9135fc0445b199c88b..69ff2b6ecc52c50b8d6c71085e6239ad9e1040dc 100644 (file)
@@ -88,7 +88,12 @@ class Pattern:
         normalized: Pattern = self.normalized()
         negative = normalized.flood_filled().mirrored()
         dims = normalized.dims()
-        slots = set(CellCoord(canvas - dims + 1).all_up_to())
+        slots = set(
+            map(
+                lambda e: CellCoord(e + 1),
+                CellCoord(canvas - dims - 1).all_up_to(),
+            )
+        )
         for excluded in excluding:
             slots -= negative.offset(excluded).__cells
         if len(slots) == 0:
index bc0608007216b4a2b85045871ee275a8abee35ed..ff633624def71b215436e99836726e0326521d42 100644 (file)
@@ -76,6 +76,10 @@ class Cardinal(Enum):
     def right(self) -> "Cardinal":
         return self.left().opposite()
 
+    @staticmethod
+    def all() -> list["Cardinal"]:
+        return [Cardinal.NORTH, Cardinal.SOUTH, Cardinal.EAST, Cardinal.WEST]
+
 
 class WallCoord:
     def __init__(self, orientation: Orientation, a: int, b: int) -> None:
@@ -161,11 +165,18 @@ class CellCoord(IVec2):
                 return WallCoord(Orientation.HORIZONTAL, self.y, self.x)
             case Cardinal.SOUTH:
                 return WallCoord(Orientation.HORIZONTAL, self.y + 1, self.x)
-            case Cardinal.EAST:
-                return WallCoord(Orientation.VERTICAL, self.x, self.y)
             case Cardinal.WEST:
+                return WallCoord(Orientation.VERTICAL, self.x, self.y)
+            case Cardinal.EAST:
                 return WallCoord(Orientation.VERTICAL, self.x + 1, self.y)
 
+    def get_neighbour(self, cardinal: Cardinal) -> "CellCoord":
+        return next(
+            filter(
+                lambda e: e != self, self.get_wall(cardinal).neighbour_cells()
+            )
+        )
+
     def tile_coords(self) -> IVec2:
         return IVec2(self.x * 2 + 1, self.y * 2 + 1)
 
@@ -178,6 +189,4 @@ class CellCoord(IVec2):
                 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 map(self.get_neighbour, Cardinal.all())
index 04227c4c12ccfd346dc168aa88a29a1e860aec75..c00a50abc40a5b38c6b87509983478c07953f1a1 100644 (file)
@@ -1,7 +1,7 @@
 WIDTH=20
 HEIGHT=20
-ENTRY=2,5
-#EXIT=100,100
+ENTRY=5,5
+EXIT=10,10
 OUTPUT_FILE=test
 PERFECT=False
 SEED=111
@@ -18,6 +18,7 @@ TILEMAP_BACKGROUND="{1000,1000,1000: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="   #   "
@@ -26,11 +27,11 @@ 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="  ###  "