Difference between revisions of "Maze.py"

From PeformIQ Upgrade
Jump to navigation Jump to search
(New page: =maze.py= A python implementation of a maze generator. Shows the use of classes in Python ==Script== <pre> #!/usr/bin/env python # $Id: maze.py,v 1.2 2000/12/12 19:45:01 tlavoie Exp t...)
 
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
=maze.py=
=Overview=


A python implementation of a maze generator.
A Python implementation of a maze generator which demonstrates the use of classes.
 
Shows the use of classes in Python


==Script==
==Script==
Line 9: Line 7:
<pre>
<pre>
#!/usr/bin/env python
#!/usr/bin/env python
 
#
# $Id: maze.py,v 1.2 2000/12/12 19:45:01 tlavoie Exp tlavoie $
# $Id: maze.py,v 1.2 2000/12/12 19:45:01 tlavoie Exp tlavoie $
# $Log: maze.py,v $
# $Log: maze.py,v $
Line 15: Line 13:
# Last save before changing self.cells from a list to a dictionary.
# Last save before changing self.cells from a list to a dictionary.
#
#
#--------------------------------------------------------------------------


import sys, random
import sys, random
#--------------------------------------------------------------------------


def debug(x):
def debug(x):
      pass
  pass
    #print 'DEBUG: ' + `x`
  #print 'DEBUG: ' + `x`
    #sys.stdout.flush()
  #sys.stdout.flush()


#--------------------------------------------------------------------------


class MazeCell:
class MazeCell:
    "Class which defines a single cell in an n-dimensional maze"
  "Class which defines a single cell in an n-dimensional maze"


    def __init__ (self, size=4):
  #-----------------------------------------------------------------------
        self.numNeighbours = size  # will be changed when initialized by maze routine
        self.currState = 0        # 0 is out, 1 is frontier, 2 is in.  This is used for Prim's algorithm.
        self.myNeighbours = []
        self.myWalls = {}


    def stateFrontier(self):
  def __init__ (self, size=4):
        if(self.currState == 0):
      self.numNeighbours = size  #  Will be changed when initialized by maze routine
            self.currState = 1
      self.currState = 0        #  0 is out, 1 is frontier, 2 is in.
            return(1)  # used to decide whether or not maze code should increment  frontier counter.
                                # This is used for Prim's algorithm.
        else:
      self.myNeighbours = []
            return(None)
      self.myWalls = {}


  #-----------------------------------------------------------------------


    def stateIn(self):
  def stateFrontier(self):
        self.currState = 2
      if(self.currState == 0):
        #  Incrementing "out" neighbours to "frontier" state will be handled by maze code, not here.
        self.currState = 1
        return(1)              #  Used to decide whether or not maze code
                                #  should increment  frontier counter.
      else:
        return(None)
 
  #-----------------------------------------------------------------------
 
  def stateIn(self):
      self.currState = 2
      #  Incrementing "out" neighbours to "frontier" state will
      #  be handled by maze code, not here.
 
#--------------------------------------------------------------------------


class PrimMaze:
class PrimMaze:
    "Creates a maze using Prim's algorithm"
  "Creates a maze using Prim's algorithm"
    def __init__(self):
 
        self.frontiers = {}  # keep a dict of current frontier cells.
  #-----------------------------------------------------------------------
        self.cells = {}         # Store cell data here...
 
        self.opposites = {}       # create a list of "return" paths since cell walls map both ways.
  def __init__(self):
        self.height = 0
      self.frontiers = {}  # keep a dict of current frontier cells.
        self.width = 0
      self.cells = {}       # Store cell data here...
      self.opposites = {}     # create a list of "return" paths since cell walls map both ways.
      self.height = 0
      self.width = 0
 
  #-----------------------------------------------------------------------
 
  def genPrim(self):
      currCell = random.randint(0,len(self.cells))
      self.cells[currCell].stateIn()
 
      debug('cell ' + `currCell` + ' now IN')
 
      for i in range(self.cells[0].numNeighbours):
        thisNeigh = self.cells[currCell].myNeighbours[i]
 
        if(thisNeigh != -1):
            if(self.cells[thisNeigh].stateFrontier() == 1):
              self.frontiers[thisNeigh] = 1
              debug('cell ' + `thisNeigh` + ' now FRONTIER')
 
      #  Now, if there are any frontier cells, pick one, give it a path
      #  to an 'IN' cell,  and make it IN also.
 
      while(len(self.frontiers) > 0):
        debug('frontier cells: ' + `self.frontiers`)
        nextCell = self.frontiers.keys()[random.randint(0,len(self.frontiers.keys()) -1)]


    def genPrim(self):
        debug('cell ' + `nextCell` + ' is NEXT')
        currCell = random.randint(0,len(self.cells))
 
        self.cells[currCell].stateIn()
        foundIn = 0
        debug('cell ' + `currCell` + ' now IN')
 
        for i in range(self.cells[0].numNeighbours):
        # Now, pick a neighbour who's IN, at random
 
        while(foundIn == 0):
            myPalRef = random.randint(0, len(self.cells[nextCell].myNeighbours) - 1)
            myPal = self.cells[nextCell].myNeighbours[myPalRef]
 
            if (myPal != -1):
              debug('cell ' + `myPal` + ' is my pal')
              if(self.cells[myPal].currState == 2):
                  foundIn = 1
 
        # OK, we've got a route out to the existing maze. Move own state to IN
 
        self.cells[nextCell].stateIn()
 
        # break down walls
        # debug('BAD WALL: ' + `self.cells[nextCell].myWalls[myPal]`)
 
        debug('BAD WALL1: ' + `myPalRef`)
        debug('BAD WALL2: ' + `self.opposites[myPalRef]`)
        debug('nextCell walls: ' + `self.cells[nextCell].myWalls`)
 
        del self.cells[nextCell].myWalls[myPalRef]
        del self.cells[myPal].myWalls[self.opposites[myPalRef]]
 
        # update OUT neighbours to FRONTIER
 
        currCell = nextCell
 
        for i in range(self.cells[0].numNeighbours):
             thisNeigh = self.cells[currCell].myNeighbours[i]
             thisNeigh = self.cells[currCell].myNeighbours[i]
             if(thisNeigh != -1):
             if(thisNeigh != -1):
                if(self.cells[thisNeigh].stateFrontier() == 1):
              if(self.cells[thisNeigh].stateFrontier() == 1):
                    self.frontiers[thisNeigh] = 1
                  self.frontiers[thisNeigh] = 1
                    debug('cell ' + `thisNeigh` + ' now FRONTIER')
                  debug('cell ' + `thisNeigh` + ' now FRONTIER')


        # Now, if there are any frontier cells, pick one, give it a path to an 'IN' cell,  and make it IN also.
        # Remove currCell from frontiers{}
        while(len(self.frontiers) > 0):
            debug('frontier cells: ' + `self.frontiers`)
            nextCell = self.frontiers.keys()[random.randint(0,len(self.frontiers.keys()) -1)]
            debug('cell ' + `nextCell` + ' is NEXT')
            foundIn = 0
            # Now, pick a neighbour who's IN, at random
            while(foundIn == 0):
                myPalRef = random.randint(0, len(self.cells[nextCell].myNeighbours) - 1)
                myPal = self.cells[nextCell].myNeighbours[myPalRef]
                if (myPal != -1):
                    debug('cell ' + `myPal` + ' is my pal')
                    if(self.cells[myPal].currState == 2):
                        foundIn = 1


          # OK, we've got a route out to the existing maze. Move own state to IN
        del self.frontiers[currCell]
            self.cells[nextCell].stateIn()
            # break down walls
            # debug('BAD WALL: ' + `self.cells[nextCell].myWalls[myPal]`)
            debug('BAD WALL1: ' + `myPalRef`)
            debug('BAD WALL2: ' + `self.opposites[myPalRef]`)
            debug('nextCell walls: ' + `self.cells[nextCell].myWalls`)
            del self.cells[nextCell].myWalls[myPalRef]
            del self.cells[myPal].myWalls[self.opposites[myPalRef]]


            # update OUT neighbours to FRONTIER
  #-----------------------------------------------------------------------
            currCell = nextCell
            for i in range(self.cells[0].numNeighbours):
                thisNeigh = self.cells[currCell].myNeighbours[i]
                if(thisNeigh != -1):
                    if(self.cells[thisNeigh].stateFrontier() == 1):
                        self.frontiers[thisNeigh] = 1
                        debug('cell ' + `thisNeigh` + ' now FRONTIER')


            # remove currCell from frontiers{}
  def ortho(self, x=10, y=10):
            del self.frontiers[currCell]
      self.width = x
      self.height = y


    def ortho(self, x=10, y=10):
      # Define wall-mapping for this maze geometry.
        self.width = x
        self.height = y


        # Define wall-mapping for this maze geometry.
      self.opposites[0] = 2
        self.opposites[0] = 2
      self.opposites[1] = 3
        self.opposites[1] = 3
      self.opposites[2] = 0
        self.opposites[2] = 0
      self.opposites[3] = 1
        self.opposites[3] = 1


        # Create basic stuff prior to maze generation
      # Create basic stuff prior to maze generation
        counter = 0
        for i in range(y):
            sys.stdout.flush()
            for j in range(x):
                self.cells[counter] = MazeCell(4)


                # fill for neighbour to "north"
      counter = 0
                if(i == 0):
      for i in range(y):
                    self.cells[counter].myNeighbours.append(-1)
        sys.stdout.flush()
                else:
        for j in range(x):
                    self.cells[counter].myNeighbours.append(counter - x)
            self.cells[counter] = MazeCell(4)


                # fill for neighbour to "east"
            # fill for neighbour to "north"
                if(j == (x - 1)):
                    self.cells[counter].myNeighbours.append(-1)
                else:
                    self.cells[counter].myNeighbours.append(counter + 1)


                # fill for neighbour to "south"
            if(i == 0):
                if(i == (y - 1)):
              self.cells[counter].myNeighbours.append(-1)
                    self.cells[counter].myNeighbours.append(-1)
            else:
                else:
              self.cells[counter].myNeighbours.append(counter - x)
                    self.cells[counter].myNeighbours.append(counter + x)


                # fill for neighbour to "west"
            # fill for neighbour to "east"
                if(j == 0):
                    self.cells[counter].myNeighbours.append(-1)
                else:
                    self.cells[counter].myNeighbours.append(counter - 1)


                self.cells[counter].myWalls[0] = 1
            if(j == (x - 1)):
                self.cells[counter].myWalls[1] = 1
              self.cells[counter].myNeighbours.append(-1)
                self.cells[counter].myWalls[2] = 1
            else:
                self.cells[counter].myWalls[3] = 1
              self.cells[counter].myNeighbours.append(counter + 1)


                # If we're done in this loop, increment counter
            # fill for neighbour to "south"
                counter = counter + 1


        ## Basic init done, now actually traverse this crap to build maze structure.
            if(i == (y - 1)):
        self.genPrim()
              self.cells[counter].myNeighbours.append(-1)
            else:
              self.cells[counter].myNeighbours.append(counter + x)


        # DEBUG: print out stuff for maze
            # fill for neighbour to "west"
##        print "Test: "+ `x` + ' ' + `y`
##        counter = 0
##        for i in range(y):
##            for j in range(x):
##                print 'Neighbours: ' +`len(self.cells[counter].myNeighbours)`
##                for z in range(len(self.cells[counter].myNeighbours)):
##                    sys.stdout.write(`self.cells[counter].myNeighbours[z]` + ' ')
##                sys.stdout.write('\n')


##               counter = counter + 1
            if(j == 0):
              self.cells[counter].myNeighbours.append(-1)
            else:
               self.cells[counter].myNeighbours.append(counter - 1)


    def printOrthoAscii(self):
            self.cells[counter].myWalls[0] = 1
        debug('***PRINTING***  height: ' + `self.height` + '  width: ' + `self.width`)
            self.cells[counter].myWalls[1] = 1
        # print very top bit
            self.cells[counter].myWalls[2] = 1
        y = 0
            self.cells[counter].myWalls[3] = 1


         sys.stdout.write('+')
            # If we're done in this loop, increment counter
        for j in range(self.width):
 
            if(self.cells[j].myWalls.has_key(0)):
            counter = counter + 1
                sys.stdout.write('--+')
 
      ## Basic init done, now actually traverse this crap to build maze structure.
 
      self.genPrim()
 
      # DEBUG: print out stuff for maze
 
##      print "Test: "+ `x` + ' ' + `y`
##      counter = 0
##      for i in range(y):
##         for j in range(x):
##            print 'Neighbours: ' +`len(self.cells[counter].myNeighbours)`
##            for z in range(len(self.cells[counter].myNeighbours)):
##              sys.stdout.write(`self.cells[counter].myNeighbours[z]` + ' ')
##            sys.stdout.write('\n')
 
##            counter = counter + 1
 
  #-----------------------------------------------------------------------
 
  def printOrthoAscii(self):
      debug('***PRINTING***  height: ' + `self.height` + '  width: ' + `self.width`)
      # print very top bit
      y = 0
 
      sys.stdout.write('+')
      for j in range(self.width):
        if(self.cells[j].myWalls.has_key(0)):
            sys.stdout.write('--+')
        else:
            sys.stdout.write('  +')
      sys.stdout.write('\n')
 
      # Now, for each row, print sides, then bottoms
 
      for i in range(self.height):
        # sides
 
        sys.stdout.write('|')
 
        for j in range(self.width):
            if(self.cells[(i * self.width) + j].myWalls.has_key(1)):
              sys.stdout.write('  |')
             else:
             else:
                sys.stdout.write(' +')
              sys.stdout.write('   ')
        sys.stdout.write('\n')
 
        sys.stdout.write('\n')


        # Now, for each row, print sides, then bottoms
        # bottom


        for i in range(self.height):
        sys.stdout.write('+')
 
        for j in range(self.width):
            if(self.cells[(i * self.width) + j].myWalls.has_key(2)):
              sys.stdout.write('--+')
            else:
              sys.stdout.write('  +')


            # sides
        sys.stdout.write('\n')
            sys.stdout.write('|')
            for j in range(self.width):
                if(self.cells[(i * self.width) + j].myWalls.has_key(1)):
                    sys.stdout.write('  |')
                else:
                    sys.stdout.write('  ')
            sys.stdout.write('\n')


            # bottom
#--------------------------------------------------------------------------
            sys.stdout.write('+')
            for j in range(self.width):
                if(self.cells[(i * self.width) + j].myWalls.has_key(2)):
                    sys.stdout.write('--+')
                else:
                    sys.stdout.write('  +')
            sys.stdout.write('\n')


def mazeBench():
def mazeBench():
      import profile,pstats
  import profile,pstats
 
  profile.run('foo = PrimMaze()')
  profile.run('foo.ortho(25,25)', 'maze_prof')
  foo.printOrthoAscii()
  sys.stdout.flush()
  p = pstats.Stats('maze_prof')
  p.sort_stats('time')
  p.print_stats()


      profile.run('foo = PrimMaze()')
#--------------------------------------------------------------------------
      profile.run('foo.ortho(25,25)', 'maze_prof')
      foo.printOrthoAscii()
      sys.stdout.flush()
      p = pstats.Stats('maze_prof')
      p.sort_stats('time')
      p.print_stats()


if __name__ == '__main__':
if __name__ == '__main__':
    foo = PrimMaze()
  foo = PrimMaze()
    foo.ortho(25,25)
  foo.ortho(25,25)
    foo.printOrthoAscii()
  foo.printOrthoAscii()
    # mazeBench()
  # mazeBench()
 
#--------------------------------------------------------------------------
 
</pre>
</pre>


[[Category:Python]]
[[Category:Python]]
[[Category:Examples]]

Latest revision as of 01:40, 20 September 2011

Overview

A Python implementation of a maze generator which demonstrates the use of classes.

Script

#!/usr/bin/env python
#
# $Id: maze.py,v 1.2 2000/12/12 19:45:01 tlavoie Exp tlavoie $
# $Log: maze.py,v $
# Revision 1.2  2000/12/12 19:45:01  tlavoie
# Last save before changing self.cells from a list to a dictionary.
#
#--------------------------------------------------------------------------

import sys, random

#--------------------------------------------------------------------------

def debug(x):
   pass
   #print 'DEBUG: ' + `x`
   #sys.stdout.flush()

#--------------------------------------------------------------------------

class MazeCell:
   "Class which defines a single cell in an n-dimensional maze"

   #-----------------------------------------------------------------------

   def __init__ (self, size=4):
      self.numNeighbours = size  #  Will be changed when initialized by maze routine
      self.currState = 0         #  0 is out, 1 is frontier, 2 is in.
                                 #  This is used for Prim's algorithm.
      self.myNeighbours = []
      self.myWalls = {}

   #-----------------------------------------------------------------------

   def stateFrontier(self):
      if(self.currState == 0):
         self.currState = 1
         return(1)               #  Used to decide whether or not maze code
                                 #  should increment  frontier counter.
      else:
         return(None)

   #-----------------------------------------------------------------------

   def stateIn(self):
      self.currState = 2
      #  Incrementing "out" neighbours to "frontier" state will
      #  be handled by maze code, not here.

#--------------------------------------------------------------------------

class PrimMaze:
   "Creates a maze using Prim's algorithm"

   #-----------------------------------------------------------------------

   def __init__(self):
      self.frontiers = {}   # keep a dict of current frontier cells.
      self.cells = {}        # Store cell data here...
      self.opposites = {}      # create a list of "return" paths since cell walls map both ways.
      self.height = 0
      self.width = 0

   #-----------------------------------------------------------------------

   def genPrim(self):
      currCell = random.randint(0,len(self.cells))
      self.cells[currCell].stateIn()

      debug('cell ' + `currCell` + ' now IN')

      for i in range(self.cells[0].numNeighbours):
         thisNeigh = self.cells[currCell].myNeighbours[i]

         if(thisNeigh != -1):
            if(self.cells[thisNeigh].stateFrontier() == 1):
               self.frontiers[thisNeigh] = 1
               debug('cell ' + `thisNeigh` + ' now FRONTIER')

      #  Now, if there are any frontier cells, pick one, give it a path
      #  to an 'IN' cell,  and make it IN also.

      while(len(self.frontiers) > 0):
         debug('frontier cells: ' + `self.frontiers`)
         nextCell = self.frontiers.keys()[random.randint(0,len(self.frontiers.keys()) -1)]

         debug('cell ' + `nextCell` + ' is NEXT')

         foundIn = 0

         # Now, pick a neighbour who's IN, at random

         while(foundIn == 0):
            myPalRef = random.randint(0, len(self.cells[nextCell].myNeighbours) - 1)
            myPal = self.cells[nextCell].myNeighbours[myPalRef]

            if (myPal != -1):
               debug('cell ' + `myPal` + ' is my pal')
               if(self.cells[myPal].currState == 2):
                  foundIn = 1

         # OK, we've got a route out to the existing maze. Move own state to IN

         self.cells[nextCell].stateIn()

         # break down walls
         # debug('BAD WALL: ' + `self.cells[nextCell].myWalls[myPal]`)

         debug('BAD WALL1: ' + `myPalRef`)
         debug('BAD WALL2: ' + `self.opposites[myPalRef]`)
         debug('nextCell walls: ' + `self.cells[nextCell].myWalls`)

         del self.cells[nextCell].myWalls[myPalRef]
         del self.cells[myPal].myWalls[self.opposites[myPalRef]]

         # update OUT neighbours to FRONTIER

         currCell = nextCell

         for i in range(self.cells[0].numNeighbours):
            thisNeigh = self.cells[currCell].myNeighbours[i]
            if(thisNeigh != -1):
               if(self.cells[thisNeigh].stateFrontier() == 1):
                  self.frontiers[thisNeigh] = 1
                  debug('cell ' + `thisNeigh` + ' now FRONTIER')

         # Remove currCell from frontiers{}

         del self.frontiers[currCell]

   #-----------------------------------------------------------------------

   def ortho(self, x=10, y=10):
      self.width = x
      self.height = y

      # Define wall-mapping for this maze geometry.

      self.opposites[0] = 2
      self.opposites[1] = 3
      self.opposites[2] = 0
      self.opposites[3] = 1

      # Create basic stuff prior to maze generation

      counter = 0
      for i in range(y):
         sys.stdout.flush()
         for j in range(x):
            self.cells[counter] = MazeCell(4)

            # fill for neighbour to "north"

            if(i == 0):
               self.cells[counter].myNeighbours.append(-1)
            else:
               self.cells[counter].myNeighbours.append(counter - x)

            # fill for neighbour to "east"

            if(j == (x - 1)):
               self.cells[counter].myNeighbours.append(-1)
            else:
               self.cells[counter].myNeighbours.append(counter + 1)

            # fill for neighbour to "south"

            if(i == (y - 1)):
               self.cells[counter].myNeighbours.append(-1)
            else:
               self.cells[counter].myNeighbours.append(counter + x)

            # fill for neighbour to "west"

            if(j == 0):
               self.cells[counter].myNeighbours.append(-1)
            else:
               self.cells[counter].myNeighbours.append(counter - 1)

            self.cells[counter].myWalls[0] = 1
            self.cells[counter].myWalls[1] = 1
            self.cells[counter].myWalls[2] = 1
            self.cells[counter].myWalls[3] = 1

            # If we're done in this loop, increment counter

            counter = counter + 1

      ## Basic init done, now actually traverse this crap to build maze structure.

      self.genPrim()

      # DEBUG: print out stuff for maze

##      print "Test: "+ `x` + ' ' + `y`
##      counter = 0
##      for i in range(y):
##         for j in range(x):
##            print 'Neighbours: ' +`len(self.cells[counter].myNeighbours)`
##            for z in range(len(self.cells[counter].myNeighbours)):
##               sys.stdout.write(`self.cells[counter].myNeighbours[z]` + ' ')
##            sys.stdout.write('\n')

##            counter = counter + 1

   #-----------------------------------------------------------------------

   def printOrthoAscii(self):
      debug('***PRINTING***   height: ' + `self.height` + '   width: ' + `self.width`)
      # print very top bit
      y = 0

      sys.stdout.write('+')
      for j in range(self.width):
         if(self.cells[j].myWalls.has_key(0)):
            sys.stdout.write('--+')
         else:
            sys.stdout.write('  +')
      sys.stdout.write('\n')

      # Now, for each row, print sides, then bottoms

      for i in range(self.height):
         # sides

         sys.stdout.write('|')

         for j in range(self.width):
            if(self.cells[(i * self.width) + j].myWalls.has_key(1)):
               sys.stdout.write('  |')
            else:
               sys.stdout.write('   ')

         sys.stdout.write('\n')

         # bottom

         sys.stdout.write('+')

         for j in range(self.width):
            if(self.cells[(i * self.width) + j].myWalls.has_key(2)):
               sys.stdout.write('--+')
            else:
               sys.stdout.write('  +')

         sys.stdout.write('\n')

#--------------------------------------------------------------------------

def mazeBench():
   import profile,pstats

   profile.run('foo = PrimMaze()')
   profile.run('foo.ortho(25,25)', 'maze_prof')
   foo.printOrthoAscii()
   sys.stdout.flush()
   p = pstats.Stats('maze_prof')
   p.sort_stats('time')
   p.print_stats()

#--------------------------------------------------------------------------

if __name__ == '__main__':
   foo = PrimMaze()
   foo.ortho(25,25)
   foo.printOrthoAscii()
   # mazeBench()

#--------------------------------------------------------------------------