Computer Programming 150
(04/23/2008)
Two Dimension Cellular Automata programs are played out on a two dimensional grid of cells (see below). Cells have “states” – this case one of two states: “on” denoted by “red” cells and “off” denoted by the background color “white”. The program progresses thru discrete time intervals or “generations” with the configuration at the next generation being determined by rules applied to the current generation.
The most famous cellular automata system is the Game of Life. Here cells are born or die at the next generation depending on how many neighbors they have for the current generation. For example if a dead cell has three live neighbors during the current generation, it is born during the next generation. Live cells die if they have two few neighbors (1 or less) or too many neighbor (4 or more). The amazing thing about the Game of Life is the complex behavior these simple rules produce.
A simpler game is Brownian Motion where live cells randomly move from one generation to the next

The Grid50 class uses the two-dimensional array self.grid to store the information display by the cell matrix. The interface uses 1 based indexing to access cells (although the implementation uses 0 based indexing).
#
# File: Grid50.py
# Date: April 22, 2008
#
# Desc: Module for a 50 by 50
cellular grid
class
Grid50:
def __init__(self, win):
#
# Constructor for 50 by 50 cell grid
# located in left side of 640 by 480
window
#
self.win = win
self.x_lo = 39 # LL cell matrix
coordinates
self.y_lo = 39
self.size = 8 # size of cell in
pixels
self.max = 50
self.fColor = "red" # color of “on” cell
self.bColor = "white" # color of “off” cell
self.grid = []
#
# 50 by 50 grid array of integers -
#
for i in range(self.max):
self.grid = self.grid +
[[0]*(self.max)]
def clearGrid(self):
# clear cell matrix and grid array
for y in range(self.max):
for x in range(self.max):
if self.grid[y][x] != 0:
self.grid[y][x] = 0
self.drawOne(x+1,y+1) # use
1 based indexing
def setGrid(self, x, y):
# sets grid(x,y) to 1
# uses 1 based indexing
self.grid[y-1][x-1] = 1
def unsetGrid(self, x, y):
# sets grid(x,y) to 0
# uses 1 based indexing
self.grid[y-1][x-1] = 0
def drawOne(self, x, y):
#
# draws cell at grid(x,y)
# uses 1 based indexing
#
p1 = Point(self.x_lo + (x-1)*self.size,
self.y_lo + (y-1)*self.size)
p2 = Point(self.x_lo + x*self.size,
self.y_lo + y*self.size)
square = Rectangle(p1, p2)
square.setOutline("black")
if self.grid[y-1][x-1] == 1:
square.setFill(self.fColor)
else:
square.setFill(self.bColor)
square.draw(self.win)
def drawAll(self):
# draws all cells
for y in range(self.max):
for x in range(self.max):
p1 = Point(self.x_lo +
x*self.size, self.y_lo + y*self.size)
p2 = Point(self.x_lo +
(x+1)*self.size, self.y_lo + (y+1)*self.size)
square = Rectangle(p1, p2)
square.setOutline("black")
if self.grid[y][x] == 1:
square.setFill(self.fColor)
else:
square.setFill(self.bColor)
square.draw(self.win)
def drawFrame(self):
# draws cell matrix frame
mysize = self.size * self.max + 4
xlo = self.x_lo - 2
ylo = self.y_lo - 2
xhi = xlo + mysize
yhi = ylo + mysize
Line(Point(xlo, ylo), Point(xhi,
ylo)).draw(self.win)
Line(Point(xhi, ylo), Point(xhi,
yhi)).draw(self.win)
Line(Point(xhi, yhi), Point(xlo,
yhi)).draw(self.win)
Line(Point(xlo, yhi), Point(xlo,
ylo)).draw(self.win)
def setPixel(self, x, y):
# uses 1 based indexing to set cell at
coordinates (x,y)
if (0 < x <= self.max) and (0
< y <= self.max):
self.grid[y-1][x-1] = 1
self.drawOne(x,y)
def unsetPixel(self,x, y):
# uses 1 based indexing to unset cell at
coordinates (x,y)
if (0 < x <= self.max) and (0
< y <= self.max):
self.grid[y-1][x-1] = 0
self.drawOne(x,y)
def isPixelOn(self, x, y):
# returns True if grid(x,y) == 1
# uses 1 based indexing
return self.grid[y-1][x-1] == 1
def togglePixel(self, p1):
# used with p1 = win.getMouse()
x = p1.getX()
y = p1.getY()
xhi = self.x_lo + self.size * self.max
yhi = self.y_lo + self.size * self.max
if (self.x_lo <= x < xhi) and
(self.y_lo <= y < yhi):
x = int(x - self.x_lo)/self.size +
1
y = int(y - self.y_lo)/self.size +
1
# uses 1 based indexing
if self.grid[y-1][x-1] == 0:
self.setPixel(x,y)
else:
self.unsetPixel(x, y)
BrownianMotion.py randomly moves the cells on the grid (simulating Brownian motion).
#
# Name:
# File: BrownianMotion.py
# Date: April 22, 2008
#
# Desc: Brownian Motion Demo
from
random import randrange
from
graphics import *
from
button import button
from
Grid50 import *
def
myRandom(x, y):
#
# return xy coordinates of one of 8 cardinal
points
#
n = randrange(8)
if (n == 0):
return x-1, y-1
if (n == 1):
return x , y-1
if (n == 2):
return x+1, y-1
if (n == 3):
return x-1, y
if (n == 4):
return x+1, y
if (n == 5):
return x-1, y+1
if (n == 6):
return x , y+1
if (n == 7):
return x+1, y+1
def
main():
# set up 640 x 480 graphic window
screenWidth = 640
screenHeight = 480
title = "Grid 50 - Brownian
Motion"
win = GraphWin(title, screenWidth,
screenHeight)
win.setCoords(0, 0, screenWidth,
screenHeight)
win.setBackground("white")
# create grid object
grid = Grid50(win)
grid.drawFrame()
grid.drawAll()
# create and activate 5 buttons
quitButton = button(win, Point(500, 50),
80, 20, “Quit”)
quitButton.activate()
clearButton = button(win, Point(500, 80),
80, 20, “Clear”)
clearButton.activate()
captureButton = button(win, Point(500,
110), 80, 20, “Capture”)
captureButton.activate()
stepButton = button(win, Point(500, 140),
80, 20, “Step”)
stepButton.activate()
runButton = button(win, Point(500, 170),
80, 20, “Run”)
runButton.activate()
# set up table to mirror contents of cell
matrix
table = []
for i in range(52):
table = table + [[0]*52]
# event
handling loop
while True:
p1 = win.getMouse()
# toggle cells
if (39 <= p1.getX() < 439) and
(39 <= p1.getY() < 439):
grid.togglePixel(p1)
# capture contents of cell matrix to table
elif captureButton.clicked(p1):
for y in range(1,51):
for x in range(1, 51):
if grid.isPixelOn(x, y):
table[y][x] = 1
else:
table[y][x] = 0
# clear grid
elif clearButton.clicked(p1):
grid.clearGrid()
# execute one step
elif stepButton.clicked(p1):
for y in range(1, 51):
for x in range(1,51):
if table[y][x] == 1: # if cell set
table[y][x] = 0 # randomly move it
grid.unsetPixel(x, y)
x1, y1 = myRandom(x,y)
table[y1][x1] = 2 # mark as new cell
grid.setPixel(x1, y1)
for y in range(1, 51):
for x in range(1,51): # clean up - remove new cell mark
if table[y][x] == 2:
table[y][x] = 1
# execute 10 steps
elif runButton.clicked(p1):
for i in range(10):
for y in range(1, 51):
for x in range(1,51):
if table[y][x] ==
1: # if cell set
table[y][x] =
0 # randomly move it
grid.unsetPixel(x,
y)
x1, y1 = myRandom(x,y)
table[y1][x1] =
2 # mark as new cell
grid.setPixel(x1,
y1)
for y in range(1, 51):
for x in range(1,51): # clean up - remove new cell mark
if table[y][x] ==
2:
table[y][x] = 1
# quit
elif quitButton.clicked(p1):
break
win.close()
main()