2 Commits

Author SHA1 Message Date
da7e d2d477d1b5 config class added 2026-03-19 16:13:13 +01:00
da7e c6c7e6e47e Generator class rework 2026-03-19 14:59:18 +01:00
6 changed files with 136 additions and 86 deletions
+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
@@ -2,13 +2,12 @@ from dataclasses import dataclass
import numpy import numpy
from .Cell import Cell from .Cell import Cell
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
@@ -24,18 +23,12 @@ 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: def export_maze(self, file_name: str) -> None:
with open(file_name, "w") as file: with open(file_name, "w") as file:
file.write(self.__str__()) 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]:
+77 -75
View File
@@ -1,87 +1,89 @@
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: class MazeGenerator(ABC):
class Kruskal: @abstractmethod
@staticmethod @classmethod
def walls_to_maze( def generator(
walls: list[tuple[int, int]], height: int, width: int cls, height: int, width: int
) -> np.ndarray: ) -> Generator[np.ndarray, None, np.ndarray]: ...
maze: np.ndarray = np.array(
[[Cell(value=0) for _ in range(width)] for _ in range(height)]
)
for wall in walls:
x, y = wall
match y - x:
case 1:
maze[math.trunc((x / width))][x % width].set_est(True)
maze[math.trunc((y / width))][y % width].set_west(True)
case width:
maze[math.trunc((x / width))][x % width].set_south(
True
)
maze[math.trunc((y / width))][y % width].set_north(
True
)
for x in range(height):
for y in range(width):
if x == 0:
maze[x][y].set_north(True)
if x == height - 1:
maze[x][y].set_south(True)
if y == 0:
maze[x][y].set_est(True)
if y == width - 1:
maze[x][y].set_west(True)
return maze
@staticmethod
def is_in_same_set(
sets: list[list[int]], wall: tuple[int, int]
) -> bool:
a, b = wall
for set in sets:
if a in set and b in set:
return True
if a in set or b in set:
return False
return False
@staticmethod class Kruskal(MazeGenerator):
def merge_sets(sets: list[list[int]], wall: tuple[int, int]) -> None: @staticmethod
a, b = wall def walls_to_maze(
base_set = None walls: list[tuple[int, int]], height: int, width: int
for set in sets: ) -> np.ndarray:
if base_set is None and (a in set or b in set): maze: np.ndarray = np.array(
base_set = set [[Cell(value=0) for _ in range(width)] for _ in range(height)]
elif base_set and (a in set or b in set): )
base_set += set for wall in walls:
sets.remove(set) x, y = wall
match y - x:
case 1:
maze[math.trunc((x / width))][x % width].set_est(True)
maze[math.trunc((y / width))][y % width].set_west(True)
case width:
maze[math.trunc((x / width))][x % width].set_south(True)
maze[math.trunc((y / width))][y % width].set_north(True)
for x in range(height):
for y in range(width):
if x == 0:
maze[x][y].set_north(True)
if x == height - 1:
maze[x][y].set_south(True)
if y == 0:
maze[x][y].set_est(True)
if y == width - 1:
maze[x][y].set_west(True)
return maze
@classmethod @staticmethod
def kruskal( def is_in_same_set(sets: list[list[int]], wall: tuple[int, int]) -> bool:
cls, height: int, width: int a, b = wall
) -> Generator[np.ndarray, None, np.ndarray]: for set in sets:
sets = [[i] for i in range(height * width)] if a in set and b in set:
walls = [] return True
for h in range(height): if a in set or b in set:
for w in range(width - 1): return False
walls += [(w + (width * h), w + (width * h) + 1)] return False
for w in range(width):
for h in range(height - 1):
walls += [(w + (width * h), w + (width * h) + width)]
np.random.shuffle(walls)
yield cls.walls_to_maze(walls, height, width) @staticmethod
for wall in walls: def merge_sets(sets: list[list[int]], wall: tuple[int, int]) -> None:
if not cls.is_in_same_set(sets, wall): a, b = wall
cls.merge_sets(sets, wall) base_set = None
walls.remove(wall) for set in sets:
yield cls.walls_to_maze(walls, height, width) if base_set is None and (a in set or b in set):
return cls.walls_to_maze(walls, height, width) base_set = set
elif base_set and (a in set or b in set):
base_set += set
sets.remove(set)
@classmethod
def generator(
cls, height: int, width: int
) -> Generator[np.ndarray, None, np.ndarray]:
sets = [[i] for i in range(height * width)]
walls = []
for h in range(height):
for w in range(width - 1):
walls += [(w + (width * h), w + (width * h) + 1)]
for w in range(width):
for h in range(height - 1):
walls += [(w + (width * h), w + (width * h) + width)]
np.random.shuffle(walls)
yield cls.walls_to_maze(walls, height, width)
for wall in walls:
if not cls.is_in_same_set(sets, wall):
cls.merge_sets(sets, wall)
walls.remove(wall)
yield cls.walls_to_maze(walls, height, width)
return cls.walls_to_maze(walls, height, width)
def main(): def main():
+8
View File
@@ -0,0 +1,8 @@
from abc import ABC, abstractmethod
from .Maze import Maze
class MazeSolver(ABC):
@abstractmethod
@classmethod
def solve(cls, 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"]