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.
''' 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()
''' 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 """
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()
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 ) )
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()
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.
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.
''' 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 ) )
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.