Conway's Game of Life In Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | '''
Created on 6 June 2012
@author: bakera
Each cell needs to be considered with neighbours
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
http://en.wikipedia.org/wiki/Conway's_Game_of_Life
'''
import numpy as np
import time
import sys
from ctypes import *
def check(grid, row,col, countNumberOfAdjoiningCellsActive, verbose=False):
try:
#if verbose:
# print 'before row %d, col %d, value %d, count %d' % (row, col, grid[row, col], len(countNumberOfAdjoiningCellsActive))
if (col >= 0) and (row >=0): # protect
if grid[row,col] != 0:
countNumberOfAdjoiningCellsActive.append(grid[row,col])
if verbose:
print 'after row %d, col %d, value %d, count %d' % (row, col, grid[row, col], len(countNumberOfAdjoiningCellsActive))
except :
pass
def isalive((r,c), (lr,lc), grid, location, verbose= False):
''' apply rules against the r,c based on the local grid[lr,lc]
Parameters
------------
all parameters are keyword parameters
(r,c) : interger tuple
represents the location of the cell in the global matrix
(lr,lc) : integer tuple
represents the location in the local grid matrix
grid : matrix
represents the neighbours to analyse
location: string
either corner, edge, top or body
'''
countNumberOfAdjoiningCellsActive = []
check(grid, lr, lc+1, countNumberOfAdjoiningCellsActive, verbose)
check(grid, lr, lc-1, countNumberOfAdjoiningCellsActive, verbose )
check(grid, lr+1, lc, countNumberOfAdjoiningCellsActive, verbose )
check(grid, lr+1, lc+1, countNumberOfAdjoiningCellsActive, verbose )
check(grid, lr+1, lc-1, countNumberOfAdjoiningCellsActive, verbose )
check(grid, lr-1, lc, countNumberOfAdjoiningCellsActive, verbose )
check(grid, lr-1, lc+1, countNumberOfAdjoiningCellsActive, verbose )
check(grid, lr-1, lc-1, countNumberOfAdjoiningCellsActive, verbose )
if verbose:
if len(countNumberOfAdjoiningCellsActive) != 0:
print 'value %d, countNumberOfAdjoiningCellsActive %d' % (grid[lr,lc], len(countNumberOfAdjoiningCellsActive))
isAlive = False
#Any live cell with fewer than two live neighbours dies, as if caused by under-population.
#Any live cell with more than three live neighbours dies, as if by overcrowding.
if grid[lr,lc] == 1 and (len(countNumberOfAdjoiningCellsActive) < 2
or len(countNumberOfAdjoiningCellsActive) > 3) : # under or over populated then die out
isAlive = False
#Any live cell with two or three live neighbours lives on to the next generation.
elif grid[lr,lc] == 1 and (len(countNumberOfAdjoiningCellsActive) == 2 or len(countNumberOfAdjoiningCellsActive) == 3):
isAlive = True
#Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
elif grid[lr,lc] == 0 and (len(countNumberOfAdjoiningCellsActive) == 3):
isAlive = True
return isAlive
def iterate_grid(a, (p,q)):
''' generate the next sub matrix from the original matrix
yield the sub matrix back to be consumed
Parameters
----------
all parameters are keyword parameters
a : parent matrix
represents the larger parent matrix
(p,q): tuple
represents the dimensions of each sub
matrix to generate
Returns
-------
yields the next sub matrix
'''
row, col = np.shape(a)
#print row,col
for (r,c) in ((r,c) for r in np.arange(row-p+2) for c in np.arange(col-q+2)):
if r ==0 and c == 0: #corner
yield (r,r+q, c,c+q), (r,c), (0,0), np.matrix(a[r:r+q, c:c+q]), 'corner'
elif r ==0 and c != 0: #top
yield (r,r+q, c-1,c+q), (r,c), (0,1), np.matrix(a[r:r+q, c-1:c+q]) , 'top'
elif r !=0 and c == 0: # edge,
yield (r-1,r+q, c,c+q), (r,c), (1,0), np.matrix(a[r-1:r+q, c:c+q]) , 'edge'
else: # normal body
yield (r-1,r+p, c-1,c+q), (r,c), (1,1), np.matrix(a[r-1:r+p, c-1:c+q]) , 'body'
def prettyprint(state):
colours = {1: 'red', 2: 'red', 3: 'red', 4: 'red', 5: 'red', 6: 'red'}
colour_map = {1: 10, 2: 12, 3: 13, 4: 14, 5: 15, 6: 9, 0:5} # empty cells colour black
if (not (sys.platform == 'linux2')):windll.Kernel32.GetStdHandle.restype = c_ulong
if (not (sys.platform == 'linux2')):h = windll.Kernel32.GetStdHandle(c_ulong(0xfffffff5))
for i, row in enumerate(state.flat):
i = i + 1
color = colour_map[row]
if (not (sys.platform == 'linux2')):
windll.Kernel32.SetConsoleTextAttribute(h, color)
r, c = np.shape(state)
if i == 0:
sys.stdout.write(" %d" % (row))
elif (i % 10 == 0) :
sys.stdout.write(" %d\n" % (row)) # include 2 spaces for the twissler
sys.stdout.flush()
else:
sys.stdout.write(" %d" % (row)) # include 2 spaces for the twissler
if (not (sys.platform == 'linux2')):windll.Kernel32.SetConsoleTextAttribute(h, 15) # return to white
def test_sharing_array(model, steps=10, verbose=False):
start = np.loadtxt(model)
next = np.loadtxt('empty.dat')
#print start
for step in np.arange(steps):
time.sleep(1)
prettyprint(start)
for (r1,r2,c1,c2), (r,c), (lr,lc), grid, location in iterate_grid(start, (2,2)): # data is a 2-D array
# r,c - location in the parent matrix
# lr, lc - location in the child grid of the cell to evaluate
#print (r,c), (lr,lc), grid, grid[lr,lc], location
if isalive((r,c), (lr,lc), grid, location, verbose):
next[r,c] = 1
else:
next[r,c] = 0
start = next.copy() # independent copy of next
if __name__ == '__main__':
verboseFlag = False
test_sharing_array('glider.dat', 5, verboseFlag)
glider.dat
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 1 0 1 0 0 0 0 0 0
0 0 1 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0
|