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