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 loggerinfodebugtrace
####################################################################################################
Log = logger('Maze/Obj')

Limit = lambda lst,ix: max(0min(len(lst)-1ix))


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## 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__ (selfrowscols):
        '''Create new Maze Object instance.'''
        self.dims = (rowscols)
        self.entry_cell = (00)
        self.exit_cell = (self.dims[0]-1self.dims[1]-1)
        # Initialize maze data...
        self.set_all_cells(MazeObject.EmptyCell)
        self.set_all_walls(MazeObject.MazeWall)

    def cell (selfrowcol): return self.cells[row][col]

    def wall_n (selfrowcol): return self.ns_walls[row  ][col  ]
    def wall_e (selfrowcol): return self.we_walls[row  ][col+1]
    def wall_s (selfrowcol): return self.ns_walls[row+1][col  ]
    def wall_w (selfrowcol): 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 (selfrowcolstate): self.cells[row][col] = state

    def set_all_cells (selfstate):
        '''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 (selfrowcolstate): self.ns_walls[row  ][col  ] = state
    def set_wall_e (selfrowcolstate): self.we_walls[row  ][col+1] = state
    def set_wall_s (selfrowcolstate): self.ns_walls[row+1][col  ] = state
    def set_wall_w (selfrowcolstate): self.we_walls[row  ][col  ] = state

    def set_all_walls (selfstate):
        '''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 (selffp):
        '''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_symsself.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_symsself.wall_w(row,col))
                fp.write(self.vwall_syms[w])
                # Print cell...
                c = Limit(self.cell_symsself.cell(row,col))
                fp.write(self.cell_syms[c])
            # Print east-most wall...
            w = Limit(self.vwall_symsself.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_symsself.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 (selffp):
        '''X: Maze Visitor idea -- entry method.'''
        self.sout = fp
        self.visit_maze(self)

    def visit_maze (selfvisitor):
        '''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(rowcol)
                visitor.print_wall_n(rowcolself.wall_n(row,col))
            visitor.print_corner(rowcol)
            visitor.print_eol(rowcol)
            # Print cells (and vertical walls)...
            for col in range(self.dims[1]):
                # Print west wall...
                visitor.print_wall_w(rowcolself.wall_w(row,col))
                # Print cell...
                visitor.print_cell(rowcolself.cell(row,col))
            # Print east-most wall...
            visitor.print_wall_e(rowcolself.wall_e(row,col))
            visitor.print_eol(rowcol)
        # Print south-most walls...
        for col in range(self.dims[1]):
            visitor.print_corner(rowcol)
            visitor.print_wall_s(rowcolself.wall_s(row,col))
        visitor.print_corner(rowcol)
        visitor.print_eol(rowcol)

    def print_corner (selfrowcol):
        self.sout.write('+')
    def print_eol (selfrowcol):
        self.sout.write('\n')
        self.sout.flush()
    def print_cell (selfrowcolval):
        ix = Limit(self.cell_symsval)
        self.sout.write(self.cell_syms[ix])
    def print_wall_n (selfrowcolval):
        ix = Limit(self.hwall_symsval)
        self.sout.write(self.hwall_syms[ix])
    def print_wall_s (selfrowcolval):
        ix = Limit(self.hwall_symsval)
        self.sout.write(self.hwall_syms[ix])
    def print_wall_w (selfrowcolval):
        ix = Limit(self.vwall_symsval)
        self.sout.write(self.vwall_syms[ix])
    def print_wall_e (selfrowcolval):
        ix = Limit(self.vwall_symsval)
        self.sout.write(self.vwall_syms[ix])



##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
## 3D MAZE OBJECT
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~##
class MazeCubeObject (object):
    '''3D Maze Object class.'''

    def __init__ (selfrowscolslevels):
        '''Create new Maze Cube Object instance.'''
        super(MazeCubeObject,self).__init__(rowscols)
        # 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(46)

    mz.set_entry_and_exit()

    mz.set_wall_e(0,0MazeObject.NoWall)
    mz.set_wall_s(0,1MazeObject.NoWall)
    mz.set_wall_s(1,1MazeObject.NoWall)
    mz.set_wall_e(2,1MazeObject.NoWall)
    mz.set_wall_e(2,2MazeObject.NoWall)
    mz.set_wall_e(2,3MazeObject.NoWall)
    mz.set_wall_s(2,4MazeObject.NoWall)
    mz.set_wall_e(3,4MazeObject.NoWall)

    mz.set_cell(2,2MazeObject.PathMarker)

    Log.info()
    Log.info(str(mz))
    Log.info(repr(mz))
    Log.info()
    mz.print_maze(Log.fp())
    Log.info()

    Log.end()

####################################################################################################
'''eof'''