mirror of
https://github.com/maoakeEnterprise/amazing.git
synced 2026-04-28 16:04:35 +02:00
Merge branch 'docstring'
This commit is contained in:
+179
-7
@@ -7,18 +7,41 @@ import random
|
||||
|
||||
|
||||
class MazeSolver(ABC):
|
||||
"""Define the common interface for maze-solving algorithms."""
|
||||
|
||||
def __init__(self, start: tuple[int, int], end: tuple[int, int]) -> None:
|
||||
"""Initialize the maze solver.
|
||||
|
||||
Args:
|
||||
start: Start coordinates using 1-based indexing.
|
||||
end: End coordinates using 1-based indexing.
|
||||
"""
|
||||
self.start = (start[1] - 1, start[0] - 1)
|
||||
self.end = (end[1] - 1, end[0] - 1)
|
||||
|
||||
@abstractmethod
|
||||
def solve(
|
||||
self, maze: Maze, height: int | None = None, width: int | None = None
|
||||
) -> str: ...
|
||||
) -> str:
|
||||
"""Solve the maze and return the path as direction letters.
|
||||
|
||||
Args:
|
||||
maze: The maze to solve.
|
||||
height: Optional maze height.
|
||||
width: Optional maze width.
|
||||
|
||||
Returns:
|
||||
A string representing the path using cardinal directions.
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
class AStar(MazeSolver):
|
||||
"""Solve a maze using the A* pathfinding algorithm."""
|
||||
|
||||
class Node:
|
||||
"""Represent a node used during A* exploration."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinate: tuple[int, int],
|
||||
@@ -27,6 +50,15 @@ class AStar(MazeSolver):
|
||||
f: int,
|
||||
parent: Any,
|
||||
) -> None:
|
||||
"""Initialize a search node.
|
||||
|
||||
Args:
|
||||
coordinate: Coordinates of the node.
|
||||
g: Cost from the start node.
|
||||
h: Heuristic cost to the goal.
|
||||
f: Total estimated cost.
|
||||
parent: Parent node in the reconstructed path.
|
||||
"""
|
||||
self.coordinate = coordinate
|
||||
self.g = g
|
||||
self.h = h
|
||||
@@ -34,12 +66,35 @@ class AStar(MazeSolver):
|
||||
self.parent = parent
|
||||
|
||||
def __eq__(self, value: object, /) -> bool:
|
||||
"""Compare a node to a coordinate.
|
||||
|
||||
Args:
|
||||
value: Object to compare with.
|
||||
|
||||
Returns:
|
||||
``True`` if the value equals the node coordinate, otherwise
|
||||
``False``.
|
||||
"""
|
||||
return value == self.coordinate
|
||||
|
||||
def __init__(self, start: tuple[int, int], end: tuple[int, int]) -> None:
|
||||
"""Initialize the A* solver.
|
||||
|
||||
Args:
|
||||
start: Start coordinates using 1-based indexing.
|
||||
end: End coordinates using 1-based indexing.
|
||||
"""
|
||||
super().__init__(start, end)
|
||||
|
||||
def h(self, n: tuple[int, int]) -> int:
|
||||
"""Compute the Manhattan distance heuristic to the goal.
|
||||
|
||||
Args:
|
||||
n: Coordinates of the current node.
|
||||
|
||||
Returns:
|
||||
The heuristic distance to the end coordinate.
|
||||
"""
|
||||
return (
|
||||
max(n[0], self.end[0])
|
||||
- min(n[0], self.end[0])
|
||||
@@ -51,8 +106,18 @@ class AStar(MazeSolver):
|
||||
self,
|
||||
maze: NDArray[Any],
|
||||
actual: tuple[int, int],
|
||||
close: list['Node'],
|
||||
close: list["Node"],
|
||||
) -> list[tuple[int, int]]:
|
||||
"""Return all reachable neighboring coordinates.
|
||||
|
||||
Args:
|
||||
maze: Maze grid to inspect.
|
||||
actual: Current coordinate.
|
||||
close: List of already explored nodes.
|
||||
|
||||
Returns:
|
||||
A list of reachable adjacent coordinates not yet closed.
|
||||
"""
|
||||
path = [
|
||||
(
|
||||
(actual[0], actual[1] - 1)
|
||||
@@ -89,7 +154,18 @@ class AStar(MazeSolver):
|
||||
]
|
||||
return [p for p in path if p is not None]
|
||||
|
||||
def get_path(self, maze: NDArray[Any]) -> list['Node']:
|
||||
def get_path(self, maze: NDArray[Any]) -> list["Node"]:
|
||||
"""Perform A* exploration until the destination is reached.
|
||||
|
||||
Args:
|
||||
maze: Maze grid to solve.
|
||||
|
||||
Returns:
|
||||
The closed list ending with the goal node.
|
||||
|
||||
Raises:
|
||||
Exception: If no path can be found.
|
||||
"""
|
||||
open: list[AStar.Node] = []
|
||||
close: list[AStar.Node] = []
|
||||
|
||||
@@ -123,6 +199,17 @@ class AStar(MazeSolver):
|
||||
raise Exception("Path not found")
|
||||
|
||||
def get_rev_dir(self, current: Node) -> str:
|
||||
"""Determine the direction taken from the parent to the current node.
|
||||
|
||||
Args:
|
||||
current: Current node in the reconstructed path.
|
||||
|
||||
Returns:
|
||||
A cardinal direction letter.
|
||||
|
||||
Raises:
|
||||
Exception: If the parent-child relationship cannot be translated.
|
||||
"""
|
||||
if current.parent.coordinate == (
|
||||
current.coordinate[0],
|
||||
current.coordinate[1] - 1,
|
||||
@@ -146,7 +233,15 @@ class AStar(MazeSolver):
|
||||
else:
|
||||
raise Exception("Translate error: AStar path not found")
|
||||
|
||||
def translate(self, close: list['Node']) -> str:
|
||||
def translate(self, close: list["Node"]) -> str:
|
||||
"""Translate a node chain into a path string.
|
||||
|
||||
Args:
|
||||
close: Closed list ending with the goal node.
|
||||
|
||||
Returns:
|
||||
A string of direction letters from start to end.
|
||||
"""
|
||||
current = close[-1]
|
||||
res = ""
|
||||
while True:
|
||||
@@ -159,6 +254,17 @@ class AStar(MazeSolver):
|
||||
def solve(
|
||||
self, maze: Maze, height: int | None = None, width: int | None = None
|
||||
) -> str:
|
||||
"""Solve the maze using A*.
|
||||
|
||||
Args:
|
||||
maze: The maze to solve.
|
||||
height: Unused optional maze height.
|
||||
width: Unused optional maze width.
|
||||
|
||||
Returns:
|
||||
A string representing the path using cardinal directions.
|
||||
"""
|
||||
|
||||
maze_arr = maze.get_maze()
|
||||
if maze_arr is None:
|
||||
raise Exception("Maze is not initialized")
|
||||
@@ -167,12 +273,33 @@ class AStar(MazeSolver):
|
||||
|
||||
|
||||
class DepthFirstSearchSolver(MazeSolver):
|
||||
"""Solve a maze using depth-first search with backtracking."""
|
||||
|
||||
def __init__(self, start: tuple[int, int], end: tuple[int, int]):
|
||||
"""Initialize the depth-first search solver.
|
||||
|
||||
Args:
|
||||
start: Start coordinates using 1-based indexing.
|
||||
end: End coordinates using 1-based indexing.
|
||||
"""
|
||||
super().__init__(start, end)
|
||||
|
||||
def solve(
|
||||
self, maze: Maze, height: int | None = None, width: int | None = None
|
||||
) -> str:
|
||||
"""Solve the maze using depth-first search.
|
||||
|
||||
Args:
|
||||
maze: The maze to solve.
|
||||
height: Maze height.
|
||||
width: Maze width.
|
||||
|
||||
Returns:
|
||||
A string representing the path using cardinal directions.
|
||||
|
||||
Raises:
|
||||
Exception: If no path can be found.
|
||||
"""
|
||||
path_str = ""
|
||||
if height is None or width is None:
|
||||
raise Exception("We need Height and Width in the arg")
|
||||
@@ -207,9 +334,23 @@ class DepthFirstSearchSolver(MazeSolver):
|
||||
return path_str
|
||||
|
||||
@staticmethod
|
||||
def random_path(visited: NDArray[Any], coord: tuple[int, int],
|
||||
maze: NDArray[Any], h_w: tuple[int, int]
|
||||
) -> list[str]:
|
||||
def random_path(
|
||||
visited: NDArray[Any],
|
||||
coord: tuple[int, int],
|
||||
maze: NDArray[Any],
|
||||
h_w: tuple[int, int],
|
||||
) -> list[str]:
|
||||
"""Return all valid unvisited directions from the current cell.
|
||||
|
||||
Args:
|
||||
visited: Boolean array marking visited cells.
|
||||
coord: Current coordinate.
|
||||
maze: Maze grid to inspect.
|
||||
h_w: Tuple containing maze height and width.
|
||||
|
||||
Returns:
|
||||
A list of valid direction letters.
|
||||
"""
|
||||
random_p = []
|
||||
h, w = h_w
|
||||
y, x = coord
|
||||
@@ -229,6 +370,15 @@ class DepthFirstSearchSolver(MazeSolver):
|
||||
|
||||
@staticmethod
|
||||
def next_path(rand_path: list[str]) -> str:
|
||||
"""Select the next move at random.
|
||||
|
||||
Args:
|
||||
rand_path: List of available directions.
|
||||
|
||||
Returns:
|
||||
A randomly selected direction.
|
||||
"""
|
||||
|
||||
return random.choice(rand_path)
|
||||
|
||||
@staticmethod
|
||||
@@ -239,6 +389,19 @@ class DepthFirstSearchSolver(MazeSolver):
|
||||
h_w: tuple[int, int],
|
||||
move: list[str],
|
||||
) -> tuple[list[Any], list[Any]]:
|
||||
"""Backtrack until a cell with an unexplored path is found.
|
||||
|
||||
Args:
|
||||
path: Current path of visited coordinates.
|
||||
visited: Boolean array marking visited cells.
|
||||
maze: Maze grid to inspect.
|
||||
h_w: Tuple containing maze height and width.
|
||||
move: List of moves made so far.
|
||||
|
||||
Returns:
|
||||
A tuple containing the updated path and move list.
|
||||
"""
|
||||
|
||||
while path:
|
||||
last = path[-1]
|
||||
if DepthFirstSearchSolver.random_path(visited, last, maze, h_w):
|
||||
@@ -249,6 +412,15 @@ class DepthFirstSearchSolver(MazeSolver):
|
||||
|
||||
@staticmethod
|
||||
def next_cell(coord: tuple[int, int], next: str) -> tuple[int, int]:
|
||||
"""Return the coordinates of the next cell in the given direction.
|
||||
|
||||
Args:
|
||||
coord: Current coordinate.
|
||||
next: Direction to move.
|
||||
|
||||
Returns:
|
||||
The coordinates of the next cell.
|
||||
"""
|
||||
y, x = coord
|
||||
next_step = {"N": (-1, 0), "S": (1, 0), "W": (0, -1), "E": (0, 1)}
|
||||
add_y, add_x = next_step[next]
|
||||
|
||||
Reference in New Issue
Block a user