diff --git a/Makefile b/Makefile index 7027e1d..beae4f9 100644 --- a/Makefile +++ b/Makefile @@ -20,3 +20,6 @@ lint-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 diff --git a/src/amaz_lib/MazeGenerator.py b/src/amaz_lib/MazeGenerator.py index 5f2f7a2..0d4e102 100644 --- a/src/amaz_lib/MazeGenerator.py +++ b/src/amaz_lib/MazeGenerator.py @@ -3,6 +3,7 @@ from typing import Generator import numpy as np from .Cell import Cell import math +import random class MazeGenerator(ABC): @@ -84,17 +85,111 @@ class Kruskal(MazeGenerator): return self.walls_to_maze(walls, height, width) -def main(): - try: - for alg in MazeGenerator.Kruskal.kruskal(10, 10): - maze = alg - # print(maze) - # print() - print(maze) +class DepthFirstSearch: - except GeneratorExit as maze: - print(maze) + @staticmethod + def generator(width: int, height: int) -> np.ndarray: + maze = DepthFirstSearch.init_maze(width, height) + visited = [] + path = [] + w_h = (width, height) + coord = (0, 0) + while len(visited) < width * height: + x, y = coord + rand_steps = DepthFirstSearch.random_cells(visited, coord, w_h) + if len(rand_steps) == 0: + path = DepthFirstSearch.back_on_step(path, w_h) + coord = DepthFirstSearch.last(path) + rand_steps = DepthFirstSearch.random_cells(path, coord, w_h) + x, y = coord + wall = DepthFirstSearch.next_step(rand_steps) + wall_r = DepthFirstSearch.reverse_path(wall) + maze[y][x] = DepthFirstSearch.broken_wall(maze[y][x], wall) + visited = DepthFirstSearch.add_cell_visited(coord, visited) + path = DepthFirstSearch.add_cell_visited(coord, path) + coord = DepthFirstSearch.next_cell(x, y, wall) + x, y = coord + maze[y][x] = DepthFirstSearch.broken_wall(maze[y][x], wall_r) + 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 -if __name__ == "__main__": - main() + @staticmethod + def add_cell_visited(coord: tuple, visited: list = []) -> list: + visited.append(coord) + return visited + + @staticmethod + def random_cells(visited: list, coord: tuple, w_h: tuple) -> list: + rand_cell = [] + x, y = coord + width, height = w_h + # NORTH + if y - 1 >= 0 and coord not in visited: + rand_cell.append("N") + + # SOUTH + if y + 1 < height and coord not in visited: + rand_cell.append("S") + + # WEST + if x - 1 >= 0 and coord not in visited: + rand_cell.append("W") + + # EAST + if x + 1 < width and coord not in visited: + rand_cell.append("E") + return rand_cell + + @staticmethod + def next_step(rand_cell: list) -> str: + return 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) + + def reverse_path(next: str) -> str: + reverse = { + "N": "S", + "S": "N", + "W": "E", + "E": "W" + } + return reverse[next] + + @staticmethod + def last(path: list): + return path(len(path) - 1) + + def back_on_step(path: list, w_h: tuple) -> list: + last = DepthFirstSearch.last(path) + r_cells = DepthFirstSearch.random_cells(path, last, w_h) + while len(r_cells == 0): + path.pop(len(path) - 1) + last = DepthFirstSearch.last(path) + r_cells = DepthFirstSearch.random_cells(path, last, w_h) + return path diff --git a/src/amaz_lib/__init__.py b/src/amaz_lib/__init__.py index 9a6cf1d..f9ebb1b 100644 --- a/src/amaz_lib/__init__.py +++ b/src/amaz_lib/__init__.py @@ -1,8 +1,8 @@ from .Cell import Cell from .Maze import Maze -from .MazeGenerator import MazeGenerator +from .MazeGenerator import MazeGenerator, DepthFirstSearch from .MazeSolver import MazeSolver __version__ = "1.0.0" __author__ = "us" -__all__ = ["Cell", "Maze", "MazeGenerator", "MazeSolver"] +__all__ = ["Cell", "Maze", "MazeGenerator", "MazeSolver", "DepthFirstSearch"] diff --git a/tests/test_Depth.py b/tests/test_Depth.py new file mode 100644 index 0000000..c7e6eed --- /dev/null +++ b/tests/test_Depth.py @@ -0,0 +1,17 @@ +from amaz_lib.MazeGenerator import DepthFirstSearch +from amaz_lib.Cell import Cell + + +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: + maze = DepthFirstSearch.init_maze(10, 10) + lst = DepthFirstSearch.add_cell_visited(maze[0][0]) + rand_cells = DepthFirstSearch.random_cells(lst, maze, 0, 1) + assert len(rand_cells) == 2 diff --git a/tests/test_Maze.py b/tests/test_Maze.py index 7bdf580..6c0ae53 100644 --- a/tests/test_Maze.py +++ b/tests/test_Maze.py @@ -15,7 +15,7 @@ def test_maze_setter_getter() -> None: ) maze.set_maze(test) - assert numpy.array_equal(maze.get_maze(), test) == True + assert numpy.array_equal(maze.get_maze(), test) is True def test_maze_str() -> None: