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@43
|
37 def start_row(self, row):
|
bgneal@43
|
38 """Set the first row to row and set next to 1."""
|
bgneal@43
|
39 self.array[0, :] = row
|
bgneal@43
|
40 self.next = 1
|
bgneal@43
|
41
|
bgneal@40
|
42 def step(self):
|
bgneal@40
|
43 """Executes one time step by computing the next row of the array."""
|
bgneal@40
|
44 i = self.next
|
bgneal@40
|
45 self.next += 1
|
bgneal@40
|
46 assert i > 0, 'call start_single() or start_random() first'
|
bgneal@40
|
47
|
bgneal@40
|
48 a = self.array
|
bgneal@40
|
49 t = self.table
|
bgneal@40
|
50
|
bgneal@40
|
51 # update the ghost cells to enable wrapping
|
bgneal@40
|
52 a[i - 1, 0] = a[i - 1, self.m - 2]
|
bgneal@40
|
53 a[i - 1, self.m - 1] = a[i - 1, 1]
|
bgneal@40
|
54
|
bgneal@40
|
55 for j in xrange(1, self.m - 1):
|
bgneal@40
|
56 a[i, j] = t[tuple(a[i - 1, j-1:j+2])]
|
bgneal@40
|
57
|
bgneal@42
|
58 def loop(self, steps=1):
|
bgneal@42
|
59 """Executes the given number of time steps."""
|
bgneal@42
|
60 for i in xrange(steps):
|
bgneal@42
|
61 self.step()
|
bgneal@40
|
62
|
bgneal@40
|
63 def get_array(self, start=0, end=None):
|
bgneal@40
|
64 """Gets a slice of columns from the CA, with slice indices
|
bgneal@40
|
65 (start, end).
|
bgneal@40
|
66
|
bgneal@40
|
67 """
|
bgneal@40
|
68 # do not return the ghost columns
|
bgneal@40
|
69 if end==None:
|
bgneal@40
|
70 return self.array[:, start+1:self.m-1]
|
bgneal@40
|
71 else:
|
bgneal@40
|
72 return self.array[:, start+1:end+1]
|
bgneal@40
|
73
|
bgneal@42
|
74 def wrap(self):
|
bgneal@42
|
75 """Copies the last row to row 0, then resets the CA to start back at the
|
bgneal@42
|
76 top.
|
bgneal@42
|
77
|
bgneal@42
|
78 """
|
bgneal@42
|
79 a = self.array
|
bgneal@42
|
80 a[0, :] = a[self.next - 1, :]
|
bgneal@42
|
81 self.next = 1
|
bgneal@42
|
82
|
bgneal@40
|
83
|
bgneal@40
|
84 if __name__ == '__main__':
|
bgneal@40
|
85
|
bgneal@40
|
86 import sys
|
bgneal@40
|
87 from CADrawer import PyplotDrawer
|
bgneal@40
|
88
|
bgneal@42
|
89 def main(script, rule, n, m):
|
bgneal@40
|
90 rule = int(rule)
|
bgneal@40
|
91 n = int(n)
|
bgneal@42
|
92 m = int(m)
|
bgneal@42
|
93 ca = CircularCA(rule, n, m)
|
bgneal@40
|
94 ca.start_single()
|
bgneal@40
|
95 ca.loop(n - 1)
|
bgneal@40
|
96
|
bgneal@40
|
97 drawer = PyplotDrawer()
|
bgneal@40
|
98 drawer.draw(ca)
|
bgneal@40
|
99 drawer.show()
|
bgneal@40
|
100
|
bgneal@40
|
101
|
bgneal@40
|
102 main(*sys.argv)
|