import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
import pygame
import random
from abc import abstractmethod
class Cell:
NUMBER_OF_CELLS = 0
def __init__(self):
self.id = Cell.NUMBER_OF_CELLS
Cell.NUMBER_OF_CELLS = 1
self.top_cell = None
self.bottom_cell = None
self.left_cell = None
self.right_cell = None
self.top_wall = True
self.bottom_wall = True
self.left_wall = True
self.right_wall = True
self.visited = False
def set_visited(self, visited: bool) -> None:
"""Sets the visited status of the cell
Args:
visited (bool): The visited status to set
"""
self.visited = visited
def has_unvisited_neighbors(self) -> bool:
"""Check if all neighbors are visited or not
Returns:
bool: True if there is at least one, else False
"""
return (
self.top_cell is not None
and not self.top_cell.visited
or self.bottom_cell is not None
and not self.bottom_cell.visited
or self.left_cell is not None
and not self.left_cell.visited
or self.right_cell is not None
and not self.right_cell.visited
)
def get_unvisited_neighbors(self) -> list["Cell"]:
"""Get all univisited neighbors of the cell (top, bottom, left and right)
Returns:
list["Cell"]: List of unvisited neighbors (cells)
"""
neighbors = []
if self.top_cell is not None and not self.top_cell.visited:
neighbors.append(self.top_cell)
if self.bottom_cell is not None and not self.bottom_cell.visited:
neighbors.append(self.bottom_cell)
if self.left_cell is not None and not self.left_cell.visited:
neighbors.append(self.left_cell)
if self.right_cell is not None and not self.right_cell.visited:
neighbors.append(self.right_cell)
return neighbors
def open_wall_with(self, cell: "Cell") -> None:
"""Opens the wall in the given direction for both cells (method called and parameter one)
Args:
cell (Cell): The cell to open the wall with
"""
direction = self.get_direction(cell)
if direction == "top":
self.top_wall = False
self.top_cell.bottom_wall = False
elif direction == "bottom":
self.bottom_wall = False
self.bottom_cell.top_wall = False
elif direction == "left":
self.left_wall = False
self.left_cell.right_wall = False
elif direction == "right":
self.right_wall = False
self.right_cell.left_wall = False
def get_direction(self, cell: "Cell") -> str:
"""Gets the direction to the given cell from this cell
Args:
cell (Cell): The cell to get the direction to
Returns:
str: The direction to the given cell or empty string if not found
"""
if self.top_cell is cell:
return "top"
elif self.bottom_cell is cell:
return "bottom"
elif self.left_cell is cell:
return "left"
elif self.right_cell is cell:
return "right"
else:
return ""
def __str__(self):
tmp = ""
tmp = "1" if self.top_wall else "0"
tmp = "1" if self.bottom_wall else "0"
tmp = "1" if self.left_wall else "0"
tmp = "1" if self.right_wall else "0"
return f" {tmp} "
class Maze:
def __init__(self, width: int, height: int):
self.width = width
self.height = height
self.cells = [[Cell() for _ in range(height)] for _ in range(width)]
self.link_cells()
def __str__(self):
s = ""
for i in range(self.height):
for j in range(self.width):
s = str(self.cells[j][i])
s = "\n"
return s
def link_cells(self) -> None:
"""Links all cells recursively"""
for i in range(self.width):
for j in range(self.height):
cell = self.cells[i][j]
if j > 0:
cell.top_cell = self.cells[i][j - 1]
if j < self.height - 1:
cell.bottom_cell = self.cells[i][j 1]
if i > 0:
cell.left_cell = self.cells[i - 1][j]
if i < self.width - 1:
cell.right_cell = self.cells[i 1][j]
class MazeBuilder:
ALGORITHMS = []
def __init__(self, num: int):
MazeBuilder.ALGORITHMS = [
method_name
for method_name in dir(self)
if callable(getattr(self, method_name)) and not method_name.startswith("__")
and method_name != "build"
]
self.algorithm = MazeBuilder.ALGORITHMS[num]
def build(self, maze: Maze) -> None:
self.algorithm(maze)
@staticmethod
def depth_first_search(maze: Maze) -> None:
"""Depth First Search algorithm (iterative, cuz the recursive one overflows the stack)
Args:
maze (Maze): An untouched maze object to be built upon
"""
maze.cells[0][0].set_visited(True)
stack = [maze.cells[0][0]]
while len(stack) != 0:
current_cell = stack.pop()
if current_cell.has_unvisited_neighbors():
stack.append(current_cell)
chosen_cell = random.choice(current_cell.get_unvisited_neighbors())
current_cell.open_wall_with(chosen_cell)
chosen_cell.set_visited(True)
stack.append(chosen_cell)
class MazeSolver:
ALGORITHMS = []
def __init__(self, num: int):
MazeSolver.ALGORITHMS = [
method_name
for method_name in dir(self)
if callable(getattr(self, method_name)) and not method_name.startswith("__")
and method_name != "solve"
]
self.algorithm = MazeSolver.ALGORITHMS[num]
def solve(self, maze: Maze) -> None:
self.algorithm(maze)
@staticmethod
def a_star(maze: Maze) -> None:
"""Depth First Search algorithm (iterative, cuz the recursive one overflows the stack)
Args:
maze (Maze): An untouched maze object to be built upon
"""
pass
class Color:
WHITE = pygame.Color("white")
BLACK = pygame.Color("black")
RED = pygame.Color("red")
BLUE = pygame.Color("blue")
class Window:
FPS = 60
WIDTH = 1280
HEIGHT = 720
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((Window.WIDTH, Window.HEIGHT))
self.screen.fill(Color.WHITE)
pygame.display.set_caption("MazeBuild&Solve")
self.run = False
self.clock = pygame.time.Clock()
@abstractmethod
def start(self):
raise NotImplementedError("Not implemented abstract method for object type 'Window'")
class MazeDrawer(Window):
def __init__(self, alg_gen: int, alg_sol: int, width: int, height: int):
super().__init__()
self.maze_builder = MazeBuilder(alg_gen)
self.maze_solver = MazeSolver(alg_sol)
self.maze = Maze(width, height)
def _draw_line(self, start_coords: tuple, end_coords: tuple):
pygame.draw.line(self.screen, Color.BLACK, start_coords, end_coords)
def _draw_maze(self) -> None:
"""Draw the maze on pygame's window"""
pass
def start(self):
self.run = True
while self.run:
# Setting the tick to Window.FPS (default: 60)
self.clock.tick(Window.FPS)
# Looking for any event
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.run = False
self._draw_maze()
pygame.display.update()
pygame.display.flip()
def main():
_ = MazeBuilder(0) # Needed to init the MazeBuilder.ALGORITHMS list
_ = MazeSolver(0) # Needed to init the MazeSolver.ALGORITHMS list
alg_gen, alg_sol, width, height = -1, -1, -1, -1
for i in range(len(MazeBuilder.ALGORITHMS)):
print(f"{i}: {MazeBuilder.ALGORITHMS[i]}")
print()
while(alg_gen := int(input("Input the n° of the algorithm for the generation: ")) not in range(len(MazeBuilder.ALGORITHMS))):
continue
print()
for i in range(len(MazeSolver.ALGORITHMS)):
print(f"{i}: {MazeSolver.ALGORITHMS[i]}")
print()
while(alg_sol := int(input("Input the n° of the algorithm for the solving: ")) not in range(len(MazeSolver.ALGORITHMS))):
continue
print()
while(width := int(input("Width of the maze: \t")) not in range(1000)):
continue
print()
while(height := int(input("Height of the maze: \t")) not in range(1000)):
continue
m = MazeDrawer(alg_gen, alg_sol, width, height)
print(m.maze)
m.start()
if __name__ == "__main__":
main()
I'm having trouble with the last instruction of my code. Well, I don't understand why, but my print(m.maze)
function prints me nothing and all attributs of the maze are False
. What is happening? I tried everything but I couldn't find the issue.
As you might have noticed, I'm doing a maze builder and solver. However, when the Maze object is created from the user's input, the maze goes empty for no reason...
CodePudding user response:
Your big problem is parentheses:
while(alg_gen := int(input("Input the n° of the algorithm for the generation: ")) not in range(len(MazeBuilder.ALGORITHMS))):
Because of the way you have written it, alg_gen
is going to be assigned the result of the not in
expression, which is False
. Thus, what you're passing to the Maze
constructor is False
xFalse
, and it happily creates a 0x0 maze.
You need:
while((alg_gen := int(input("Input the n° of the algorithm for the generation: "))) not in range(len(MazeBuilder.ALGORITHMS))):
and the same in the rest of the inputs. That's the danger with getting too clever. This probably would be clearer without the walrus:
while True:
alg_gen = int(input(...))
if alg_gen in range(len(MazeBuilder.ALGORITHMS)):
break