Search code examples
pythonpython-3.xpygamemaze

Pygame Maze Game not creating levels correctly


So I'm trying to create a maze game with levels for a project at school. The code is a bit repetitive sorry I've only just started coding using pygame. When run the program should output a maze that once the user completes moves onto the next level - each level is randomly generated. However, only the first level is showing properly the rest of the levels appear to be a grid- which is making me think that the game is creating a new maze over the old.

I've pasted the code below - feel free to leave any advice on how to improve what I have :)

class Maze:

  def __init__(self, rows=30, cols=40):

    self.rows = rows
    self.cols = cols
    self.keep_going = 1

    self.maze = {}
    for y in range(rows):
      for x in range(cols):
        cell = {'south' : 1, 'east' : 1, 'visited': 0}
        self.maze[(x,y)] = cell

  def generate(self, start_cell=None, stack=[])

    if start_cell is None:
      start_cell = self.maze[(self.cols-1, self.rows-1)]

    if not self.keep_going:
      return

    self.check_finished()
    neighbors = []

    # if the stack is empty, add the start cell
    if len(stack) == 0:
      stack.append(start_cell)

    # set current cell to last cell
    curr_cell = stack[-1]

    # get neighbors and shuffle 'em up a bit
    neighbors = self.get_neighbors(curr_cell)
    shuffle(neighbors)

    for neighbor in neighbors:
      if neighbor['visited'] == 0:
        neighbor['visited'] = 1
        stack.append(neighbor)
        self.knock_wall(curr_cell, neighbor)

        self.generate(start_cell, stack)

  def get_coords(self, cell):
    # grabs coords of a given cell
    coords = (-1, -1)
    for k in self.maze:
      if self.maze[k] is cell:
        coords = (k[0], k[1])
        break
    return coords

  def get_neighbors(self, cell):
    # obvious
    neighbors = []

    (x, y) = self.get_coords(cell)
    if (x, y) == (-1, -1):
      return neighbors

    north = (x, y-1)
    south = (x, y+1)
    east = (x+1, y)
    west = (x-1, y)

    if north in self.maze:
      neighbors.append(self.maze[north])
    if south in self.maze:
      neighbors.append(self.maze[south])
    if east in self.maze:
      neighbors.append(self.maze[east])
    if west in self.maze:
      neighbors.append(self.maze[west])

    return neighbors

  def knock_wall(self, cell, neighbor):
    # knocks down wall between cell and neighbor.
    xc, yc = self.get_coords(cell)
    xn, yn = self.get_coords(neighbor)

    # Which neighbor?
    if xc == xn and yc == yn + 1:
      # neighbor's above, knock out south wall of neighbor
      neighbor['south'] = 0
    elif xc == xn and yc == yn - 1:
      # neighbor's below, knock out south wall of cell
      cell['south'] = 0
    elif xc == xn + 1 and yc == yn:
      # neighbor's left, knock out east wall of neighbor
      neighbor['east'] = 0
    elif xc == xn - 1 and yc == yn:
      # neighbor's right, knock down east wall of cell
      cell['east'] = 0

  def check_finished(self):
    # Checks if we're done generating
    done = 1
    for k in self.maze:
      if self.maze[k]['visited'] == 0:
        done = 0
        break
    if done:
      self.keep_going = 0

Solution

  • [...] the rest of the levels appear to be a grid- which is making me think that the game is creating a new maze over the old.

    The issue is caused by a common mistake in Python.

    See Default Argument Values

    Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes

    In your case the arguments to the method generate of class Maze has default arguments:

    class Maze:
       # [...]
    
       def generate(self, start_cell=None, stack=[]):
           # [...]
    

    In the method generate elements are append to stack. The maze is generated trusting in the default argument:

    self.maze_obj.generate(self.maze_obj.maze[(0,0)])
    

    That causes that the 1st generation of the mace succeeds, but the following generation fails, because stack contains all the elements of the former generation process.

    Pass an empty list to generate to solve the issue:

    self.maze_obj.generate(self.maze_obj.maze[(0,0)])

    self.maze_obj.generate(self.maze_obj.maze[(0,0)], [])
    

    Or change the default argument to None:

    class Maze:
        # [...]
    
        def generate(self, start_cell=None, stack=None):
            if stack == None:
                stack = []