Compare commits

...

2 commits

Author SHA1 Message Date
609ae4fae9
remove CellInvalidWallError, more refactoring 2024-02-14 10:21:15 +00:00
98a7533de8
refactor: a bit of refactoring
- Move the custom exceptions to errors.py
- Created enums to label the cell walls. Replaces the constants defined
  in the Cell class.
2024-02-14 10:14:27 +00:00
4 changed files with 92 additions and 84 deletions

102
cell.py
View file

@ -1,5 +1,18 @@
from typing import Dict
from enum import Enum
from graphics import Window, Point, Line
import errors
class CellWallLabel(Enum):
"""
CellWallLabel is used to label a CellWall
"""
TOP = 0
BOTTOM = 1
LEFT = 2
RIGHT = 3
class CellWall:
@ -26,11 +39,6 @@ class Cell:
A Cell represents a grid on the maze.
"""
TOP_WALL = 0
BOTTOM_WALL = 1
LEFT_WALL = 2
RIGHT_WALL = 3
def __init__(
self,
x1: int, y1: int,
@ -39,13 +47,13 @@ class Cell:
) -> None:
# Validation
if (x2 < x1) or (y2 < y1):
raise CellInvalidError(x1, y1, x2, y2)
raise errors.CellInvalidError(x1, y1, x2, y2)
if (x2 - x1) < 2:
raise CellTooSmallError("horizontal", x2-x1)
raise errors.CellTooSmallError("horizontal", x2-x1)
if (y2 - y1) < 2:
raise CellTooSmallError("vertical", y2-y1)
raise errors.CellTooSmallError("vertical", y2-y1)
# Define the cell walls
top_wall = Line(Point(x1, y1), Point(x2, y1))
@ -53,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[int, CellWall] = {
Cell.TOP_WALL: CellWall(top_wall, window),
Cell.BOTTOM_WALL: CellWall(bottom_wall, window),
Cell.LEFT_WALL: CellWall(left_wall, window),
Cell.RIGHT_WALL: CellWall(right_wall, window),
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),
}
# Calculate the cell's central point
@ -80,10 +88,10 @@ class Cell:
"""
configure_walls configures the existence of the Cell's walls.
"""
self._walls[Cell.TOP_WALL].exists = top
self._walls[Cell.BOTTOM_WALL].exists = bottom
self._walls[Cell.LEFT_WALL].exists = left
self._walls[Cell.RIGHT_WALL].exists = right
self._walls[CellWallLabel.TOP].exists = top
self._walls[CellWallLabel.BOTTOM].exists = bottom
self._walls[CellWallLabel.LEFT].exists = left
self._walls[CellWallLabel.RIGHT].exists = right
def centre(self) -> Point:
"""
@ -91,9 +99,10 @@ class Cell:
"""
return self._centre
def wall_exists(self, wall: int) -> bool:
if wall not in self._walls:
raise CellInvalidWallError(wall)
def wall_exists(self, wall: CellWallLabel) -> bool:
"""
returns True if a given cell wall exists, or false otherwise.
"""
return self._walls[wall].exists
# def break_walls_r(self, i: int, j: int) -> None:
@ -110,8 +119,8 @@ class Cell:
if not self._window:
return
for _, wall in self._walls.items():
wall.draw()
for label in CellWallLabel:
self._walls[label].draw()
def draw_move(self, to_cell: 'Cell', undo: bool = False) -> None:
"""
@ -126,52 +135,3 @@ class Cell:
fill_colour = "grey"
line = Line(self.centre(), to_cell.centre())
self._window.draw_line(line, fill_colour)
class CellInvalidWallError(Exception):
"""
CellInvalidWallError is raised when the program tries to specify a Cell's
Wall that does not exist.
"""
def __init__(self, wall: int, *args):
super().__init__(args)
self.wall = wall
def __str__(self):
return f"Invalid Cell Wall (wall int: {self.wall}) specified."
class CellInvalidError(Exception):
"""
CellInvalidError is raised when the program tries to create a Cell whose
values are invalid. The values are invalid when x2 is smaller than x1
and/or y2 is smaller than y1. When creating a Cell the x and y values
should always represent the top left and the bottom right corners of
the cell (i.e. x1 < x2 and y1 < y2).
"""
def __init__(self, x1: int, y1: int, x2: int, y2: int, *args):
super().__init__(args)
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
def __str__(self):
return f"Invalid Cell values received. Please ensure that both: x1 ({self.x1}) < x2 ({self.x2}), and y1 ({self.y1}) < y2 ({self.y2})"
class CellTooSmallError(Exception):
"""
CellTooSmallError is raised when the program tries to create a Cell
which is too small to correctly draw it's central point.
"""
def __init__(self, size_type: str, size: int, *args):
super().__init__(args)
self.size_type = size_type
self.size = size
def __str__(self):
return f"The {self.size_type} size of the cell ({self.size}) is too small."

33
errors.py Normal file
View file

@ -0,0 +1,33 @@
class CellInvalidError(Exception):
"""
CellInvalidError is raised when the program tries to create a Cell whose
values are invalid. The values are invalid when x2 is smaller than x1
and/or y2 is smaller than y1. When creating a Cell the x and y values
should always represent the top left and the bottom right corners of
the cell (i.e. x1 < x2 and y1 < y2).
"""
def __init__(self, x1: int, y1: int, x2: int, y2: int, *args):
super().__init__(args)
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
def __str__(self):
return f"Invalid Cell values received. Please ensure that both: x1 ({self.x1}) < x2 ({self.x2}), and y1 ({self.y1}) < y2 ({self.y2})"
class CellTooSmallError(Exception):
"""
CellTooSmallError is raised when the program tries to create a Cell
which is too small to correctly draw it's central point.
"""
def __init__(self, size_type: str, size: int, *args):
super().__init__(args)
self.size_type = size_type
self.size = size
def __str__(self):
return f"The {self.size_type} size of the cell ({self.size}) is too small."

29
maze.py
View file

@ -39,9 +39,12 @@ class Maze:
# Create the Maze's cells
self._cells: List[List[Cell]] = [None for i in range(self._num_cell_rows)]
self._create_cells()
self._break_entrance_and_exit()
self._open_entrance_and_exit()
def _create_cells(self):
def _create_cells(self) -> None:
"""
creates all the cells and draws them.
"""
cursor_x = self._x_position
cursor_y = self._y_position
@ -66,20 +69,32 @@ class Maze:
if self._window:
self._draw_cells()
def _break_entrance_and_exit(self):
# break entrance and draw
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()
# break exit and 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 _draw_cells(self):
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):
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)

View file

@ -1,7 +1,7 @@
import unittest
import cell
from cell import Cell
from cell import Cell, CellWallLabel
from maze import Maze
import errors
class Tests(unittest.TestCase):
@ -57,10 +57,10 @@ class Tests(unittest.TestCase):
2,
2,
)
self.assertFalse(maze._cells[0][0].wall_exists(Cell.TOP_WALL))
self.assertFalse(maze._cells[0][0].wall_exists(CellWallLabel.TOP))
self.assertFalse(
maze._cells[number_of_cell_rows - 1]
[number_of_cells_per_row - 1].wall_exists(Cell.BOTTOM_WALL)
[number_of_cells_per_row - 1].wall_exists(CellWallLabel.BOTTOM)
)
def test_invalid_cell_exception(self):
@ -74,7 +74,7 @@ class Tests(unittest.TestCase):
]
for case in cases:
with self.assertRaises(cell.CellInvalidError):
with self.assertRaises(errors.CellInvalidError):
_ = Cell(
x1=case["x1"],
y1=case["y1"],
@ -93,7 +93,7 @@ class Tests(unittest.TestCase):
]
for case in cases:
with self.assertRaises(cell.CellTooSmallError):
with self.assertRaises(errors.CellTooSmallError):
_ = Cell(
x1=case["x1"],
y1=case["y1"],