add the algorithm to solve the maze
This commit is contained in:
parent
5be7f5041a
commit
758bf00451
4 changed files with 90 additions and 26 deletions
28
cell.py
28
cell.py
|
@ -4,7 +4,7 @@ from graphics import Window, Point, Line
|
|||
import errors
|
||||
|
||||
|
||||
class CellWallLabel(Enum):
|
||||
class CellWallLabels(Enum):
|
||||
"""
|
||||
CellWallLabel is used to label a CellWall
|
||||
"""
|
||||
|
@ -61,11 +61,11 @@ class Cell:
|
|||
left_wall = Line(Point(x1, y1), Point(x1, y2))
|
||||
right_wall = Line(Point(x2, y1), Point(x2, y2))
|
||||
|
||||
self._walls: Dict[CellWallLabel, CellWall] = {
|
||||
CellWallLabel.TOP: CellWall(top_wall, window),
|
||||
CellWallLabel.BOTTOM: CellWall(bottom_wall, window),
|
||||
CellWallLabel.LEFT: CellWall(left_wall, window),
|
||||
CellWallLabel.RIGHT: CellWall(right_wall, window),
|
||||
self._walls: Dict[CellWallLabels, CellWall] = {
|
||||
CellWallLabels.TOP: CellWall(top_wall, window),
|
||||
CellWallLabels.BOTTOM: CellWall(bottom_wall, window),
|
||||
CellWallLabels.LEFT: CellWall(left_wall, window),
|
||||
CellWallLabels.RIGHT: CellWall(right_wall, window),
|
||||
}
|
||||
|
||||
# Calculate the cell's central point
|
||||
|
@ -90,13 +90,13 @@ class Cell:
|
|||
configure_walls configures the existence of the Cell's walls.
|
||||
"""
|
||||
if top is not None:
|
||||
self._walls[CellWallLabel.TOP].exists = top
|
||||
self._walls[CellWallLabels.TOP].exists = top
|
||||
if bottom is not None:
|
||||
self._walls[CellWallLabel.BOTTOM].exists = bottom
|
||||
self._walls[CellWallLabels.BOTTOM].exists = bottom
|
||||
if left is not None:
|
||||
self._walls[CellWallLabel.LEFT].exists = left
|
||||
self._walls[CellWallLabels.LEFT].exists = left
|
||||
if right is not None:
|
||||
self._walls[CellWallLabel.RIGHT].exists = right
|
||||
self._walls[CellWallLabels.RIGHT].exists = right
|
||||
|
||||
def centre(self) -> Point:
|
||||
"""
|
||||
|
@ -104,10 +104,14 @@ class Cell:
|
|||
"""
|
||||
return self._centre
|
||||
|
||||
def wall_exists(self, wall: CellWallLabel) -> bool:
|
||||
def wall_exists(self, wall: CellWallLabels) -> bool:
|
||||
"""
|
||||
returns True if a given cell wall exists, or false otherwise.
|
||||
"""
|
||||
if wall not in CellWallLabels:
|
||||
raise TypeError(
|
||||
"The argument does not appear to be a valid cell wall."
|
||||
)
|
||||
return self._walls[wall].exists
|
||||
|
||||
def draw(self) -> None:
|
||||
|
@ -117,7 +121,7 @@ class Cell:
|
|||
if not self._window:
|
||||
return
|
||||
|
||||
for label in CellWallLabel:
|
||||
for label in CellWallLabels:
|
||||
self._walls[label].draw()
|
||||
|
||||
def draw_move(self, to_cell: 'Cell', undo: bool = False) -> None:
|
||||
|
|
8
main.py
8
main.py
|
@ -5,7 +5,7 @@ from maze import Maze
|
|||
def main():
|
||||
window = Window(800, 800)
|
||||
|
||||
_ = Maze(
|
||||
game = Maze(
|
||||
x_position=10,
|
||||
y_position=10,
|
||||
num_cell_rows=30,
|
||||
|
@ -15,6 +15,12 @@ def main():
|
|||
window=window
|
||||
)
|
||||
|
||||
solved = game.solve()
|
||||
if solved:
|
||||
print("Maze solved successfully :)")
|
||||
else:
|
||||
print("I'm unable to solve the maze :(")
|
||||
|
||||
window.mainloop()
|
||||
|
||||
|
||||
|
|
74
maze.py
74
maze.py
|
@ -1,9 +1,9 @@
|
|||
from typing import List
|
||||
from typing import List, Dict
|
||||
from time import sleep
|
||||
import random
|
||||
from enum import Enum
|
||||
from graphics import Window
|
||||
from cell import Cell
|
||||
from cell import Cell, CellWallLabels
|
||||
|
||||
|
||||
class MazeDirections(Enum):
|
||||
|
@ -39,7 +39,7 @@ class MazePosition:
|
|||
) -> 'MazePosition':
|
||||
if direction not in MazeDirections:
|
||||
raise TypeError(
|
||||
"The argument does not appear to be a valid MazeDirection"
|
||||
"The argument does not appear to be a valid maze direction."
|
||||
)
|
||||
|
||||
if direction is MazeDirections.ABOVE and (self.i-1 >= 0):
|
||||
|
@ -105,6 +105,8 @@ class Maze:
|
|||
self._cells: List[List[Cell]] = [
|
||||
None for i in range(self._num_cell_rows)]
|
||||
self._create_cell_grid()
|
||||
|
||||
# Open up the maze's entrance and exit.
|
||||
self._open_entrance_and_exit()
|
||||
|
||||
start_position = MazePosition(
|
||||
|
@ -114,13 +116,7 @@ class Maze:
|
|||
max_j=self._num_cells_per_row-1,
|
||||
)
|
||||
|
||||
end_position = MazePosition(
|
||||
i=self._num_cell_rows-1,
|
||||
j=self._num_cells_per_row-1,
|
||||
max_i=self._num_cell_rows-1,
|
||||
max_j=self._num_cells_per_row-1,
|
||||
)
|
||||
|
||||
# Generate the maze.
|
||||
self._break_walls_r(start_position)
|
||||
|
||||
def _create_cell_grid(self) -> None:
|
||||
|
@ -227,6 +223,59 @@ class Maze:
|
|||
|
||||
self._break_walls_r(next_position)
|
||||
|
||||
def solve(self) -> bool:
|
||||
"""
|
||||
solve attempts to solve the generated maze.
|
||||
"""
|
||||
start_position = MazePosition(
|
||||
i=0,
|
||||
j=0,
|
||||
max_i=self._num_cell_rows-1,
|
||||
max_j=self._num_cells_per_row-1,
|
||||
)
|
||||
|
||||
end_position = MazePosition(
|
||||
i=self._num_cell_rows-1,
|
||||
j=self._num_cells_per_row-1,
|
||||
max_i=self._num_cell_rows-1,
|
||||
max_j=self._num_cells_per_row-1,
|
||||
)
|
||||
|
||||
return self._solve_r(start_position, end_position)
|
||||
|
||||
def _solve_r(
|
||||
self,
|
||||
current_position: MazePosition,
|
||||
end_position: MazePosition,
|
||||
) -> bool:
|
||||
if current_position == end_position:
|
||||
return True
|
||||
current_cell = self._cells[current_position.i][current_position.j]
|
||||
current_cell.visited_by_maze_solver = True
|
||||
|
||||
wall_map: Dict[MazeDirections, CellWallLabels] = {
|
||||
MazeDirections.ABOVE: CellWallLabels.BOTTOM,
|
||||
MazeDirections.BELOW: CellWallLabels.TOP,
|
||||
MazeDirections.LEFT: CellWallLabels.RIGHT,
|
||||
MazeDirections.RIGHT: CellWallLabels.LEFT,
|
||||
}
|
||||
|
||||
for direction in MazeDirections:
|
||||
adjacent_position = current_position.get_adjacent_position(
|
||||
direction)
|
||||
if adjacent_position is None:
|
||||
continue
|
||||
adjacent_cell = self._cells[adjacent_position.i][adjacent_position.j]
|
||||
if adjacent_cell.visited_by_maze_solver or adjacent_cell.wall_exists(wall_map[direction]):
|
||||
continue
|
||||
self._draw_path(current_cell, adjacent_cell)
|
||||
result = self._solve_r(adjacent_position, end_position)
|
||||
if result is True:
|
||||
return True
|
||||
self._draw_path(current_cell, adjacent_cell, undo=True)
|
||||
|
||||
return False
|
||||
|
||||
def _draw_cell(self, i: int, j: int) -> None:
|
||||
"""
|
||||
_draw_cell draws the cells in an animated way.
|
||||
|
@ -234,3 +283,8 @@ class Maze:
|
|||
self._cells[i][j].draw()
|
||||
self._window.redraw()
|
||||
sleep(0.05)
|
||||
|
||||
def _draw_path(self, current_cell: Cell, next_cell: Cell, undo: bool = False) -> None:
|
||||
current_cell.draw_move(next_cell, undo)
|
||||
self._window.redraw()
|
||||
sleep(0.05)
|
||||
|
|
6
tests.py
6
tests.py
|
@ -1,5 +1,5 @@
|
|||
import unittest
|
||||
from cell import Cell, CellWallLabel
|
||||
from cell import Cell, CellWallLabels
|
||||
import maze
|
||||
import errors
|
||||
|
||||
|
@ -65,10 +65,10 @@ class Tests(unittest.TestCase):
|
|||
None,
|
||||
None,
|
||||
)
|
||||
self.assertFalse(m._cells[0][0].wall_exists(CellWallLabel.TOP))
|
||||
self.assertFalse(m._cells[0][0].wall_exists(CellWallLabels.TOP))
|
||||
self.assertFalse(
|
||||
m._cells[number_of_cell_rows - 1]
|
||||
[number_of_cells_per_row - 1].wall_exists(CellWallLabel.BOTTOM)
|
||||
[number_of_cells_per_row - 1].wall_exists(CellWallLabels.BOTTOM)
|
||||
)
|
||||
|
||||
def test_invalid_cell_exception(self):
|
||||
|
|
Loading…
Reference in a new issue