maze_gen.py
'''Maze Generator #1.
Coder@Sonnack.com
September 16, 2014
'''
from sys import argv
from random import random, shuffle
from logger import logger, info, debug, trace
from maze_obj import MazeObject
from maze_nav import MazeNavigator
from maze_img import MazeImage
Log = logger('Maze/Gen')
RandomWall = lambda f: random() < f
class MazeGen1 (MazeNavigator):
'''\
Maze Generator #1.
methods:
generate_maze ()
internal methods:
carve_a_path (cell_value) - carve a single path in the maze
find_a_path () - find a random cell on a path for branching to a new path
change_direction () - turn left or right (50/50 either way)
'''
max_steps = 100
wall_change = 0.1
occu_change = 0.1
dir_change = 1
def __init__ (self, mz, row=0, col=0):
super(MazeGen1,self).__init__(mz, row, col)
self.mz = mz
self.tot_steps = 0
self.dir_steps = 0
self.path_list = []
def generate_maze (self):
'''Generate a maze by carving paths in it.'''
self.mz.set_all_cells(MazeObject.EmptyCell)
self.mz.set_cell(self.curr_row, self.curr_col, MazeObject.MazeEntry)
self.reset_curr_cell()
for ix in range(self.mz.dims[0] * self.mz.dims[1]):
self.carve_a_path(1)
if self.mz.all_cells_occupied():
Log.info('Finished after %d iterations.' % ix)
self.mz.set_all_cells(MazeObject.EmptyCell)
self.mz.set_entry_and_exit()
return
Log.error('NOT DONE AFTER %d ITERATIONS!' % ix)
def carve_a_path (self, cell_value):
'''Carve a path through the maze. Set each cell in the path to cell_value.'''
Log.debug()
Log.debug('NEW PATH...')
if 0 < len(self.path_list):
if not self.find_a_path():
return
Log.debug('using: (%d, %d)' % (self.curr_row, self.curr_col))
self.tot_steps = 0
self.dir_steps = 0
current_path = []
while self.tot_steps < self.max_steps:
s = self.Abbrev[self.direction]
t = (self.tot_steps, self.dir_steps, s, self.curr_row, self.curr_col)
Log.trace('STEP: %d (%d:%s) [%d, %d]' % t)
current_path.append([self.curr_row, self.curr_col, 2])
tc = self.get_next_cell()
if tc < 0:
Log.trace('Hit the wall!')
if self.wall_change < random():
if self.change_direction():
continue
break
if 0 < tc:
Log.trace('Hit another path!')
if self.occu_change < random():
if self.change_direction():
continue
break
self.set_next_wall(MazeObject.NoWall)
self.move_to_next_cell()
self.set_curr_cell(cell_value)
self.tot_steps = self.tot_steps + 1
self.dir_steps = self.dir_steps + 1
if self.dir_change < (random() * self.dir_steps):
if self.change_direction():
self.dir_steps = 0
n = len(current_path)
Log.debug('New Path: %d step%s' % (n, '' if n == 1 else 's'))
self.path_list.append(current_path)
def find_a_path (self):
'''Randomly pick a cell on an existing path to branch from. Set the new row,col and direction.'''
maze_path_seq = [x for x in range(len(self.path_list))]
shuffle(maze_path_seq)
for mx,maze_path in map(lambda ix: (ix, self.path_list[ix]), maze_path_seq):
Log.trace('MazePath: %d (%d cells)' % (mx, len(maze_path)))
maze_cell_seq = [x for x in range(len(maze_path))]
shuffle(maze_cell_seq)
for cx,path_cell in map(lambda ix: (ix,maze_path[ix]), maze_cell_seq):
Log.trace('PathCell: %d [%d,%d (%d)]' % (cx, path_cell[0], path_cell[1], path_cell[2]))
if path_cell[2] < 4:
self.curr_row = path_cell[0]
self.curr_col = path_cell[1]
Log.trace('testing: (%d, %d)' % (self.curr_row, self.curr_col))
if self.get_north_cell() == 0:
self.direction = self.North
return True
if self.get_east_cell() == 0:
self.direction = self.East
return True
if self.get_south_cell() == 0:
self.direction = self.South
return True
if self.get_west_cell() == 0:
self.direction = self.West
return True
Log.debug('Unable to find a path!')
return False
def change_direction (self):
'''Turn Left/Right (50% random choice) or Right/Left if first choice not possible. Return True if successful.'''
if random() < 0.5:
if self.get_left_cell() == MazeObject.EmptyCell:
d = self.turn_left()
else:
if self.get_right_cell() == MazeObject.EmptyCell:
d = self.turn_right()
else:
return False
else:
if self.get_right_cell() == MazeObject.EmptyCell:
d = self.turn_right()
else:
if self.get_left_cell() == MazeObject.EmptyCell:
d = self.turn_left()
else:
return False
Log.trace('NEW DIRECTION: %d' % d)
return True
if __name__ == '__main__':
print('autorun: %s' % argv[0])
Log.start('maze_gen.log')
Log.level(info())
mz = MazeObject(12, 20)
mi = MazeImage(mz)
mg = MazeGen1(mz)
mg.generate_maze()
mz.print_maze(Log.fp())
mi.draw_maze()
mi.show()
mi.save('maze_gen.png')
Log.end()
'''eof'''