from typing import Dict from graphics import Window, Point, Line class CellWall: """ A CellWall represents the existence (or non-existence) of a Cell's wall. """ def __init__(self, line: Line, window: Window) -> None: self.exists = True self.line = line self._window = window def draw(self): fill_colour = self._window.cell_grid_colour if not self.exists: fill_colour = self._window.background_colour self._window.draw_line(self.line, fill_colour=fill_colour) 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, x2: int, y2: int, window: Window = None, ) -> None: # Validation if (x2 < x1) or (y2 < y1): raise CellInvalidError(x1, y1, x2, y2) if (x2 - x1) < 2: raise CellTooSmallError("horizontal", x2-x1) if (y2 - y1) < 2: raise CellTooSmallError("vertical", y2-y1) # Define the cell walls top_wall = Line(Point(x1, y1), Point(x2, y1)) bottom_wall = Line(Point(x1, y2), Point(x2, y2)) 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), } # Calculate the cell's central point centre_x = x1 + ((x2 - x1) / 2) centre_y = y1 + ((y2 - y1) / 2) self._centre = Point(centre_x, centre_y) # A reference to the root Window class for drawing purposes. self._window = window self.visited = False def configure_walls( self, top: bool = True, bottom: bool = True, left: bool = True, right: bool = True, ) -> None: """ 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 def centre(self) -> Point: """ centre returns the Cell's central point """ return self._centre def wall_exists(self, wall: int) -> bool: if wall not in self._walls: raise CellInvalidWallError(wall) return self._walls[wall].exists # def break_walls_r(self, i: int, j: int) -> None: # self.visited = True # while True: # list_i = [] # list_j = [] # break def draw(self) -> None: """ draw draws the cell onto the canvas """ if not self._window: return for _, wall in self._walls.items(): wall.draw() def draw_move(self, to_cell: 'Cell', undo: bool = False) -> None: """ draw_move draws a path between the centre of this cell and the centre of the given cell. """ if not self._window: return fill_colour = "red" if undo: 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."