Dan Anglin
fa75e61acf
Update the DFS algorithm to allow the user to select if the solver should randomly choose the directions when traversing through the maze. The duplicate method has been removed.
172 lines
5.8 KiB
Python
172 lines
5.8 KiB
Python
from typing import Dict, Callable, List
|
|
import random
|
|
from maze import Maze, MazeDirection, MazePosition
|
|
from cell import CellWallLabels
|
|
|
|
|
|
class Solver:
|
|
def __init__(self, game: Maze):
|
|
self._game = game
|
|
self._solver = "solver"
|
|
|
|
# This is a dictionary mapping the direction to the maze to the
|
|
# wall of the adjacent cell. It is used to identify the wall that could
|
|
# potentially block the solver's path.
|
|
# For example if the solver wants to move to the right, it's path
|
|
# could be blocked by the adjacent cell's left wall.
|
|
self._wall_map: Dict[MazeDirection, CellWallLabels] = {
|
|
MazeDirection.ABOVE: CellWallLabels.BOTTOM,
|
|
MazeDirection.BELOW: CellWallLabels.TOP,
|
|
MazeDirection.LEFT: CellWallLabels.RIGHT,
|
|
MazeDirection.RIGHT: CellWallLabels.LEFT,
|
|
}
|
|
|
|
def solve(
|
|
self,
|
|
solve_method: Callable[[MazePosition, MazePosition, bool], bool],
|
|
enable_random_direction: bool = False,
|
|
) -> bool:
|
|
"""
|
|
solve attempts to solve the generated maze.
|
|
"""
|
|
start_position = MazePosition(
|
|
i=0,
|
|
j=0,
|
|
last_i=self._game.get_last_i(),
|
|
last_j=self._game.get_last_j(),
|
|
)
|
|
|
|
end_position = MazePosition(
|
|
i=self._game.get_last_i(),
|
|
j=self._game.get_last_j(),
|
|
last_i=self._game.get_last_i(),
|
|
last_j=self._game.get_last_j(),
|
|
)
|
|
|
|
return solve_method(start_position, end_position, enable_random_direction)
|
|
|
|
def solve_with_dfs_r(
|
|
self,
|
|
current_position: MazePosition,
|
|
end_position: MazePosition,
|
|
enable_random_direction: bool = False,
|
|
) -> bool:
|
|
if current_position == end_position:
|
|
return True
|
|
|
|
self._game.mark_cell_as_visited(
|
|
i=current_position.i,
|
|
j=current_position.j,
|
|
visitor=self._solver,
|
|
)
|
|
|
|
if enable_random_direction:
|
|
random.seed()
|
|
|
|
while True:
|
|
possible_directions: List[MazeDirection] = []
|
|
for direction in MazeDirection:
|
|
adjacent_position = current_position.get_adjacent_position(
|
|
direction,
|
|
)
|
|
if adjacent_position is None:
|
|
continue
|
|
if self._game.cell_was_visited_by(
|
|
i=adjacent_position.i,
|
|
j=adjacent_position.j,
|
|
visitor=self._solver,
|
|
) or self._game.cell_wall_exists(
|
|
i=adjacent_position.i,
|
|
j=adjacent_position.j,
|
|
wall=self._wall_map[direction],
|
|
):
|
|
continue
|
|
possible_directions.append(direction)
|
|
if len(possible_directions) == 0:
|
|
break
|
|
next_direction = None
|
|
if len(possible_directions) == 1 or not enable_random_direction:
|
|
next_direction = possible_directions[0]
|
|
else:
|
|
next_direction = random.choice(possible_directions)
|
|
next_position = current_position.get_adjacent_position(
|
|
next_direction,
|
|
)
|
|
self._game.draw_path_between(current_position, next_position)
|
|
solved = self.solve_with_dfs_r(
|
|
next_position, end_position)
|
|
if solved:
|
|
return True
|
|
self._game.draw_path_between(
|
|
current_position,
|
|
next_position,
|
|
undo=True
|
|
)
|
|
|
|
return False
|
|
|
|
def solve_with_bfs_r(
|
|
self,
|
|
current_position: MazePosition,
|
|
end_position: MazePosition,
|
|
enable_random_direction: bool = False,
|
|
) -> bool:
|
|
self._game.mark_cell_as_visited(
|
|
i=current_position.i,
|
|
j=current_position.j,
|
|
visitor=self._solver,
|
|
)
|
|
|
|
if enable_random_direction:
|
|
random.seed()
|
|
|
|
while True:
|
|
possible_directions = []
|
|
for direction in MazeDirection:
|
|
adjacent_position = current_position.get_adjacent_position(
|
|
direction,
|
|
)
|
|
|
|
if adjacent_position is None:
|
|
continue
|
|
if self._game.cell_was_visited_by(
|
|
i=adjacent_position.i,
|
|
j=adjacent_position.j,
|
|
visitor=self._solver,
|
|
) or self._game.cell_wall_exists(
|
|
i=adjacent_position.i,
|
|
j=adjacent_position.j,
|
|
wall=self._wall_map[direction],
|
|
):
|
|
continue
|
|
if adjacent_position == end_position:
|
|
self._game.draw_path_between(
|
|
current_position, adjacent_position)
|
|
return True
|
|
|
|
possible_directions.append(direction)
|
|
if len(possible_directions) == 0:
|
|
break
|
|
next_direction = None
|
|
if len(possible_directions) == 1 or not enable_random_direction:
|
|
next_direction = possible_directions[0]
|
|
else:
|
|
next_direction = random.choice(possible_directions)
|
|
next_position = current_position.get_adjacent_position(
|
|
next_direction,
|
|
)
|
|
self._game.draw_path_between(current_position, next_position)
|
|
solved = self.solve_with_bfs_r(
|
|
next_position,
|
|
end_position,
|
|
enable_random_direction
|
|
)
|
|
if solved:
|
|
return True
|
|
self._game.draw_path_between(
|
|
current_position,
|
|
next_position,
|
|
undo=True
|
|
)
|
|
|
|
return False
|