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()