Difference between revisions of "Maze.py"
Jump to navigation
Jump to search
PeterHarding (talk | contribs) |
PeterHarding (talk | contribs) |
||
| Line 218: | Line 218: | ||
[[Category:Python]] | [[Category:Python]] | ||
[[Category:Examples]] | |||
Revision as of 17:11, 19 July 2009
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()