maze_obj.py
'''Maze Object Class.
The Maze model consists of cells surrounded by walls. Any wall can have
door connecting an adjacent cell. Doors can potentially have multiple
states (open, closed, closed+locked, etc).
The Maze object supports a three-dimensional maze. Such a maze has
multiple levels. Cells have (potentially) top and/or bottom doors
that connect to cells in the level above or below.
Internally, the Maze object maintains four arrays:
Maze Cells [rows , cols ]
North/South Doors [rows+1, cols ]
West/East Doors [rows , cols+1]
Bottom/Top Doors [rows , cols ] * (levels + 1)
0 1 2 3 4 5 6 7
+-@-+-@-+-@-+-@-+-@-+-@-+-@-+-@-+
0 # # # # # # # # #
+-@-+-@-+-@-+-@-+-@-+-@-+-@-+-@-+ @ N/S door
1 # # # # X # # # # #
+-@-+-@-+-@-+-@-+-@-+-@-+-@-+-@-+
2 # # # # # # # # # # E/W door
+-@-+-@-+-@-+-@-+-@-+-@-+-@-+-@-+
3 # # # # # # # # #
+-@-+-@-+-@-+-@-+-@-+-@-+-@-+-@-+
e.g. Cell = [1,3]
N/S Door = [1,3]/[2,3] {0,0}/{+1,0}
W/E Door = [1,3]/[1,4] {0,0}/{0,+1}
B/T Door = [1,3]/[1,3] [Level]/[Level+1]
Coder@Sonnack.com
September 16, 2014
'''
from sys import argv
from logger import logger, info, debug, trace
Log = logger('Maze/Obj')
Limit = lambda lst,ix: max(0, min(len(lst)-1, ix))
class MazeObject (object):
'''\
Maze Object class.
properties:
dims - (rows, cols)
cells - Maze Cells matrix
ns_walls - North-South walls matrix
we_walls - West-East walls matrix
entry_cell - Row-Col of Maze Entry cell
exit_cell - Row-Col of Maze Exit cell
methods:
print_maze (fp)
all_cells_occupied ()
set_entry_and_exit ()
cell (row, col)
set_cell (row, col, state)
set_all_cells (state)
wall_n (row, col)
wall_e (row, col)
wall_s (row, col)
wall_w (row, col)
set_wall_n (row, col, state)
set_wall_e (row, col, state)
set_wall_s (row, col, state)
set_wall_w (row, col, state)
set_all_walls (state)
'''
cell_syms = [' ', ' @ ', ' # ', ' $ ', ' ? ']
hwall_syms = [' ', '---', '-o-', '-x-']
vwall_syms = [ ' ' , '|' , 'o' , 'x' ]
corner_sym = '+'
EmptyCell = int(0)
NoWall = int(0)
PathMarker = int(1)
MazeWall = int(1)
MazeEntry = int(2)
MazeExit = int(3)
PathTag = int(4)
def __init__ (self, rows, cols):
'''Create new Maze Object instance.'''
self.dims = (rows, cols)
self.entry_cell = (0, 0)
self.exit_cell = (self.dims[0]-1, self.dims[1]-1)
self.set_all_cells(MazeObject.EmptyCell)
self.set_all_walls(MazeObject.MazeWall)
def cell (self, row, col): return self.cells[row][col]
def wall_n (self, row, col): return self.ns_walls[row ][col ]
def wall_e (self, row, col): return self.we_walls[row ][col+1]
def wall_s (self, row, col): return self.ns_walls[row+1][col ]
def wall_w (self, row, col): return self.we_walls[row ][col ]
def set_entry_and_exit (self):
'''Set Entry and Exit cell and wall values.'''
self.set_cell(self.entry_cell[0], self.entry_cell[1], MazeObject.MazeEntry)
self.set_cell(self.exit_cell[0] , self.exit_cell[1] , MazeObject.MazeExit)
self.set_wall_w(self.entry_cell[0], self.entry_cell[1], MazeObject.MazeEntry)
self.set_wall_e(self.exit_cell[0] , self.exit_cell[1] , MazeObject.MazeExit)
def set_cell (self, row, col, state): self.cells[row][col] = state
def set_all_cells (self, state):
'''Set all maze cells to the same state.'''
self.cells = [[state for col in range(self.dims[1])] for row in range(self.dims[0])]
def set_wall_n (self, row, col, state): self.ns_walls[row ][col ] = state
def set_wall_e (self, row, col, state): self.we_walls[row ][col+1] = state
def set_wall_s (self, row, col, state): self.ns_walls[row+1][col ] = state
def set_wall_w (self, row, col, state): self.we_walls[row ][col ] = state
def set_all_walls (self, state):
'''Set all maze walls to the same state.'''
self.ns_walls = [[state for col in range(self.dims[1]) ] for row in range(self.dims[0]+1)]
self.we_walls = [[state for col in range(self.dims[1]+1)] for row in range(self.dims[0]) ]
def all_cells_occupied (self):
'''Test to see if all cells are occupied.'''
for maze_row in self.cells:
for maze_cell in maze_row:
if maze_cell == MazeObject.EmptyCell:
return False
return True
def print_maze (self, fp):
'''Dump an ASCII version of the maze to the given file pointer.'''
for row in range(self.dims[0]):
for col in range(self.dims[1]):
w = Limit(self.hwall_syms, self.wall_n(row,col))
fp.write(self.corner_sym)
fp.write(self.hwall_syms[w])
fp.write(self.corner_sym)
fp.write('\n')
for col in range(self.dims[1]):
w = Limit(self.vwall_syms, self.wall_w(row,col))
fp.write(self.vwall_syms[w])
c = Limit(self.cell_syms, self.cell(row,col))
fp.write(self.cell_syms[c])
w = Limit(self.vwall_syms, self.wall_e(row,col))
fp.write(self.vwall_syms[w])
fp.write('\n')
for col in range(self.dims[1]):
w = Limit(self.hwall_syms, self.wall_s(row,col))
fp.write(self.corner_sym)
fp.write(self.hwall_syms[w])
fp.write(self.corner_sym)
fp.write('\n')
def __str__ (self):
'''String value.'''
s = '%d x %d (%d,%d) -> (%d,%d)'
t = (self.dims[0], self.dims[1], self.entry_cell[0], self.entry_cell[1], self.exit_cell[0], self.exit_cell[1])
return s % t
def __repr__ (self):
'''JSON-like string.'''
s = '{MazeObject:{rows:%d, cols:%d, entry:(%d,%d), exit:(%d,%d)}}'
t = (self.dims[0], self.dims[1], self.entry_cell[0], self.entry_cell[1], self.exit_cell[0], self.exit_cell[1])
return s % t
def visit_maze_demo (self, fp):
'''X: Maze Visitor idea -- entry method.'''
self.sout = fp
self.visit_maze(self)
def visit_maze (self, visitor):
'''Visit Maze. Visitor must support: print_cell, print_wall_{NESW}, print_corner, print_eol.'''
for row in range(self.dims[0]):
for col in range(self.dims[1]):
visitor.print_corner(row, col)
visitor.print_wall_n(row, col, self.wall_n(row,col))
visitor.print_corner(row, col)
visitor.print_eol(row, col)
for col in range(self.dims[1]):
visitor.print_wall_w(row, col, self.wall_w(row,col))
visitor.print_cell(row, col, self.cell(row,col))
visitor.print_wall_e(row, col, self.wall_e(row,col))
visitor.print_eol(row, col)
for col in range(self.dims[1]):
visitor.print_corner(row, col)
visitor.print_wall_s(row, col, self.wall_s(row,col))
visitor.print_corner(row, col)
visitor.print_eol(row, col)
def print_corner (self, row, col):
self.sout.write('+')
def print_eol (self, row, col):
self.sout.write('\n')
self.sout.flush()
def print_cell (self, row, col, val):
ix = Limit(self.cell_syms, val)
self.sout.write(self.cell_syms[ix])
def print_wall_n (self, row, col, val):
ix = Limit(self.hwall_syms, val)
self.sout.write(self.hwall_syms[ix])
def print_wall_s (self, row, col, val):
ix = Limit(self.hwall_syms, val)
self.sout.write(self.hwall_syms[ix])
def print_wall_w (self, row, col, val):
ix = Limit(self.vwall_syms, val)
self.sout.write(self.vwall_syms[ix])
def print_wall_e (self, row, col, val):
ix = Limit(self.vwall_syms, val)
self.sout.write(self.vwall_syms[ix])
class MazeCubeObject (object):
'''3D Maze Object class.'''
def __init__ (self, rows, cols, levels):
'''Create new Maze Cube Object instance.'''
super(MazeCubeObject,self).__init__(rows, cols)
self.bt_walls = []
for lvl in range(levels):
a = [[MazeObject.MazeWall for col in range(self.dims[1])] for row in range(self.dims[0])]
self.bt_walls.append(a)
if __name__ == '__main__':
print('autorun: %s' % argv[0])
Log.start('maze_obj.log')
Log.level(info())
mz = MazeObject(4, 6)
mz.set_entry_and_exit()
mz.set_wall_e(0,0, MazeObject.NoWall)
mz.set_wall_s(0,1, MazeObject.NoWall)
mz.set_wall_s(1,1, MazeObject.NoWall)
mz.set_wall_e(2,1, MazeObject.NoWall)
mz.set_wall_e(2,2, MazeObject.NoWall)
mz.set_wall_e(2,3, MazeObject.NoWall)
mz.set_wall_s(2,4, MazeObject.NoWall)
mz.set_wall_e(3,4, MazeObject.NoWall)
mz.set_cell(2,2, MazeObject.PathMarker)
Log.info()
Log.info(str(mz))
Log.info(repr(mz))
Log.info()
mz.print_maze(Log.fp())
Log.info()
Log.end()
'''eof'''