bgneal@40
|
1 """Chapter 6, exercise 1 in Allen Downey's Think Complexity book.
|
bgneal@40
|
2
|
bgneal@40
|
3 "Create a new class called CircularCA that extends CA so that the cells are
|
bgneal@40
|
4 arranged in a ring. Hint: you might find it useful to add a column of 'ghost
|
bgneal@40
|
5 cells' to the array."
|
bgneal@40
|
6
|
bgneal@40
|
7 """
|
bgneal@40
|
8
|
bgneal@40
|
9 import numpy
|
bgneal@40
|
10
|
bgneal@40
|
11 from CA import CA
|
bgneal@40
|
12
|
bgneal@40
|
13
|
bgneal@40
|
14 class CircularCA(CA):
|
bgneal@40
|
15 """A circular cellular automaton; the cells are arranged in a ring."""
|
bgneal@40
|
16
|
bgneal@42
|
17 def __init__(self, rule, n=100, m=50):
|
bgneal@40
|
18 """Parameters:
|
bgneal@40
|
19
|
bgneal@40
|
20 * rule: an integer in the range 0-255 that represents the CA rule
|
bgneal@40
|
21 using Wolfram's encoding
|
bgneal@40
|
22 * n: the number of rows (time steps) in the result
|
bgneal@42
|
23 * m: the number of columns
|
bgneal@40
|
24
|
bgneal@40
|
25 """
|
bgneal@40
|
26 self.table = self.make_table(rule)
|
bgneal@40
|
27 self.n = n
|
bgneal@40
|
28 # a ghost column is added to either end
|
bgneal@42
|
29 self.m = m + 2 # add 2 ghost columns
|
bgneal@40
|
30 self.array = numpy.zeros((n, self.m), dtype=numpy.int8)
|
bgneal@40
|
31 self.next = 0
|
bgneal@40
|
32
|
bgneal@40
|
33 def start_single(self):
|
bgneal@40
|
34 self.array[0, 1] = 1
|
bgneal@40
|
35 self.next = 1
|
bgneal@40
|
36
|
bgneal@40
|
37 def step(self):
|
bgneal@40
|
38 """Executes one time step by computing the next row of the array."""
|
bgneal@40
|
39 i = self.next
|
bgneal@40
|
40 self.next += 1
|
bgneal@40
|
41 assert i > 0, 'call start_single() or start_random() first'
|
bgneal@40
|
42
|
bgneal@40
|
43 a = self.array
|
bgneal@40
|
44 t = self.table
|
bgneal@40
|
45
|
bgneal@40
|
46 # update the ghost cells to enable wrapping
|
bgneal@40
|
47 a[i - 1, 0] = a[i - 1, self.m - 2]
|
bgneal@40
|
48 a[i - 1, self.m - 1] = a[i - 1, 1]
|
bgneal@40
|
49
|
bgneal@40
|
50 for j in xrange(1, self.m - 1):
|
bgneal@40
|
51 a[i, j] = t[tuple(a[i - 1, j-1:j+2])]
|
bgneal@40
|
52
|
bgneal@42
|
53 def loop(self, steps=1):
|
bgneal@42
|
54 """Executes the given number of time steps."""
|
bgneal@42
|
55 for i in xrange(steps):
|
bgneal@42
|
56 self.step()
|
bgneal@40
|
57
|
bgneal@40
|
58 def get_array(self, start=0, end=None):
|
bgneal@40
|
59 """Gets a slice of columns from the CA, with slice indices
|
bgneal@40
|
60 (start, end).
|
bgneal@40
|
61
|
bgneal@40
|
62 """
|
bgneal@40
|
63 # do not return the ghost columns
|
bgneal@40
|
64 if end==None:
|
bgneal@40
|
65 return self.array[:, start+1:self.m-1]
|
bgneal@40
|
66 else:
|
bgneal@40
|
67 return self.array[:, start+1:end+1]
|
bgneal@40
|
68
|
bgneal@42
|
69 def wrap(self):
|
bgneal@42
|
70 """Copies the last row to row 0, then resets the CA to start back at the
|
bgneal@42
|
71 top.
|
bgneal@42
|
72
|
bgneal@42
|
73 """
|
bgneal@42
|
74 a = self.array
|
bgneal@42
|
75 a[0, :] = a[self.next - 1, :]
|
bgneal@42
|
76 self.next = 1
|
bgneal@42
|
77
|
bgneal@40
|
78
|
bgneal@40
|
79 if __name__ == '__main__':
|
bgneal@40
|
80
|
bgneal@40
|
81 import sys
|
bgneal@40
|
82 from CADrawer import PyplotDrawer
|
bgneal@40
|
83
|
bgneal@42
|
84 def main(script, rule, n, m):
|
bgneal@40
|
85 rule = int(rule)
|
bgneal@40
|
86 n = int(n)
|
bgneal@42
|
87 m = int(m)
|
bgneal@42
|
88 ca = CircularCA(rule, n, m)
|
bgneal@40
|
89 ca.start_single()
|
bgneal@40
|
90 ca.loop(n - 1)
|
bgneal@40
|
91
|
bgneal@40
|
92 drawer = PyplotDrawer()
|
bgneal@40
|
93 drawer.draw(ca)
|
bgneal@40
|
94 drawer.show()
|
bgneal@40
|
95
|
bgneal@40
|
96
|
bgneal@40
|
97 main(*sys.argv)
|