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