annotate Life.py @ 47:2b0e229e163c

Chapter 7, exercise 2. R-Pentomino game of life.
author Brian Neal <bgneal@gmail.com>
date Thu, 25 Jul 2013 21:53:44 -0500
parents
children 98eb01502cf5
rev   line source
bgneal@47 1 """ Code example from Complexity and Computation, a book about
bgneal@47 2 exploring complexity science with Python. Available free from
bgneal@47 3
bgneal@47 4 http://greenteapress.com/complexity
bgneal@47 5
bgneal@47 6 Copyright 2011 Allen B. Downey.
bgneal@47 7 Distributed under the GNU General Public License at gnu.org/licenses/gpl.html.
bgneal@47 8
bgneal@47 9 Modified by Brian Neal to add a variable delay to the animation and to provide
bgneal@47 10 the cval parameter to convolve.
bgneal@47 11
bgneal@47 12 """
bgneal@47 13
bgneal@47 14 import numpy
bgneal@47 15 import scipy.ndimage
bgneal@47 16
bgneal@47 17 import matplotlib
bgneal@47 18 matplotlib.use('TkAgg')
bgneal@47 19 import matplotlib.pyplot as pyplot
bgneal@47 20
bgneal@47 21
bgneal@47 22 class Life(object):
bgneal@47 23 """Implements Conway's Game of Life.
bgneal@47 24
bgneal@47 25 n: the number of rows and columns
bgneal@47 26 """
bgneal@47 27
bgneal@47 28 def __init__(self, n, mode='wrap', cval=0.0, random=False):
bgneal@47 29 """Attributes:
bgneal@47 30 n: number of rows and columns
bgneal@47 31 mode: how border conditions are handled
bgneal@47 32 array: the numpy array that contains the data.
bgneal@47 33 weights: the kernel used for convolution
bgneal@47 34 """
bgneal@47 35 self.n = n
bgneal@47 36 self.mode = mode
bgneal@47 37 self.cval = cval
bgneal@47 38 if random:
bgneal@47 39 self.array = numpy.random.random_integers(0, 1, (n, n))
bgneal@47 40 else:
bgneal@47 41 self.array = numpy.zeros((n, n), numpy.int8)
bgneal@47 42
bgneal@47 43 self.weights = numpy.array([[1,1,1],
bgneal@47 44 [1,10,1],
bgneal@47 45 [1,1,1]])
bgneal@47 46
bgneal@47 47 def add_glider(self, x=0, y=0):
bgneal@47 48 coords = [(0,1), (1,2), (2,0), (2,1), (2,2)]
bgneal@47 49 for i, j in coords:
bgneal@47 50 self.array[x+i, y+j] = 1
bgneal@47 51
bgneal@47 52 def loop(self, steps=1):
bgneal@47 53 """Executes the given number of time steps."""
bgneal@47 54 [self.step() for i in xrange(steps)]
bgneal@47 55
bgneal@47 56 def step(self):
bgneal@47 57 """Executes one time step."""
bgneal@47 58 con = scipy.ndimage.filters.convolve(self.array,
bgneal@47 59 self.weights,
bgneal@47 60 mode=self.mode,
bgneal@47 61 cval=self.cval)
bgneal@47 62
bgneal@47 63 boolean = (con==3) | (con==12) | (con==13)
bgneal@47 64 self.array = numpy.int8(boolean)
bgneal@47 65
bgneal@47 66
bgneal@47 67 class LifeViewer(object):
bgneal@47 68 """Generates an animated view of the grid."""
bgneal@47 69 def __init__(self, life, cmap=matplotlib.cm.gray_r, delay=1000):
bgneal@47 70 self.life = life
bgneal@47 71 self.cmap = cmap
bgneal@47 72 self.delay = delay
bgneal@47 73
bgneal@47 74 self.fig = pyplot.figure()
bgneal@47 75 pyplot.axis([0, life.n, 0, life.n])
bgneal@47 76 pyplot.xticks([])
bgneal@47 77 pyplot.yticks([])
bgneal@47 78
bgneal@47 79 self.pcolor = None
bgneal@47 80 self.update()
bgneal@47 81
bgneal@47 82 def update(self):
bgneal@47 83 """Updates the display with the state of the grid."""
bgneal@47 84 if self.pcolor:
bgneal@47 85 self.pcolor.remove()
bgneal@47 86
bgneal@47 87 a = self.life.array
bgneal@47 88 self.pcolor = pyplot.pcolor(a, cmap=self.cmap)
bgneal@47 89 self.fig.canvas.draw()
bgneal@47 90
bgneal@47 91 def animate(self, steps=10):
bgneal@47 92 """Creates the GUI and then invokes animate_callback.
bgneal@47 93
bgneal@47 94 Generates an animation with the given number of steps.
bgneal@47 95 """
bgneal@47 96 self.steps = steps
bgneal@47 97 self.fig.canvas.manager.window.after(self.delay, self.animate_callback)
bgneal@47 98 pyplot.show()
bgneal@47 99
bgneal@47 100 def animate_callback(self):
bgneal@47 101 """Runs the animation."""
bgneal@47 102 for i in range(self.steps):
bgneal@47 103 self.life.step()
bgneal@47 104 self.update()
bgneal@47 105
bgneal@47 106
bgneal@47 107 def main(script, n=20, *args):
bgneal@47 108
bgneal@47 109 n = int(n)
bgneal@47 110
bgneal@47 111 life = Life(n, random=False)
bgneal@47 112 life.add_glider()
bgneal@47 113 viewer = LifeViewer(life)
bgneal@47 114 viewer.animate(steps=100)
bgneal@47 115
bgneal@47 116
bgneal@47 117 if __name__ == '__main__':
bgneal@47 118 import sys
bgneal@47 119
bgneal@47 120 profile = False
bgneal@47 121 if profile:
bgneal@47 122 import cProfile
bgneal@47 123 cProfile.run('main(*sys.argv)')
bgneal@47 124 else:
bgneal@47 125 main(*sys.argv)