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@48
|
28 def __init__(self, n, mode='wrap', cval=0.0, random=False,
|
bgneal@48
|
29 show_progress=None):
|
bgneal@47
|
30 """Attributes:
|
bgneal@47
|
31 n: number of rows and columns
|
bgneal@47
|
32 mode: how border conditions are handled
|
bgneal@47
|
33 array: the numpy array that contains the data.
|
bgneal@47
|
34 weights: the kernel used for convolution
|
bgneal@47
|
35 """
|
bgneal@47
|
36 self.n = n
|
bgneal@47
|
37 self.mode = mode
|
bgneal@47
|
38 self.cval = cval
|
bgneal@48
|
39 self.show_progress = show_progress
|
bgneal@48
|
40 self.steps = 0
|
bgneal@48
|
41
|
bgneal@47
|
42 if random:
|
bgneal@47
|
43 self.array = numpy.random.random_integers(0, 1, (n, n))
|
bgneal@47
|
44 else:
|
bgneal@47
|
45 self.array = numpy.zeros((n, n), numpy.int8)
|
bgneal@47
|
46
|
bgneal@47
|
47 self.weights = numpy.array([[1,1,1],
|
bgneal@47
|
48 [1,10,1],
|
bgneal@47
|
49 [1,1,1]])
|
bgneal@47
|
50
|
bgneal@47
|
51 def add_glider(self, x=0, y=0):
|
bgneal@47
|
52 coords = [(0,1), (1,2), (2,0), (2,1), (2,2)]
|
bgneal@47
|
53 for i, j in coords:
|
bgneal@47
|
54 self.array[x+i, y+j] = 1
|
bgneal@47
|
55
|
bgneal@47
|
56 def loop(self, steps=1):
|
bgneal@47
|
57 """Executes the given number of time steps."""
|
bgneal@48
|
58 for i in xrange(steps):
|
bgneal@48
|
59 self.step()
|
bgneal@47
|
60
|
bgneal@47
|
61 def step(self):
|
bgneal@47
|
62 """Executes one time step."""
|
bgneal@47
|
63 con = scipy.ndimage.filters.convolve(self.array,
|
bgneal@47
|
64 self.weights,
|
bgneal@47
|
65 mode=self.mode,
|
bgneal@47
|
66 cval=self.cval)
|
bgneal@47
|
67
|
bgneal@47
|
68 boolean = (con==3) | (con==12) | (con==13)
|
bgneal@47
|
69 self.array = numpy.int8(boolean)
|
bgneal@47
|
70
|
bgneal@48
|
71 self.steps += 1
|
bgneal@48
|
72 if self.show_progress and self.steps % self.show_progress == 0:
|
bgneal@48
|
73 print self.steps
|
bgneal@48
|
74
|
bgneal@47
|
75
|
bgneal@47
|
76 class LifeViewer(object):
|
bgneal@47
|
77 """Generates an animated view of the grid."""
|
bgneal@47
|
78 def __init__(self, life, cmap=matplotlib.cm.gray_r, delay=1000):
|
bgneal@47
|
79 self.life = life
|
bgneal@47
|
80 self.cmap = cmap
|
bgneal@47
|
81 self.delay = delay
|
bgneal@47
|
82
|
bgneal@47
|
83 self.fig = pyplot.figure()
|
bgneal@47
|
84 pyplot.axis([0, life.n, 0, life.n])
|
bgneal@47
|
85 pyplot.xticks([])
|
bgneal@47
|
86 pyplot.yticks([])
|
bgneal@47
|
87
|
bgneal@47
|
88 self.pcolor = None
|
bgneal@47
|
89 self.update()
|
bgneal@47
|
90
|
bgneal@47
|
91 def update(self):
|
bgneal@47
|
92 """Updates the display with the state of the grid."""
|
bgneal@47
|
93 if self.pcolor:
|
bgneal@47
|
94 self.pcolor.remove()
|
bgneal@47
|
95
|
bgneal@47
|
96 a = self.life.array
|
bgneal@47
|
97 self.pcolor = pyplot.pcolor(a, cmap=self.cmap)
|
bgneal@47
|
98 self.fig.canvas.draw()
|
bgneal@47
|
99
|
bgneal@47
|
100 def animate(self, steps=10):
|
bgneal@47
|
101 """Creates the GUI and then invokes animate_callback.
|
bgneal@47
|
102
|
bgneal@47
|
103 Generates an animation with the given number of steps.
|
bgneal@47
|
104 """
|
bgneal@47
|
105 self.steps = steps
|
bgneal@47
|
106 self.fig.canvas.manager.window.after(self.delay, self.animate_callback)
|
bgneal@47
|
107 pyplot.show()
|
bgneal@47
|
108
|
bgneal@47
|
109 def animate_callback(self):
|
bgneal@47
|
110 """Runs the animation."""
|
bgneal@47
|
111 for i in range(self.steps):
|
bgneal@47
|
112 self.life.step()
|
bgneal@47
|
113 self.update()
|
bgneal@47
|
114
|
bgneal@47
|
115
|
bgneal@47
|
116 def main(script, n=20, *args):
|
bgneal@47
|
117
|
bgneal@47
|
118 n = int(n)
|
bgneal@47
|
119
|
bgneal@47
|
120 life = Life(n, random=False)
|
bgneal@47
|
121 life.add_glider()
|
bgneal@47
|
122 viewer = LifeViewer(life)
|
bgneal@47
|
123 viewer.animate(steps=100)
|
bgneal@47
|
124
|
bgneal@47
|
125
|
bgneal@47
|
126 if __name__ == '__main__':
|
bgneal@47
|
127 import sys
|
bgneal@47
|
128
|
bgneal@47
|
129 profile = False
|
bgneal@47
|
130 if profile:
|
bgneal@47
|
131 import cProfile
|
bgneal@47
|
132 cProfile.run('main(*sys.argv)')
|
bgneal@47
|
133 else:
|
bgneal@47
|
134 main(*sys.argv)
|