### 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))

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## MAZE OBJECT
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
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)

'''
# Symbols for printing...
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)
# Initialize maze data...
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.'''
# Mark the Entrance & Exit cells...
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)
# Open the Entrance & Exit walls...
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]):
# Print north walls...
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')
# Print cells (and vertical walls)...
for col in range(self.dims[1]):
# Print west wall...
w = Limit(self.vwall_syms, self.wall_w(row,col))
fp.write(self.vwall_syms[w])
# Print cell...
c = Limit(self.cell_syms, self.cell(row,col))
fp.write(self.cell_syms[c])
# Print east-most wall...
w = Limit(self.vwall_syms, self.wall_e(row,col))
fp.write(self.vwall_syms[w])
fp.write('\n')
# Print south-most walls...
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]):
# Print north walls...
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)
# Print cells (and vertical walls)...
for col in range(self.dims[1]):
# Print west wall...
visitor.print_wall_w(row, col, self.wall_w(row,col))
# Print cell...
visitor.print_cell(row, col, self.cell(row,col))
# Print east-most wall...
visitor.print_wall_e(row, col, self.wall_e(row,col))
visitor.print_eol(row, col)
# Print south-most walls...
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])

##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## 3D MAZE OBJECT
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
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)
# Initialize Bottom/Top walls...
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'''
```