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.
This commit is contained in:
Dan Anglin 2024-02-14 10:14:27 +00:00
parent 79d9727273
commit 98a7533de8
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
4 changed files with 104 additions and 80 deletions

96
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:
"""
@ -92,8 +100,11 @@ class Cell:
return self._centre
def wall_exists(self, wall: int) -> bool:
"""
returns True if a given cell wall exists, or false otherwise.
"""
if wall not in self._walls:
raise CellInvalidWallError(wall)
raise errors.CellInvalidWallError(wall)
return self._walls[wall].exists
# def break_walls_r(self, i: int, j: int) -> None:
@ -126,52 +137,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."

47
errors.py Normal file
View file

@ -0,0 +1,47 @@
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."

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"],