bgneal@47: """ Code example from Complexity and Computation, a book about bgneal@47: exploring complexity science with Python. Available free from bgneal@47: bgneal@47: http://greenteapress.com/complexity bgneal@47: bgneal@47: Copyright 2011 Allen B. Downey. bgneal@47: Distributed under the GNU General Public License at gnu.org/licenses/gpl.html. bgneal@47: bgneal@47: Modified by Brian Neal to add a variable delay to the animation and to provide bgneal@47: the cval parameter to convolve. bgneal@47: bgneal@47: """ bgneal@47: bgneal@47: import numpy bgneal@47: import scipy.ndimage bgneal@47: bgneal@47: import matplotlib bgneal@47: matplotlib.use('TkAgg') bgneal@47: import matplotlib.pyplot as pyplot bgneal@47: bgneal@47: bgneal@47: class Life(object): bgneal@47: """Implements Conway's Game of Life. bgneal@47: bgneal@47: n: the number of rows and columns bgneal@47: """ bgneal@47: bgneal@47: def __init__(self, n, mode='wrap', cval=0.0, random=False): bgneal@47: """Attributes: bgneal@47: n: number of rows and columns bgneal@47: mode: how border conditions are handled bgneal@47: array: the numpy array that contains the data. bgneal@47: weights: the kernel used for convolution bgneal@47: """ bgneal@47: self.n = n bgneal@47: self.mode = mode bgneal@47: self.cval = cval bgneal@47: if random: bgneal@47: self.array = numpy.random.random_integers(0, 1, (n, n)) bgneal@47: else: bgneal@47: self.array = numpy.zeros((n, n), numpy.int8) bgneal@47: bgneal@47: self.weights = numpy.array([[1,1,1], bgneal@47: [1,10,1], bgneal@47: [1,1,1]]) bgneal@47: bgneal@47: def add_glider(self, x=0, y=0): bgneal@47: coords = [(0,1), (1,2), (2,0), (2,1), (2,2)] bgneal@47: for i, j in coords: bgneal@47: self.array[x+i, y+j] = 1 bgneal@47: bgneal@47: def loop(self, steps=1): bgneal@47: """Executes the given number of time steps.""" bgneal@47: [self.step() for i in xrange(steps)] bgneal@47: bgneal@47: def step(self): bgneal@47: """Executes one time step.""" bgneal@47: con = scipy.ndimage.filters.convolve(self.array, bgneal@47: self.weights, bgneal@47: mode=self.mode, bgneal@47: cval=self.cval) bgneal@47: bgneal@47: boolean = (con==3) | (con==12) | (con==13) bgneal@47: self.array = numpy.int8(boolean) bgneal@47: bgneal@47: bgneal@47: class LifeViewer(object): bgneal@47: """Generates an animated view of the grid.""" bgneal@47: def __init__(self, life, cmap=matplotlib.cm.gray_r, delay=1000): bgneal@47: self.life = life bgneal@47: self.cmap = cmap bgneal@47: self.delay = delay bgneal@47: bgneal@47: self.fig = pyplot.figure() bgneal@47: pyplot.axis([0, life.n, 0, life.n]) bgneal@47: pyplot.xticks([]) bgneal@47: pyplot.yticks([]) bgneal@47: bgneal@47: self.pcolor = None bgneal@47: self.update() bgneal@47: bgneal@47: def update(self): bgneal@47: """Updates the display with the state of the grid.""" bgneal@47: if self.pcolor: bgneal@47: self.pcolor.remove() bgneal@47: bgneal@47: a = self.life.array bgneal@47: self.pcolor = pyplot.pcolor(a, cmap=self.cmap) bgneal@47: self.fig.canvas.draw() bgneal@47: bgneal@47: def animate(self, steps=10): bgneal@47: """Creates the GUI and then invokes animate_callback. bgneal@47: bgneal@47: Generates an animation with the given number of steps. bgneal@47: """ bgneal@47: self.steps = steps bgneal@47: self.fig.canvas.manager.window.after(self.delay, self.animate_callback) bgneal@47: pyplot.show() bgneal@47: bgneal@47: def animate_callback(self): bgneal@47: """Runs the animation.""" bgneal@47: for i in range(self.steps): bgneal@47: self.life.step() bgneal@47: self.update() bgneal@47: bgneal@47: bgneal@47: def main(script, n=20, *args): bgneal@47: bgneal@47: n = int(n) bgneal@47: bgneal@47: life = Life(n, random=False) bgneal@47: life.add_glider() bgneal@47: viewer = LifeViewer(life) bgneal@47: viewer.animate(steps=100) bgneal@47: bgneal@47: bgneal@47: if __name__ == '__main__': bgneal@47: import sys bgneal@47: bgneal@47: profile = False bgneal@47: if profile: bgneal@47: import cProfile bgneal@47: cProfile.run('main(*sys.argv)') bgneal@47: else: bgneal@47: main(*sys.argv)