/
Cell2D.py
140 lines (110 loc) · 3.88 KB
/
Cell2D.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
""" Code example from Complexity and Computation, a book about
exploring complexity science with Python. Available free from
http://greenteapress.com/complexity
Copyright 2016 Allen Downey
MIT License: http://opensource.org/licenses/MIT
"""
from __future__ import print_function, division
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from scipy.signal import convolve2d
"""
For animation to work in the notebook, you might have to install
ffmpeg. On Ubuntu and Linux Mint, the following should work.
sudo add-apt-repository ppa:mc3man/trusty-media
sudo apt-get update
sudo apt-get install ffmpeg
"""
class Cell2D:
"""Implements Conway's Game of Life."""
def __init__(self, n, m=None):
"""Initializes the attributes.
n: number of rows
m: number of columns
"""
m = n if m is None else m
self.array = np.zeros((n, m), np.uint8)
def add_cells(self, row, col, *strings):
"""Adds cells at the given location.
row: top row index
col: left col index
strings: list of strings of 0s and 1s
"""
for i, s in enumerate(strings):
self.array[row+i, col:col+len(s)] = np.array([int(b) for b in s])
def step(self):
"""Executes one time step."""
pass
class Cell2DViewer:
"""Generates an animated view of an array image."""
cmap = plt.get_cmap('Greens')
options = dict(interpolation='nearest', alpha=0.8,
vmin=0, vmax=1, origin='upper')
def __init__(self, viewee):
self.viewee = viewee
self.im = None
self.hlines = None
self.vlines = None
# TODO: should this really take iters?
def step(self, iters=1):
"""Advances the viewee the given number of steps."""
for i in range(iters):
self.viewee.step()
def draw(self, grid=False):
"""Draws the array and any other elements.
grid: boolean, whether to draw grid lines
"""
self.draw_array(self.viewee.array)
if grid:
self.draw_grid()
def draw_array(self, array=None, cmap=None, **kwds):
"""Draws the cells."""
# Note: we have to make a copy because some implementations
# of step perform updates in place.
if array is None:
array = self.viewee.array
a = array.copy()
cmap = self.cmap if cmap is None else cmap
n, m = a.shape
plt.axis([0, m, 0, n])
plt.xticks([])
plt.yticks([])
options = self.options.copy()
options['extent'] = [0, m, 0, n]
options.update(kwds)
self.im = plt.imshow(a, cmap, **options)
def draw_grid(self):
"""Draws the grid."""
a = self.viewee.array
n, m = a.shape
lw = 2 if m < 7 else 1
options = dict(color='white', linewidth=lw)
# the shift is a hack to get the grid to line up with the cells
shift = 0.005 * n
rows = np.arange(n) + shift
self.hlines = plt.hlines(rows, 0, m, **options)
cols = np.arange(m)
self.vlines = plt.vlines(cols, 0, n, **options)
def animate(self, frames=20, interval=200, grid=False):
"""Creates an animation.
frames: number of frames to draw
interval: time between frames in ms
"""
fig = plt.gcf()
self.draw(grid)
anim = animation.FuncAnimation(fig, self.animate_func,
init_func=self.init_func,
frames=frames, interval=interval)
return anim
def init_func(self):
"""Called at the beginning of an animation."""
pass
def animate_func(self, i):
"""Draws one frame of the animation."""
if i > 0:
self.step()
a = self.viewee.array
self.im.set_array(a)
return (self.im,)