1 Commits

Author SHA1 Message Date
da7e a79d4e5c3b algorithm edited but nothing better 2026-03-24 15:33:50 +01:00
9 changed files with 57 additions and 205 deletions
-8
View File
@@ -18,13 +18,5 @@ lint-strict:
uv run flake8 . uv run flake8 .
uv run mypy . --strict uv run mypy . --strict
run_test_parsing:
PYTHONPATH=src uv run pytest tests/test_parsing.py
run_test_dfs:
PYTHONPATH=src uv run pytest tests/test_Depth.py
run_test_maze_gen:
PYTHONPATH=src uv run pytest tests/test_MazeGenerator.py
run_test: run_test:
uv run pytest uv run pytest
+10 -7
View File
@@ -1,21 +1,24 @@
import os import os
from numpy import ma
from src.amaz_lib import MazeGenerator, Kruskal, AStar
from src.amaz_lib import Maze from src.amaz_lib import Maze
from src.amaz_lib import MazeGenerator
import src.amaz_lib as g
def main(maze_gen: MazeGenerator) -> None: def main() -> None:
# try: # try:
maze = Maze(maze=None) maze = Maze(maze=None)
gen = maze_gen.generator(100, 100) generator = Kruskal()
for alg in gen: for alg in generator.generator(20, 20):
maze.set_maze(alg) maze.set_maze(alg)
os.system("clear") # os.system("clear")
maze.ascii_print() maze.ascii_print()
# solver = AStar((1, 1), (14, 18))
# print(solver.solve(maze))
# except Exception as err: # except Exception as err:
# print(err) # print(err)
if __name__ == "__main__": if __name__ == "__main__":
main(g.DepthFirstSearch()) main()
+3 -5
View File
@@ -1,10 +1,8 @@
from dataclasses import dataclass from pydantic import BaseModel, Field
@dataclass class Cell(BaseModel):
class Cell: value: int = Field(ge=0, le=15)
def __init__(self, value: int) -> None:
self.value = value
def __str__(self) -> str: def __str__(self) -> str:
return hex(self.value).removeprefix("0x").upper() return hex(self.value).removeprefix("0x").upper()
+7 -8
View File
@@ -26,15 +26,14 @@ class Maze:
return res return res
def ascii_print(self) -> None: def ascii_print(self) -> None:
for cell in self.maze[0]:
print("_", end="")
if cell.get_north():
print("__", end="")
else:
print(" ", end="")
print("_")
for line in self.maze: for line in self.maze:
if line is self.maze[0]:
for cell in line:
print("_", end="")
if cell.get_north():
print("__", end="")
else:
print(" ", end="")
print()
for cell in line: for cell in line:
if cell is line[0] and cell.get_west(): if cell is line[0] and cell.get_west():
print("|", end="") print("|", end="")
+27 -135
View File
@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Generator from dataclasses import dataclass
from typing import Generator, Set
import numpy as np import numpy as np
from .Cell import Cell from .Cell import Cell
import math import math
@@ -13,9 +14,13 @@ class MazeGenerator(ABC):
class Kruskal(MazeGenerator): class Kruskal(MazeGenerator):
class Set:
def __init__(self, cells: list[int]) -> None:
self.cells: list[int] = cells
@staticmethod @staticmethod
def walls_to_maze( def walls_to_maze(
walls: list[tuple[int, int]], height: int, width: int walls: np.ndarray, height: int, width: int
) -> np.ndarray: ) -> np.ndarray:
maze: np.ndarray = np.array( maze: np.ndarray = np.array(
[[Cell(value=0) for _ in range(width)] for _ in range(height)] [[Cell(value=0) for _ in range(width)] for _ in range(height)]
@@ -36,43 +41,46 @@ class Kruskal(MazeGenerator):
if x == height - 1: if x == height - 1:
maze[x][y].set_south(True) maze[x][y].set_south(True)
if y == 0: if y == 0:
maze[x][y].set_est(True)
if y == width - 1:
maze[x][y].set_west(True) maze[x][y].set_west(True)
if y == width - 1:
maze[x][y].set_est(True)
return maze return maze
@staticmethod @staticmethod
def is_in_same_set(sets: list[list[int]], wall: tuple[int, int]) -> bool: def is_in_same_set(sets: np.ndarray, wall: tuple[int, int]) -> bool:
a, b = wall a, b = wall
for set in sets: for set in sets:
if a in set and b in set: if a in set.cells and b in set.cells:
return True return True
if a in set or b in set: elif a in set.cells or b in set.cells:
return False return False
return False return False
@staticmethod @staticmethod
def merge_sets(sets: list[list[int]], wall: tuple[int, int]) -> None: def merge_sets(sets: np.ndarray, wall: tuple[int, int]) -> None:
a, b = wall a, b = wall
base_set = None base_set = None
for set in sets: for i in range(len(sets)):
if base_set is None and (a in set or b in set): if base_set is None and (a in sets[i].cells or b in sets[i].cells):
base_set = set base_set = sets[i]
elif base_set and (a in set or b in set): elif base_set and (a in sets[i].cells or b in sets[i].cells):
base_set += set base_set.cells += sets[i].cells
sets.remove(set) np.delete(sets, i)
return
raise Exception("two sets not found")
def generator( def generator(
self, height: int, width: int self, height: int, width: int
) -> Generator[np.ndarray, None, np.ndarray]: ) -> Generator[np.ndarray, None, np.ndarray]:
sets = [[i] for i in range(height * width)] sets = np.array([self.Set([i]) for i in range(height * width)])
walls = [] walls = []
for h in range(height): for h in range(height):
for w in range(width - 1): for w in range(width - 1):
walls += [(w + (width * h), w + (width * h) + 1)] walls += [(w + (width * h), w + (width * h) + 1)]
for w in range(width): for h in range(height - 1):
for h in range(height - 1): for w in range(width):
walls += [(w + (width * h), w + (width * h) + width)] walls += [(w + (width * h), w + (width * (h + 1)))]
print(walls)
np.random.shuffle(walls) np.random.shuffle(walls)
yield self.walls_to_maze(walls, height, width) yield self.walls_to_maze(walls, height, width)
@@ -81,121 +89,5 @@ class Kruskal(MazeGenerator):
self.merge_sets(sets, wall) self.merge_sets(sets, wall)
walls.remove(wall) walls.remove(wall)
yield self.walls_to_maze(walls, height, width) yield self.walls_to_maze(walls, height, width)
print(f"nb sets: {len(sets)}")
return self.walls_to_maze(walls, height, width) return self.walls_to_maze(walls, height, width)
class DepthFirstSearch(MazeGenerator):
def generator(self, width: int, height: int
) -> Generator[np.ndarray, None, np.ndarray]:
maze = DepthFirstSearch.init_maze(width, height)
visited = np.zeros((height, width), dtype=bool)
path = list()
w_h = (width, height)
coord = (0, 0)
x, y = coord
first = True
while path or first:
first = False
visited[y, x] = True
path = DepthFirstSearch.add_cell_visited(coord, path)
random_c = DepthFirstSearch.random_cells(visited, coord, w_h)
if len(random_c) == 0:
path = DepthFirstSearch.back_on_step(path, w_h, visited)
if path:
coord = path[-1]
random_c = DepthFirstSearch.random_cells(visited, coord, w_h)
x, y = coord
if not path:
break
wall = DepthFirstSearch.next_step(random_c)
maze[y][x] = DepthFirstSearch.broken_wall(maze[y][x], wall)
coord = DepthFirstSearch.next_cell(x, y, wall)
wall_r = DepthFirstSearch.reverse_path(wall)
x, y = coord
maze[y][x] = DepthFirstSearch.broken_wall(maze[y][x], wall_r)
yield maze
return maze
@staticmethod
def init_maze(width: int, height: int) -> np.ndarray:
maze = np.array([[Cell(value=15) for _ in range(width)]
for _ in range(height)])
return maze
@staticmethod
def add_cell_visited(coord: tuple, path: set) -> list:
path.append(coord)
return path
@staticmethod
def random_cells(visited: np.array, coord: tuple, w_h: tuple) -> list:
rand_cell = []
x, y = coord
width, height = w_h
if y - 1 >= 0 and not visited[y - 1][x]:
rand_cell.append("N")
if y + 1 < height and not visited[y + 1][x]:
rand_cell.append("S")
if x - 1 >= 0 and not visited[y][x - 1]:
rand_cell.append("W")
if x + 1 < width and not visited[y][x + 1]:
rand_cell.append("E")
return rand_cell
@staticmethod
def next_step(rand_cell: list) -> str:
return np.random.choice(rand_cell)
@staticmethod
def broken_wall(cell: Cell, wall: str) -> Cell:
if wall == "N":
cell.set_north(False)
elif wall == "S":
cell.set_south(False)
elif wall == "W":
cell.set_west(False)
elif wall == "E":
cell.set_est(False)
return cell
@staticmethod
def next_cell(x: int, y: int, next: str) -> tuple:
next_step = {
"N": (0, -1),
"S": (0, 1),
"W": (-1, 0),
"E": (1, 0)
}
add_x, add_y = next_step[next]
return (x + add_x, y + add_y)
@staticmethod
def reverse_path(next: str) -> str:
reverse = {
"N": "S",
"S": "N",
"W": "E",
"E": "W"
}
return reverse[next]
@staticmethod
def back_on_step(path: list, w_h: tuple, visited: np.array) -> list:
last = path[-1]
r_cells = DepthFirstSearch.random_cells(visited, last, w_h)
while len(path) > 0:
path.pop()
if path:
last = path[-1]
r_cells = DepthFirstSearch.random_cells(visited, last, w_h)
if r_cells:
break
return path
+2 -4
View File
@@ -1,10 +1,8 @@
from .Cell import Cell from .Cell import Cell
from .Maze import Maze from .Maze import Maze
from .MazeGenerator import MazeGenerator, DepthFirstSearch from .MazeGenerator import MazeGenerator, Kruskal
from .MazeGenerator import Kruskal
from .MazeSolver import MazeSolver, AStar from .MazeSolver import MazeSolver, AStar
__version__ = "1.0.0" __version__ = "1.0.0"
__author__ = "us" __author__ = "us"
__all__ = ["Cell", "Maze", "MazeGenerator", __all__ = ["Cell", "Maze", "MazeGenerator", "MazeSolver", "AStar", "Kruskal"]
"MazeSolver", "AStar", "Kruskal", "DepthFirstSearch"]
-27
View File
@@ -1,27 +0,0 @@
from amaz_lib.MazeGenerator import DepthFirstSearch
from amaz_lib.Cell import Cell
import numpy as np
class TestDepth:
def test_init_maze(self) -> None:
maze = DepthFirstSearch.init_maze(10, 10)
cell = Cell(value=15)
maze[1][1].set_est(False)
assert maze[0][0].value == cell.value
def test_rand_cells(self) -> None:
w_h = (10, 10)
lst = np.zeros((10, 10), dtype=bool)
lst[0, 0] = True
rand_cells = DepthFirstSearch.random_cells(lst, (0, 1), w_h)
assert len(rand_cells) == 2
def test_next_cell(self) -> None:
coord = (5, 4)
x, y = coord
assert DepthFirstSearch.next_cell(x, y, "N") == (2, 3)
def test_reverse_path(self) -> None:
assert DepthFirstSearch.reverse_path("N") == "S"
+1 -1
View File
@@ -15,7 +15,7 @@ def test_maze_setter_getter() -> None:
) )
maze.set_maze(test) maze.set_maze(test)
assert numpy.array_equal(maze.get_maze(), test) is True assert numpy.array_equal(maze.get_maze(), test) == True
def test_maze_str() -> None: def test_maze_str() -> None:
+7 -10
View File
@@ -1,14 +1,11 @@
import numpy import numpy
from amaz_lib.MazeGenerator import DepthFirstSearch from amaz_lib.MazeGenerator import Kruskal
class TestMazeGenerator: def test_kruskal_output_shape() -> None:
generator = Kruskal()
maze = numpy.array([])
for output in generator.generator(10, 10):
maze = output
def test_generator(self) -> None: assert maze.shape == (10, 10)
w_h = (300, 300)
maze = numpy.array([])
generator = DepthFirstSearch().generator(*w_h)
for output in generator:
maze = output
assert maze.shape == w_h