Merge branch 'main' into parsing

This commit is contained in:
Maoake Teriierooiterai
2026-03-19 18:21:30 +01:00
11 changed files with 147 additions and 46 deletions
+4 -1
View File
@@ -7,7 +7,6 @@ requires-python = ">=3.10"
dependencies = [ dependencies = [
"numpy>=2.2.6", "numpy>=2.2.6",
"pydantic>=2.12.5", "pydantic>=2.12.5",
"pytest>=9.0.2",
] ]
@@ -15,8 +14,12 @@ dependencies = [
dev = [ dev = [
"mypy>=1.19.1", "mypy>=1.19.1",
"flake8>=7.3.0", "flake8>=7.3.0",
"pytest>=9.0.2",
] ]
[tool.mypy] [tool.mypy]
python_version = "3.10" python_version = "3.10"
[tool.pytest.ini_options]
pythonpath = ["src"]
+46
View File
@@ -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
@@ -41,25 +41,10 @@ class Cell(BaseModel):
return self.value & 4 == 4 return self.value & 4 == 4
def set_west(self, is_wall: bool) -> None: def set_west(self, is_wall: bool) -> None:
if (not is_wall and self.value | 8 == 15) or ( if (not is_wall and self.value | 7 == 15) or (
is_wall and self.value | 8 != 15 is_wall and self.value | 7 != 15
): ):
self.value = self.value ^ (8) self.value = self.value ^ (8)
def get_west(self) -> bool: def get_west(self) -> bool:
return self.value & 8 == 8 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()
@@ -2,14 +2,12 @@ from dataclasses import dataclass
import numpy import numpy
from .Cell import Cell from .Cell import Cell
from ..MazeGenerator import MazeGenerator from .MazeGenerator import MazeGenerator
@dataclass @dataclass
class Maze: class Maze:
maze: numpy.ndarray maze: numpy.ndarray
start: tuple[int, int]
end: tuple[int, int]
def get_maze(self) -> numpy.ndarray | None: def get_maze(self) -> numpy.ndarray | None:
return self.maze return self.maze
@@ -25,18 +23,8 @@ class Maze:
for cell in line: for cell in line:
res += cell.__str__() res += cell.__str__()
res += "\n" res += "\n"
res += "\n"
res += f"{self.start[0]},{self.start[1]}\n"
res += f"{self.end[0]},{self.end[1]}\n"
return res 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: def ascii_print(self) -> None:
for line in self.maze: for line in self.maze:
if line is self.maze[0]: if line is self.maze[0]:
+8 -10
View File
@@ -1,15 +1,14 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Generator from typing import Generator
import numpy as np import numpy as np
from .classes.Cell import Cell from .Cell import Cell
import math import math
class MazeGenerator(ABC): class MazeGenerator(ABC):
@abstractmethod @abstractmethod
@classmethod
def generator( def generator(
cls, height: int, width: int self, height: int, width: int
) -> Generator[np.ndarray, None, np.ndarray]: ... ) -> Generator[np.ndarray, None, np.ndarray]: ...
@@ -63,9 +62,8 @@ class Kruskal(MazeGenerator):
base_set += set base_set += set
sets.remove(set) sets.remove(set)
@classmethod
def generator( def generator(
cls, 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 = [[i] for i in range(height * width)]
walls = [] walls = []
@@ -77,13 +75,13 @@ class Kruskal(MazeGenerator):
walls += [(w + (width * h), w + (width * h) + width)] walls += [(w + (width * h), w + (width * h) + width)]
np.random.shuffle(walls) np.random.shuffle(walls)
yield cls.walls_to_maze(walls, height, width) yield self.walls_to_maze(walls, height, width)
for wall in walls: for wall in walls:
if not cls.is_in_same_set(sets, wall): if not self.is_in_same_set(sets, wall):
cls.merge_sets(sets, wall) self.merge_sets(sets, wall)
walls.remove(wall) walls.remove(wall)
yield cls.walls_to_maze(walls, height, width) yield self.walls_to_maze(walls, height, width)
return cls.walls_to_maze(walls, height, width) return self.walls_to_maze(walls, height, width)
def main(): def main():
+7
View File
@@ -0,0 +1,7 @@
from abc import ABC, abstractmethod
from .Maze import Maze
class MazeSolver(ABC):
@abstractmethod
def solve(self, maze: Maze) -> str: ...
+4 -3
View File
@@ -1,7 +1,8 @@
from .classes.Cell import Cell from .Cell import Cell
from .classes.Maze import Maze from .Maze import Maze
from .MazeGenerator import MazeGenerator from .MazeGenerator import MazeGenerator
from .MazeSolver import MazeSolver
__version__ = "1.0.0" __version__ = "1.0.0"
__author__ = "us" __author__ = "us"
__all__ = ["Cell", "Maze", "MazeGenerator"] __all__ = ["Cell", "Maze", "MazeGenerator", "MazeSolver"]
+31
View File
@@ -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
+31
View File
@@ -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"
+11
View File
@@ -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)
Generated
+2 -2
View File
@@ -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.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 = "numpy", version = "2.4.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "pydantic" }, { name = "pydantic" },
{ name = "pytest" },
] ]
[package.dev-dependencies] [package.dev-dependencies]
dev = [ dev = [
{ name = "flake8" }, { name = "flake8" },
{ name = "mypy" }, { name = "mypy" },
{ name = "pytest" },
] ]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "numpy", specifier = ">=2.2.6" }, { name = "numpy", specifier = ">=2.2.6" },
{ name = "pydantic", specifier = ">=2.12.5" }, { name = "pydantic", specifier = ">=2.12.5" },
{ name = "pytest", specifier = ">=9.0.2" },
] ]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [ dev = [
{ name = "flake8", specifier = ">=7.3.0" }, { name = "flake8", specifier = ">=7.3.0" },
{ name = "mypy", specifier = ">=1.19.1" }, { name = "mypy", specifier = ">=1.19.1" },
{ name = "pytest", specifier = ">=9.0.2" },
] ]
[[package]] [[package]]