view Life.py @ 48:98eb01502cf5 tip

Follow up to last commit. Re-orient the r-pentomino. Added progress display.
author Brian Neal <bgneal@gmail.com>
date Wed, 31 Jul 2013 20:37:12 -0500
parents 2b0e229e163c
children
line wrap: on
line source
""" 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,
                    show_progress=None):
        """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
        self.show_progress = show_progress
        self.steps = 0

        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."""
        for i in xrange(steps):
            self.step()

    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)

        self.steps += 1
        if self.show_progress and self.steps % self.show_progress == 0:
            print self.steps


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)