diff --git a/pyproject.toml b/pyproject.toml index 738ccdf..11a0fd9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,6 @@ requires-python = ">=3.10" dependencies = [ "numpy>=2.2.6", "pydantic>=2.12.5", - "pytest>=9.0.2", ] @@ -15,8 +14,12 @@ dependencies = [ dev = [ "mypy>=1.19.1", "flake8>=7.3.0", + "pytest>=9.0.2", ] [tool.mypy] python_version = "3.10" + +[tool.pytest.ini_options] +pythonpath = ["src"] diff --git a/src/AMazeIng.py b/src/AMazeIng.py new file mode 100644 index 0000000..7389b0b --- /dev/null +++ b/src/AMazeIng.py @@ -0,0 +1,46 @@ +from dataclasses import field +from os import eventfd_read +from typing import Generator +import numpy +from typing_extensions import Self +from pydantic import AfterValidator, BaseModel, Field, model_validator + +from amaz_lib import Maze, MazeGenerator, MazeSolver +from amaz_lib.Cell import Cell + + +class AMazeIng(BaseModel): + width: int = Field(ge=3) + height: int = Field(ge=3) + entry: tuple[int, int] + exit: tuple[int, int] + output_file: str = Field(min_length=3) + perfect: bool = Field(default=True) + maze: Maze = Field(default=Maze(maze=numpy.array([]))) + generator: MazeGenerator + solver: MazeSolver + + @model_validator(mode="after") + def check_entry_exit(self) -> Self: + if self.entry[0] >= self.width or self.entry[1] >= self.height: + raise ValueError("Entry coordinates exceed the maze size") + if self.exit[0] >= self.width or self.exit[1] >= self.height: + raise ValueError("Exit coordinates exceed the maze size") + return self + + def generate(self) -> Generator[Maze, None, None]: + for array in self.generator.generator(self.height, self.width): + self.maze.set_maze(array) + yield self.maze + + def solve_path(self) -> str: + return self.solver.solve(self.maze) + + def __str__(self) -> str: + res = self.maze.__str__() + res += "\n" + res += f"{self.entry[0]},{self.entry[1]}\n" + res += f"{self.exit[0]},{self.exit[1]}\n" + res += self.solve_path() + res += "\n" + return res diff --git a/src/amaz_lib/classes/Cell.py b/src/amaz_lib/Cell.py similarity index 78% rename from src/amaz_lib/classes/Cell.py rename to src/amaz_lib/Cell.py index d6675bc..56f0c61 100644 --- a/src/amaz_lib/classes/Cell.py +++ b/src/amaz_lib/Cell.py @@ -41,25 +41,10 @@ class Cell(BaseModel): return self.value & 4 == 4 def set_west(self, is_wall: bool) -> None: - if (not is_wall and self.value | 8 == 15) or ( - is_wall and self.value | 8 != 15 + if (not is_wall and self.value | 7 == 15) or ( + is_wall and self.value | 7 != 15 ): self.value = self.value ^ (8) def get_west(self) -> bool: return self.value & 8 == 8 - - -def main() -> None: - c = Cell(value=1) - print(c.get_north()) - c.set_north(True) - print(c.get_north()) - c.set_north(True) - print(c.get_north()) - c.set_north(False) - print(c.get_north()) - - -if __name__ == "__main__": - main() diff --git a/src/amaz_lib/classes/Maze.py b/src/amaz_lib/Maze.py similarity index 77% rename from src/amaz_lib/classes/Maze.py rename to src/amaz_lib/Maze.py index e47c51c..4dcf016 100644 --- a/src/amaz_lib/classes/Maze.py +++ b/src/amaz_lib/Maze.py @@ -2,14 +2,12 @@ from dataclasses import dataclass import numpy from .Cell import Cell -from ..MazeGenerator import MazeGenerator +from .MazeGenerator import MazeGenerator @dataclass class Maze: maze: numpy.ndarray - start: tuple[int, int] - end: tuple[int, int] def get_maze(self) -> numpy.ndarray | None: return self.maze @@ -25,18 +23,8 @@ class Maze: for cell in line: res += cell.__str__() res += "\n" - res += "\n" - res += f"{self.start[0]},{self.start[1]}\n" - res += f"{self.end[0]},{self.end[1]}\n" return res - def export_maze(self, file_name: str) -> None: - with open(file_name, "w") as file: - file.write(self.__str__()) - - def solver(self) -> str: - pass - def ascii_print(self) -> None: for line in self.maze: if line is self.maze[0]: diff --git a/src/amaz_lib/MazeGenerator.py b/src/amaz_lib/MazeGenerator.py index a41536e..5f2f7a2 100644 --- a/src/amaz_lib/MazeGenerator.py +++ b/src/amaz_lib/MazeGenerator.py @@ -1,15 +1,14 @@ from abc import ABC, abstractmethod from typing import Generator import numpy as np -from .classes.Cell import Cell +from .Cell import Cell import math class MazeGenerator(ABC): @abstractmethod - @classmethod def generator( - cls, height: int, width: int + self, height: int, width: int ) -> Generator[np.ndarray, None, np.ndarray]: ... @@ -63,9 +62,8 @@ class Kruskal(MazeGenerator): base_set += set sets.remove(set) - @classmethod def generator( - cls, height: int, width: int + self, height: int, width: int ) -> Generator[np.ndarray, None, np.ndarray]: sets = [[i] for i in range(height * width)] walls = [] @@ -77,13 +75,13 @@ class Kruskal(MazeGenerator): walls += [(w + (width * h), w + (width * h) + width)] np.random.shuffle(walls) - yield cls.walls_to_maze(walls, height, width) + yield self.walls_to_maze(walls, height, width) for wall in walls: - if not cls.is_in_same_set(sets, wall): - cls.merge_sets(sets, wall) + if not self.is_in_same_set(sets, wall): + self.merge_sets(sets, wall) walls.remove(wall) - yield cls.walls_to_maze(walls, height, width) - return cls.walls_to_maze(walls, height, width) + yield self.walls_to_maze(walls, height, width) + return self.walls_to_maze(walls, height, width) def main(): diff --git a/src/amaz_lib/MazeSolver.py b/src/amaz_lib/MazeSolver.py new file mode 100644 index 0000000..2efe587 --- /dev/null +++ b/src/amaz_lib/MazeSolver.py @@ -0,0 +1,7 @@ +from abc import ABC, abstractmethod +from .Maze import Maze + + +class MazeSolver(ABC): + @abstractmethod + def solve(self, maze: Maze) -> str: ... diff --git a/src/amaz_lib/__init__.py b/src/amaz_lib/__init__.py index cf2963d..9a6cf1d 100644 --- a/src/amaz_lib/__init__.py +++ b/src/amaz_lib/__init__.py @@ -1,7 +1,8 @@ -from .classes.Cell import Cell -from .classes.Maze import Maze +from .Cell import Cell +from .Maze import Maze from .MazeGenerator import MazeGenerator +from .MazeSolver import MazeSolver __version__ = "1.0.0" __author__ = "us" -__all__ = ["Cell", "Maze", "MazeGenerator"] +__all__ = ["Cell", "Maze", "MazeGenerator", "MazeSolver"] diff --git a/tests/test_Cell.py b/tests/test_Cell.py new file mode 100644 index 0000000..9f37042 --- /dev/null +++ b/tests/test_Cell.py @@ -0,0 +1,31 @@ +import pytest +from amaz_lib.Cell import Cell + + +def test_cell_setter_getter() -> None: + cell = Cell(value=0) + + cell.set_north(True) + assert cell.get_north() is True + cell.set_north(False) + assert cell.get_north() is False + + cell.set_est(True) + assert cell.get_est() is True + cell.set_est(False) + assert cell.get_est() is False + + cell.set_south(True) + assert cell.get_south() is True + cell.set_south(False) + assert cell.get_south() is False + + cell.set_west(True) + assert cell.get_west() is True + cell.set_west(False) + assert cell.get_west() is False + + cell.set_value(8) + assert cell.get_value() == 8 + cell.set_value(0) + assert cell.get_value() == 0 diff --git a/tests/test_Maze.py b/tests/test_Maze.py new file mode 100644 index 0000000..7bdf580 --- /dev/null +++ b/tests/test_Maze.py @@ -0,0 +1,31 @@ +import numpy +from amaz_lib.Cell import Cell +from amaz_lib.Maze import Maze + + +def test_maze_setter_getter() -> None: + maze = Maze(numpy.array([])) + + test = numpy.array( + [ + [Cell(value=6), Cell(value=8), Cell(value=11)], + [Cell(value=6), Cell(value=8), Cell(value=11)], + [Cell(value=6), Cell(value=8), Cell(value=11)], + ] + ) + + maze.set_maze(test) + assert numpy.array_equal(maze.get_maze(), test) == True + + +def test_maze_str() -> None: + test = numpy.array( + [ + [Cell(value=6), Cell(value=8), Cell(value=11)], + [Cell(value=6), Cell(value=8), Cell(value=11)], + [Cell(value=6), Cell(value=8), Cell(value=11)], + ] + ) + maze = Maze(test) + + assert maze.__str__() == "68B\n68B\n68B\n" diff --git a/tests/test_MazeGenerator.py b/tests/test_MazeGenerator.py new file mode 100644 index 0000000..cb0aa6b --- /dev/null +++ b/tests/test_MazeGenerator.py @@ -0,0 +1,11 @@ +import numpy +from amaz_lib.MazeGenerator import Kruskal + + +def test_kruskal_output_shape() -> None: + generator = Kruskal() + maze = numpy.array([]) + for output in generator.generator(10, 10): + maze = output + + assert maze.shape == (10, 10) diff --git a/uv.lock b/uv.lock index b4d2e9a..92fbe72 100644 --- a/uv.lock +++ b/uv.lock @@ -14,26 +14,26 @@ dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pydantic" }, - { name = "pytest" }, ] [package.dev-dependencies] dev = [ { name = "flake8" }, { name = "mypy" }, + { name = "pytest" }, ] [package.metadata] requires-dist = [ { name = "numpy", specifier = ">=2.2.6" }, { name = "pydantic", specifier = ">=2.12.5" }, - { name = "pytest", specifier = ">=9.0.2" }, ] [package.metadata.requires-dev] dev = [ { name = "flake8", specifier = ">=7.3.0" }, { name = "mypy", specifier = ">=1.19.1" }, + { name = "pytest", specifier = ">=9.0.2" }, ] [[package]]