Skip to content

Game of Life 3D on GitHub

I have also placed the source code into a GitHub repository.

See: https://github.com/RichardAlexanderGreen/GameOfLife3D/tree/master/src

Note: The User Interface VPython_GOL.py is in progress.

Current version (here and on github) is runnable in VPython.

It does not includes a dashboard at this time.
The next version will include a dashboard for the application.

Grid3DTest.py

'''
Created on Apr 5, 2012

@author: admin
'''
import unittest

from Grid3D import Grid3D

class Grid3DTest(unittest.TestCase):

    def testCreateGrid(self):
        ''' Story: A grid may be 3 dimensional with rows, columns, and layers. '''
        grid = Grid3D( rows = 17, columns = 19, layers = 1 )
        self.assertEqual( 17, grid.rows )
        self.assertEqual( 19, grid.columns )
        self.assertEqual( 1, grid.layers )
        
    def testSetAndGet(self):
        '''
        The health of a cell is an integer.
        It may be set and inspected.
        '''
        grid = Grid3D( rows = 17, columns = 19, layers = 1 )
        grid.setCell( row = 17, column = 19, layer = 1, value = 3 )
        self.assertEquals( 3, grid.getCell( row = 17, column = 19, layer = 1 ) )
        
    def testGetEmpty(self):
        '''
        An empty cell has never been alive or dead.
        A zero valued cell was alive but has died.
        '''
        grid = Grid3D( rows = 17, columns = 19, layers = 1 )
        grid.setCell( row = 17, column = 19, layer = 1, value = 3 )
        self.assertEquals( 3, grid.getCell( row = 17, column = 19, layer = 1 ) ) 
        # If the cell is empty (as opposed to dead), we get the code value None.
        self.assertEquals( None, grid.getCell( row = 1, column = 1, layer = 1 ) )
        
    def testIndicesWrapAround(self):
        '''
        Our grid is a "toroid" -- Indices will wrap around.
        '''
        grid = Grid3D( rows = 17, columns = 19, layers = 1 )
        
        # Test the indices's upper edges
        grid.setCell( row = 18, column = 20, layer = 2, value = 3 )
        self.assertEquals( 3, grid.getCell( row = 18, column = 20, layer = 2 ) ) 
        self.assertEquals( 3, grid.getCell( row = 1, column = 1, layer = 1 ) )
        
        # Test the indices's lower edges
        grid.setCell( row = 0, column = 0, layer = 0, value = 5 )
        self.assertEquals( 5, grid.getCell( row = 0, column = 0, layer = 0 ) ) 
        self.assertEquals( 5, grid.getCell( row =17, column =19, layer = 1 ) )

        # If the cell is empty (as opposed to dead) we get the code value None.
        grid = Grid3D( rows = 17, columns = 19, layers = 1 )
        self.assertEquals( None, grid.getCell( row = 0, column = 0, layer = 0 ) )
        self.assertEquals( None, grid.getCell( row = 18, column = 20, layer = 2 ) )
       
    
    def testCountNeighbors(self):
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        # One neighbor
        grid.setCell( row = 1, column=1, layer=1, value= 7 )
        count = grid.countLiveNeighbors( row = 2, column = 2, layer = 2 )
        self.assertEquals( 1, count )
        # Two neighbors
        grid.setCell( row = 3, column=3, layer=3, value= 7 )
        count = grid.countLiveNeighbors( row = 2, column = 2, layer = 2 )
        self.assertEquals( 2, count )
        # Three neighbors
        grid.setCell( row = 1, column=3, layer=3, value= 7 )
        count = grid.countLiveNeighbors( row = 2, column = 2, layer = 2 )
        self.assertEquals( 3, count )
        # Four neighbors
        grid.setCell( row = 1, column=2, layer=3, value= 7 )
        count = grid.countLiveNeighbors( row = 2, column = 2, layer = 2 )
        self.assertEquals( 4, count )
        
        '''
        The adjacent neighbors include all cells with a row, column, or layer index plus or minus one the cell in question.
        This version includes all 26 neighbors.                 ( 9 on layer above, 9 on layer below, and 8 on same layer )
        There are  6 neighbors with a Manhattan-distance of 1.  ( nearest neighbors on 6 faces of cell's cube)
        There are 12 neighbors with a Manhattan-distance of 2.  ( additional neighbors in 3 planes containing the cell's cube )
        There are  8 neighbors with a Manhattan-distance of 3.  ( additional nieghbors on the corner vertices of the cell's cube)
        '''
        #  All Twenty-Six neighbors
        for r in ( 1, 2, 3 ):
            for c in ( 1, 2, 3 ):
                for lay in ( 1, 2, 3 ):
                    grid.setCell( r, c, lay, 11 )        
        count = grid.countLiveNeighbors( row = 2, column = 2, layer = 2 )
        self.assertEquals( 26, count )
    
    def testRule(self):
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        nextGrid = grid.nextGeneration()
        self.assertEquals( grid, nextGrid )
        
    def testDieWithZeroNeighbors(self):
        '''
        Live cell dies when there are no neighbors
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        nextGrid = grid.nextGeneration()
        self.assertEquals( 0, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
    def testPassWithZeroNeighbors(self):
        '''
        Empty cell stays empty when there are no neighbors
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        nextGrid = grid.nextGeneration()
        self.assertEquals( None, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
                
    def testDieWithOneNeighbor(self):
        '''
        Live cell dies when there is only one neighbor
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        
        nextGrid = grid.nextGeneration()
        self.assertEquals( 0, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
    def testPassWithOneNeighbor(self):
        '''
        Empty cell stays empty when there is only one neighbor
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 22 )
        
        nextGrid = grid.nextGeneration()
        self.assertEquals( None, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
    def testTwoNeighbors(self):
        '''
        A live cell lives on when there are two neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        
        nextGrid = grid.nextGeneration()
        # Additional convention: Add one to the value of a live cell that stays alive.
        self.assertEquals( 2221, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
      
    def testThreeNeighbors(self):
        '''
        A live cell lives on when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGeneration()
        # Additional convention: Add one to the value of a live cell that stays alive.
        self.assertEquals( 2221, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
      
    def testThreeNeighborsSeed(self):
        '''
        A empty gets a seed when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGeneration()
        self.assertEquals( 1, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
      
    def testThreeNeighborsRevive(self):
        '''
        A dead gets a seed when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 0 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGeneration()
        self.assertEquals( 1, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
    
    def testDieWithFourNeighbors(self):
        '''
        Live cell with four neighbors dies. (Crowded out)
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        grid.setCell( row = 1, column = 2, layer = 1, value = 121 )
        
        nextGrid = grid.nextGeneration()
        self.assertEquals( 0, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
    def testPassWithFourNeighbors(self):
        '''
        Empty cell with four neighbors stays empty. 
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        grid.setCell( row = 1, column = 2, layer = 1, value = 121 )
        
        nextGrid = grid.nextGeneration()
        self.assertEquals( None, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
    def testRandom(self):
        '''
        Randomly populate a grid
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        nextGrid = grid.seedAtRandom( percentage = 50 )
        self.assertFalse( nextGrid.isAllDead(), 'Should be about 50 % populated' )
        
    def testFlasher(self):
        '''
        See if the rule generates the "flasher" pattern
        ''' 
        grid = Grid3D( rows = 5, columns = 5, layers = 5 )
        grid.setCell( row = 3, column = 2, layer = 3, value = 3230 )
        grid.setCell( row = 3, column = 3, layer = 3, value = 3330 )
        grid.setCell( row = 3, column = 4, layer = 3, value = 3430 )
      
        secondGrid = grid.nextGeneration()
        # Following follows the 2D pattern - Cells are seeded the column orthogonal to the row.
        self.assertEquals(    1, secondGrid.getCell( row = 2, column = 3, layer = 3 ) )
        self.assertEquals( 3331, secondGrid.getCell( row = 3, column = 3, layer = 3 ) )  # Middle cell stays alive.
        self.assertEquals(    1, secondGrid.getCell( row = 4, column = 3, layer = 3 ) )
        
        #  As in 2D these cells die (They have less than 2 neighbors at this point.)
        self.assertEquals(       0, secondGrid.getCell( row = 3, column = 2, layer = 3 ) )
        self.assertEquals(       0, secondGrid.getCell( row = 3, column = 4, layer = 3 ) )
             
        # But we will also get seeds in the third dimension
        self.assertEquals(       1, secondGrid.getCell( row = 3, column = 3, layer = 2 ) )
        self.assertEquals(       1, secondGrid.getCell( row = 3, column = 3, layer = 4 ) )
        # Including the diagonals
        self.assertEquals(       1, secondGrid.getCell( row = 2, column = 3, layer = 2 ) )
        self.assertEquals(       1, secondGrid.getCell( row = 4, column = 3, layer = 2 ) )
        self.assertEquals(       1, secondGrid.getCell( row = 2, column = 3, layer = 4 ) )
        self.assertEquals(       1, secondGrid.getCell( row = 4, column = 3, layer = 4 ) )
        
        thirdGrid = secondGrid.nextGeneration()
        # Frankly, things are a little complicated at this point; and I cannot predict the outcome.
        thirdGrid.printLiveCells( liveMessage = 'Third Grid Live Cell' )
        
    def testNexGenerationViaSelectedRule(self):
        '''
        Enable different rules to be selected.
        '''
        '''
        A dead gets a seed when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 0 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGenerationViaSelectedRules( grid.rule2, grid.countLiveNeighbors )
        self.assertEquals( 1, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )

        
    def testNexGenerationWithFourRules(self):
        '''
        Enable different rules to be defined.
        '''
        
        def thisSeedRule( cellValue, nNeighbors ):          # Define a local seed rule 
            if nNeighbors == 3:   
                return True                 # Curiously, exactly 3 neighbors makes a seed
            else:
                return False
            
        def thisKillRule( cellValue, nNeighbors ):          # Define a local kill rule
            if nNeighbors > 3 or nNeighbors < 2:
                return True
            else:
                return False

        # Following re-runs the tests above but with these local rules in place of Grid3D's built-in rules.
        # See Grid3D.rule3()
        '''
        Live cell dies when there are no neighbors
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 0, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        '''
        Empty cell stays empty when there are no neighbors
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( None, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
                
        '''
        Live cell dies when there is only one neighbor
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 0, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
        '''
        Empty cell stays empty when there is only one neighbor
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 22 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( None, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
        '''
        A live cell lives on when there are two neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 2221, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
      
        '''
        A live cell lives on when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 2221, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
      
        '''
        A empty gets a seed when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 1, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
      
        '''
        A dead gets a seed when there are three neighbors  
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 0 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 1, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
    
        '''
        Live cell with four neighbors dies. (Crowded out)
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 2, value = 2220 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        grid.setCell( row = 1, column = 2, layer = 1, value = 121 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( 0, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )
        
        '''
        Empty cell with four neighbors stays empty. 
        '''
        grid = Grid3D( rows = 3, columns = 3, layers = 3 )
        grid.setCell( row = 2, column = 2, layer = 1, value = 221 )
        grid.setCell( row = 2, column = 2, layer = 3, value = 223 )
        grid.setCell( row = 1, column = 2, layer = 3, value = 123 )
        grid.setCell( row = 1, column = 2, layer = 1, value = 121 )
        
        nextGrid = grid.nextGenerationViaSelectedRuleSet( grid.rule3, grid.countLiveNeighbors, thisSeedRule, thisKillRule)
        self.assertEquals( None, nextGrid.getCell( row = 2, column = 2, layer = 2 ) )

if __name__ == "__main__":
    #import sys;sys.argv = ['', 'Test.testName']
    unittest.main()

Grid3D.py

'''
Created on Apr 5, 2012

@author: MichiPUG
'''

class Grid3D( object ):
    '''
    The Grid is the game space.
    It has rows and columns and layers.
    '''

    def __init__( self,  rows = 5, columns = 5, layers = 5  ):
        '''
        Constructor
        '''
        self.rows = rows
        self.columns = columns
        self.layers = layers
        self.grid = {}            # Note: grid is a dictionary

    def setCell(self, row = 1, column = 1, layer = 1, value = 1 ):
        '''
        This trick with modulo arithmetic
          makes the grid wrap around at all six faces.
        This feature is often called a "toroid" or "toroidal"
          because with a two dimensional grid,
          the cells can be mapped onto the surface of a 3D toroid.
        Note: If any dimension is set to one,
          there could be strange effects
          because cells might see themselves mirrored (and counted as own neighbor)
          in that dimension.
        '''
        xRow = 1 + ( row % self.rows )
        xColumn = 1 + ( column % self.columns )
        xLayer = 1 + ( layer % self.layers )
        self.grid[ (xRow,xColumn,xLayer ) ] = value     # The dictionary key is a tuple.
        
    def getCell(self, row = 1, column =1, layer = 1 ):
        # See note under setCell().
        xRow = 1 + ( row % self.rows )
        xColumn = 1 + ( column % self.columns )
        xLayer = 1 + ( layer % self.layers )
        if self.grid.has_key( (xRow,xColumn,xLayer) ):
            value = self.grid[ (xRow,xColumn,xLayer ) ]
            return value
        else:
            return None
        
    def __eq__(self, otherGrid ):
        # This method enables equals comparison of two grids.
        for row in range( self.rows ):
            for column in range( self.columns ):
                for layer in range( self.layers ):
                    if self.getCell( row, column, layer ) != otherGrid.getCell( row, column, layer ):
                        return False
                    
        return True
    
    
    def countLiveNeighbors(self, row, column, layer = 1 ):
        '''
        The adjacent neighbors include all cells with a row, column, or layer index plus or minus one the cell in question.
        This version includes all 26 neighbors.                 ( 9 on layer above, 9 on layer below, and 8 on same layer )
        There are  6 neighbors with a Manhattan-distance of 1.  ( nearest neighbors on 6 faces of cell's cube)
        There are 12 neighbors with a Manhattan-distance of 2.  ( additional neighbors in 3 planes containing the cell's cube )
        There are  8 neighbors with a Manhattan-distance of 3.  ( additional nieghbors on the corner vertices of the cell's cube)
        '''

        '''
        Eventually, we may want different versions.
        - Count only nearest neighbors. (Manhattan-distance == 1)
        - Count only certain neighbors (e.g. same layer, above, below)
        - Don't wrap over the edges.
        '''
        count = 0
        for r in ( row - 1, row, row + 1 ):
            for c in ( column - 1, column, column + 1 ):
                for lay in ( layer - 1, layer, layer + 1):
                    value = self.getCell( row = r, column = c, layer = lay )
                    if r == row and c == column and lay == layer:
                        pass
                    elif value == None :
                        pass
                    elif value == 0 :
                        pass
                    else:
                        count += 1
        return count
                    
 
    def nextGeneration( self ):
        ''' 
        Create the next generation of the grid 
        by applying the rule to the cells in the current generation.
        '''
        nextGrid = Grid3D(  rows = self.rows, columns = self.columns, layers = self.layers  )
        for rowIndex in range( self.rows ):
            for columnIndex in range( self.columns ):
                for layerIndex in range(  self.layers ):
                    value = self.rule( rowIndex, columnIndex, layerIndex )
                    nextGrid.setCell( rowIndex, columnIndex, layerIndex, value )
                
        return nextGrid
   
    def nextGenerationViaSelectedRules( self, selectedRule, selectedNeighborCounter ):
        ''' 
        Enable non-standard rules.
        Create the next generation of the grid 
        by applying the selected rule to the cells in the current generation.
        '''
        nextGrid = Grid3D(  rows = self.rows, columns = self.columns, layers = self.layers  )
        for rowIndex in range( self.rows ):
            for columnIndex in range( self.columns ):
                for layerIndex in range(  self.layers ):
                    value = selectedRule( rowIndex, columnIndex, layerIndex, selectedNeighborCounter )
                    nextGrid.setCell( rowIndex, columnIndex, layerIndex, value )
                
        return nextGrid
   
   
    def nextGenerationViaSelectedRuleSet( self, selectedRule, selectedNeighborCounter, seedRule, killRule ):
        ''' 
        Enable non-standards rules.
        Create the next generation of the grid 
        by applying the selected rule to the cells in the current generation.
        '''
        nextGrid = Grid3D(  rows = self.rows, columns = self.columns, layers = self.layers  )
        for rowIndex in range( self.rows ):
            for columnIndex in range( self.columns ):
                for layerIndex in range(  self.layers ):
                    value = selectedRule( rowIndex, columnIndex, layerIndex, selectedNeighborCounter, seedRule, killRule )
                    nextGrid.setCell( rowIndex, columnIndex, layerIndex, value )
                
        return nextGrid
   
    def rule( self, row, column, layer  ):
        '''
        Modified Game of Life rule 
        -- We are distinguishing dead from empty to enable strength or age values and visualization.
        This version of the rule distinguishes dead from empty; but is otherwise the same as the 2D rule.
        '''
        nLiveNeighbors = self.countLiveNeighbors( row, column, layer )

        value = self.getCell( row, column, layer )
        
        if value == None and nLiveNeighbors == 3 :
            return 1
        elif value == None:
            return None
        elif value > 0:
            if nLiveNeighbors == 2 or nLiveNeighbors == 3:
                return value + 1               # Live longer and prosper
            else:
                return 0                       # Too few or too many neighbors
        else:  # I am dead
            if nLiveNeighbors == 3:
                return 1                       # Curiously, exactly three neighbors make a seed
            else:
                return 0
    
   
    def rule2( self, row, column, layer, selectedNeighborCounter  ):
        '''
        This version parameterizes countLiveNeighbors() function.
        '''
        nLiveNeighbors = selectedNeighborCounter( row, column, layer )

        value = self.getCell( row, column, layer )
        
        if value == None and nLiveNeighbors == 3 :
            return 1
        elif value == None:
            return None
        elif value > 0:
            if nLiveNeighbors == 2 or nLiveNeighbors == 3:
                return value + 1               # Live longer and prosper.
            else:
                return 0                       # Too few or too many neighbors.
        else:  # I am dead
            if nLiveNeighbors == 3:
                return 1                       # Curiously, exactly three neighbors make a seed.
            else:
                return 0
    
   
    def rule3( self, row, column, layer, selectedNeighborCounter, seedRule, killRule  ):
        '''
        This version parameterizes countLiveNeighbors(), seedRule(), and killRule().
        '''
        nLiveNeighbors = selectedNeighborCounter( row, column, layer )

        cellValue = self.getCell( row, column, layer )
        
        #  Note: If seedRule() is True, killRule() is ignored.     
        if seedRule( cellValue, nLiveNeighbors ):
            
            if cellValue == None:
                return 1                             # Seed
            else:
                return  cellValue + 1                # Live longer and prosper       
        
        
        elif killRule( cellValue, nLiveNeighbors ):
            if cellValue == None:
                return None                          # Stay empty
            else:
                return 0                             # Die
            
        else: 
            if cellValue == None:
                return None
            else: 
                return cellValue + 1          # Live longer and prosper 
       
    
    def printGrid( self, gridMessage = '' ):
        ''' 
        Print utility for debug
        '''
        for layerIndex in range(  1, self.layers + 1 ):
            for rowIndex in range( 1, self.rows + 1  ):                        # Use 1 .. rows rather than than 0 .. rows-1
                for columnIndex in range( 1, self.columns + 1 ):
                    cellValue = self.getCell( row = rowIndex, column = columnIndex, layer = layerIndex )
                    self.printCell( row = rowIndex, column = columnIndex, layer = layerIndex, value = cellValue, message = gridMessage )
                    
    def printCell(self,  row, column, layer, value, message = '' ):
        print( message+ ': Layer, Row, Column =' , layer, row, column, '==>', value )
        
    def printLiveCells(self, liveMessage = 'Live cells' ):
        ''' 
        Another Print utility for debug
        '''
        for layerIndex in range(  1, self.layers + 1 ):
            for rowIndex in range( 1, self.rows + 1  ):                        # Use 1 .. rows rather than than 0 .. rows-1
                for columnIndex in range( 1, self.columns + 1 ):
                    cellValue = self.getCell( row = rowIndex, column = columnIndex, layer = layerIndex )
                    if cellValue == None:
                        pass
                    elif cellValue == 0:
                        pass
                    else:
                        self.printCell( row = rowIndex, column = columnIndex, layer = layerIndex, value = cellValue, message = liveMessage )
        
                
    def isAllDead( self ):
        ''' 
        Test for no living cells. (Game Over)
        '''
        nEmpty = 0
        for layerIndex in range(  1, self.layers + 1 ):
            for rowIndex in range( 1, self.rows + 1  ):                        # Use 1 .. rows rather than than 0 .. rows-1
                for columnIndex in range( 1, self.columns + 1 ):
                    cellValue = self.getCell( row = rowIndex, column = columnIndex, layer = layerIndex )
                    if cellValue == None:
                        nEmpty += 1
                    elif cellValue > 0:
                        #self.printCell( message = 'Alive at', row = rowIndex, column = columnIndex, layer = layerIndex, value = cellValue )
                        return False
                    
        print( '. nEmpty = ', nEmpty )
        return True
    
    def seedAtRandom(self, percentage ):
        '''
        Generate a new grid with seed value = 1 at random locations
        '''
        import random
        random.seed() 
        
        nextGrid = Grid3D(  rows = self.rows, columns = self.columns, layers = self.layers  )
        for rowIndex in range( 1, self.rows + 1  ):                        # Use 1 .. rows rather than than 0 .. rows-1
            for columnIndex in range( 1, self.columns + 1 ):
                for layerIndex in range(  1, self.layers + 1 ):
                    rand = random.randint(0,100)
                    if rand >= percentage:
                        nextGrid.setCell( row = rowIndex, column = columnIndex, layer = layerIndex, value = 1 )
                        #print('. Setting: ', rowIndex, columnIndex, layerIndex )
        
        return nextGrid
         
"""   
    def grid3DfromFile( self, file_name ):
        textFile = open( file_name )
        rows = [ line.rstrip( '\r\n' ) for line in textFile ]
        textFile.close( )
        
        width  = max( len( row ) for row in rows )
        grid = Grid3D( len( rows ), width )
    
        for ( y, row ) in enumerate( rows ):
            for ( x, cell ) in enumerate( row ):
                if cell == '*':
                    grid.rows[y][x] = 1
    
        return grid
"""    

Code from May Meeting – gtk_life.py

This is the code from the May meeting to display the boards from the Game of Life Application in gtk

#!/usr/bin/python

import gtk
import gobject

import sys

import grid

class life_ui(object):
    cell_size =  16
    cell_border = 2

    def __init__(self, file_name):

        self.grid = grid.LifeGrid(file_name)

        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_title(file_name)
        self.window.connect('delete_event', self.program_exit)
        self.window.show()

        self.drawing = gtk.DrawingArea()
        self.drawing.set_size_request(self.
            grid.width * (self.cell_size + self.cell_border), 
            self.grid.height * (self.cell_size + self.cell_border))
        self.drawing.connect('expose-event', self.expose_cb)
        self.drawing.show()


        self.hbox = gtk.HBox()
        self.hbox.add(self.drawing)
        self.hbox.show()

        self.window.add(self.hbox)

        style = self.drawing.get_style()
        self.gc_on = style.fg_gc[gtk.STATE_NORMAL]
        self.gc_off = style.bg_gc[gtk.STATE_NORMAL]

    def expose_cb(self, window, event):
        self.draw_grid()


    def loop(self):
        gobject.timeout_add(1000, self.next_grid)
        gtk.main()

    def next_grid(self):
        self.grid = self.grid.nextGeneration()
        self.draw_grid()
        return True

    def draw_grid(self):
        for x in xrange(self.grid.width):
            for y in xrange(self.grid.height):
                self.draw_cell(x, y, self.grid.rows[y][x])

    def draw_cell(self, x, y, state):
        if state:
            gc = self.gc_on
        else:
            gc = self.gc_off

        self.drawing.window.draw_rectangle(gc, True, 
            x * (self.cell_size + self.cell_border), 
            y * (self.cell_size + self.cell_border), self.cell_size, self.cell_size)
    

    def program_exit(self, widget, event):
        gtk.main_quit()
        return False

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print "gtk_life.py [file-name]"
        exit(0)

    game = life_ui(sys.argv[1])
    game.loop()
    
 

Code from May Meeting – grid.py

A copy of the source for the Game of Life, updated to include a function “LifeGrid” to read in a starting position for the first generation.

'''
Created on Apr 5, 2012

@author: admin
'''

from itertools import repeat

def LifeGrid(file_name):
    input = open(file_name)
    rows = [line.rstrip('\r\n') for line in input]
    input.close()
    
    width  = max(len(row) for row in rows)
    grid = Grid(len(rows), width)

    for (y, row) in enumerate(rows):
        for (x, cell) in enumerate(row):
            if cell == '*':
                grid.rows[y][x] = 1

    return grid

class Grid(object):
    '''
    classdocs
    '''


    def __init__(self,  height, width ):
        '''
        Constructor
        '''
        self.width = width
        self.height = height
        self.rows = [ list( repeat(0,width) ) for dummy in range(height) ]
        
    def __str__(self):
        """
        Show the grid as text
        """
        output = []
        for row in self.rows:
            line = ''.join( '*' if cell else '-' for cell in row )
            output.append( line )
            
        return '\n'.join( output )+'\n'
    
    def __str1__(self):
        """
        Another way to do it with somewhat more readable code
        """
        output = ''
        for rowIndex in range(self.height):
            for columnIndex in range(self.width):
                output += str( self.rows[rowIndex][columnIndex] )
            output += '\n'
        return output.replace('0', '-').replace('1','*')
    
    def nextGeneration(self):
        nextGrid = Grid( height = self.height, width = self.width )
        for rowIndex in range(self.height):
            for columnIndex in range(self.width):
                nextGrid.rows[rowIndex][columnIndex] = self.rule( row = rowIndex, column = columnIndex)
                
        return nextGrid
    
    def rule(self, row, column ):
        nLiveNeighbors = 0
        for r in range( max(0,row-1), min(self.height, row+2) ):
            for c in range( max(0, column-1 ), min( self.width, column+2) ):
                nLiveNeighbors += self.rows[r][c]
                
        nLiveNeighbors -= self.rows[row][column]     
        
        amAlive = self.rows[row][column]
        if amAlive:
            if nLiveNeighbors == 2 or nLiveNeighbors == 3:
                return 1
            else:
                return 0
        else:
            if nLiveNeighbors == 3:
                return 1
            else:
                return 0
                   
        
                
    
    def __eq__( self, other ):
        return ( str( self ) == str( other ) )
        

Code from April Meeting

Game of Life Minimal Python Implementation to use as a demonstration of displaying a two dimensional object in various graphic frameworks


'''
Created on Apr 5, 2012

@author: admin
'''

from itertools import repeat

class Grid(object):
    '''
    classdocs
    '''


    def __init__(self,  height, width ):
        '''
        Constructor
        '''
        self.width = width
        self.height = height
        self.rows = [ list( repeat(0,width) ) for dummy in range(height) ]
        
    def __str__(self):
        """
        Show the grid as text
        """
        output = []
        for row in self.rows:
            line = ''.join( '*' if cell else '-' for cell in row )
            output.append( line )
            
        return '\n'.join( output )+'\n'
    
    def __str1__(self):
        """
        Another way to do it with somewhat more readable code
        """
        output = ''
        for rowIndex in range(self.height):
            for columnIndex in range(self.width):
                output += str( self.rows[rowIndex][columnIndex] )
            output += '\n'
        return output.replace('0', '-').replace('1','*')
    
    def nextGeneration(self):
        nextGrid = Grid( height = self.height, width = self.width )
        for rowIndex in range(self.height):
            for columnIndex in range(self.width):
                nextGrid.rows[rowIndex][columnIndex] = self.rule( row = rowIndex, column = columnIndex)
                
        return nextGrid
    
    def rule(self, row, column ):
        nLiveNeighbors = 0
        for r in range( max(0,row-1), min(self.height, row+2) ):
            for c in range( max(0, column-1 ), min( self.width, column+2) ):
                nLiveNeighbors += self.rows[r][c]
                
        nLiveNeighbors -= self.rows[row][column]     
        
        amAlive = self.rows[row][column]
        if amAlive:
            if nLiveNeighbors == 2 or nLiveNeighbors == 3:
                return 1
            else:
                return 0
        else:
            if nLiveNeighbors == 3:
                return 1
            else:
                return 0
                   
        
                
    
    def __eq__( self, other ):
        return ( str( self ) == str( other ) )
        

And here are the unit tests.

'''
 Created on Apr 5, 2012

@author: admin
 '''
 import unittest

from grid import Grid

class GridTest(unittest.TestCase):

def testCreateGrid(self):
 grid = Grid( width = 17, height = 19 )
 self.assertEqual( 17, grid.width )
 self.assertEqual( 19, grid.height )

def testGridExtent(self):
 grid = Grid( width = 17, height = 19 )
 self.assertEquals( 19, len(grid.rows ) )
 self.assertEquals( 17, len( grid.rows[0] ) )
 self.assertEquals( 0, grid.rows[0][0] )
 self.assertEquals( 0, grid.rows[19-1][17-1] )

def testDisplayStr(self):
 grid = Grid( height = 3, width = 5 )
 grid.rows[1][1] = 1
 grid.rows[2][4] = 1
 print grid

def testDisplayStr1(self):
 grid = Grid( height = 3, width = 5 )
 grid.rows[1][1] = 1
 grid.rows[2][4] = 1
 self.assertEquals( grid.__str__(), grid.__str1__( )  )

def testRule(self):
 grid = Grid( height = 3, width = 3 )
 nextGrid = grid.nextGeneration()
 self.assertEquals( grid, nextGrid )

def testDieWhenOneNeighbor(self):
 grid = Grid( height = 3, width = 3 )
 grid.rows[1][1] = 1
 nextGrid = grid.nextGeneration()
 self.assertEqual( 0, nextGrid.rows[1][1] )

def testLiveWhenTwoNeighbors(self):
 grid = Grid( height = 3, width = 3 )
 grid.rows[1][0] = 1
 grid.rows[1][1] = 1
 grid.rows[1][2] = 1

nextGrid = grid.nextGeneration()
 self.assertEqual( 0, nextGrid.rows[1][0] )
 self.assertEqual( 1, nextGrid.rows[1][1] )
 self.assertEqual( 0, nextGrid.rows[1][2] )
 print 'testLiveWhenTwoNeighbors'
 print grid
 print
 print nextGrid

def testFlasher(self):
 grid = Grid( height = 3, width = 3 )
 grid.rows[1][0] = 1
 grid.rows[1][1] = 1
 grid.rows[1][2] = 1

 grid2 = grid.nextGeneration()
 grid3 = grid2.nextGeneration()

print 'testFlasher'
 print grid
 print
 print grid2
 print
 print grid3

if __name__ == "__main__":
 #import sys;sys.argv = ['', 'Test.testName']
 unittest.main()

June Meeting Notice

We’re meeting Thursday, June 7th at 7pm, standard time and place (check the home page) and we will continue to build on our Game of Life code to investigate various UI frameworks.

Feel free to come along and participate or observe and learn or teach or any combination.

May Meeting Notice

We’re meeting Thursday, May 3rd at 7pm, and the intention this time is to build on our Game of Life code to investigate various UI frameworks.

Feel free to come along and participate or observe and learn or teach or any combination.

Code from April Meeting – grid.py

'''
Created on Apr 5, 2012

@author: admin
'''

from itertools import repeat

class Grid(object):
    '''
    classdocs
    '''

    def __init__(self,  height, width ):
        '''
        Constructor
        '''
        self.width = width
        self.height = height
        self.rows = [ list( repeat(0,width) ) for dummy in range(height) ]

    def __str__(self):
        """
        Show the grid as text
        """
        output = []
        for row in self.rows:
            line = ''.join( '*' if cell else '-' for cell in row )
            output.append( line )

        return '\n'.join( output )+'\n'

    def __str1__(self):
        """
        Another way to do it with somewhat more readable code
        """
        output = ''
        for rowIndex in range(self.height):
            for columnIndex in range(self.width):
                output += str( self.rows[rowIndex][columnIndex] )
            output += '\n'
        return output.replace('0', '-').replace('1','*')

    def nextGeneration(self):
        nextGrid = Grid( height = self.height, width = self.width )
        for rowIndex in range(self.height):
            for columnIndex in range(self.width):
                nextGrid.rows[rowIndex][columnIndex] = self.rule( row = rowIndex, column = columnIndex)

        return nextGrid

    def rule(self, row, column ):
        nLiveNeighbors = 0
        for r in range( max(0,row-1), min(self.height, row+2) ):
            for c in range( max(0, column-1 ), min( self.width, column+2) ):
                nLiveNeighbors += self.rows[r][c]

        nLiveNeighbors -= self.rows[row][column]

        amAlive = self.rows[row][column]
        if amAlive:
            if nLiveNeighbors == 2 or nLiveNeighbors == 3:
                return 1
            else:
                return 0
        else:
            if nLiveNeighbors == 3:
                return 1
            else:
                return 0

    def __eq__( self, other ):
        return ( str( self ) == str( other ) )

April Meeting – Graphics for Life (Conway)

We’re meeting Thursday, April 5th at 7pm, and the intention this time is to build a bit of code that we can use to investigate various UI frameworks.  We’ve pretty much settled on creating code implementing Conway’s game of life, the cellular automaton created by John Horton Conway, and then proceeding on to various ways of entering and displaying the current state.

Feel free to come along and participate or observe and learn or teach or any combination.