Dan Anglin
27cdeaf7f4
Don't run self._break_walls_r() if the window is not set for now for testing purposes.
149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
from typing import List
|
|
from time import sleep
|
|
import random
|
|
from graphics import Window
|
|
from cell import Cell
|
|
|
|
|
|
class Maze:
|
|
"""
|
|
Maze represents a two-dimensional grid of Cells.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
x_position: int,
|
|
y_position: int,
|
|
num_cell_rows: int,
|
|
num_cells_per_row: int,
|
|
cell_size_x: int,
|
|
cell_size_y: int,
|
|
window: Window = None,
|
|
seed=None,
|
|
) -> None:
|
|
self._x_position = x_position
|
|
self._y_position = y_position
|
|
self._num_cell_rows = num_cell_rows
|
|
self._num_cells_per_row = num_cells_per_row
|
|
self._cell_size_x = cell_size_x
|
|
self._cell_size_y = cell_size_y
|
|
self._window = window
|
|
|
|
# initialise the random number generator
|
|
random.seed(seed)
|
|
|
|
# Create the Maze's cells
|
|
self._cells: List[List[Cell]] = [None for i in range(self._num_cell_rows)]
|
|
self._create_cells()
|
|
self._open_entrance_and_exit()
|
|
if self._window:
|
|
self._break_walls_r(0, 0)
|
|
|
|
def _create_cells(self) -> None:
|
|
"""
|
|
creates all the cells and draws them.
|
|
"""
|
|
cursor_x = self._x_position
|
|
cursor_y = self._y_position
|
|
|
|
for i in range(self._num_cell_rows):
|
|
cells: List[Cell] = [None for j in range(self._num_cells_per_row)]
|
|
for j in range(self._num_cells_per_row):
|
|
cell = Cell(
|
|
cursor_x,
|
|
cursor_y,
|
|
(cursor_x + self._cell_size_x),
|
|
(cursor_y + self._cell_size_y),
|
|
self._window
|
|
)
|
|
cells[j] = cell
|
|
if j == self._num_cells_per_row - 1:
|
|
cursor_x = self._x_position
|
|
else:
|
|
cursor_x += self._cell_size_x
|
|
self._cells[i] = cells
|
|
cursor_y += self._cell_size_y
|
|
|
|
if self._window:
|
|
self._draw_cells()
|
|
|
|
def _open_entrance_and_exit(self) -> None:
|
|
"""
|
|
opens the maze's entrance and exit cells by breaking their respective
|
|
walls. The entrance is located at the top left and the exit is located
|
|
at the bottom right of the maze.
|
|
"""
|
|
self._cells[0][0].configure_walls(top=False)
|
|
self._cells[0][0].draw()
|
|
|
|
self._cells[self._num_cell_rows-1][self._num_cells_per_row-1].configure_walls(bottom=False)
|
|
self._cells[self._num_cell_rows-1][self._num_cells_per_row-1].draw()
|
|
|
|
def _break_walls_r(self, y: int, x: int) -> None:
|
|
current_cell = self._cells[y][x]
|
|
current_cell.visited = True
|
|
above, below, left, right = "above", "below", "left", "right"
|
|
|
|
while True:
|
|
adjacent_cells = {
|
|
above: (y-1, x),
|
|
below: (y+1, x),
|
|
left: (y, x-1),
|
|
right: (y, x+1),
|
|
}
|
|
to_visit: List[str] = []
|
|
|
|
for k, value in adjacent_cells.items():
|
|
if (value[0] < 0)or (value[1] < 0) or (value[0] > self._num_cell_rows-1) or (value[1] > self._num_cells_per_row-1):
|
|
continue
|
|
if self._cells[value[0]][value[1]].visited:
|
|
continue
|
|
|
|
to_visit.append(k)
|
|
|
|
if len(to_visit) == 0:
|
|
current_cell.draw()
|
|
break
|
|
|
|
next_direction = random.choice(to_visit)
|
|
next_cell = self._cells[adjacent_cells[next_direction][0]][adjacent_cells[next_direction][1]]
|
|
|
|
if next_direction is above:
|
|
current_cell.configure_walls(top=False)
|
|
next_cell.configure_walls(bottom=False)
|
|
elif next_direction is below:
|
|
current_cell.configure_walls(bottom=False)
|
|
next_cell.configure_walls(top=False)
|
|
elif next_direction is left:
|
|
current_cell.configure_walls(left=False)
|
|
next_cell.configure_walls(right=False)
|
|
elif next_direction is right:
|
|
current_cell.configure_walls(right=False)
|
|
next_cell.configure_walls(left=False)
|
|
|
|
current_cell.draw()
|
|
next_cell.draw()
|
|
self._animate()
|
|
|
|
self._break_walls_r(
|
|
adjacent_cells[next_direction][0],
|
|
adjacent_cells[next_direction][1],
|
|
)
|
|
|
|
def _draw_cells(self) -> None:
|
|
"""
|
|
draws all the cells on the maze with a short pause between each cell
|
|
for animation purposes.
|
|
"""
|
|
for i in range(self._num_cell_rows):
|
|
for j in range(self._num_cells_per_row):
|
|
self._cells[i][j].draw()
|
|
self._animate()
|
|
|
|
def _animate(self) -> None:
|
|
"""
|
|
redraws the application and pauses for a short period of time to
|
|
provide an animation effect.
|
|
"""
|
|
self._window.redraw()
|
|
sleep(0.05)
|