-
Notifications
You must be signed in to change notification settings - Fork 3
/
ca-example.lisp
277 lines (239 loc) · 7.16 KB
/
ca-example.lisp
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
;;; CA Example From "Instant *Lisp" Chapter
;;; by William R. Swanson, Thinking Machines Corporation
;;; Load into *Lisp package
(in-package '*lisp)
;;; --- Global Variables ---
;;; This defines a permanent pvar to hold the grid of cells
(*defvar *automata-grid* 0)
;;; Total number of states allowed per cell
(defvar *total-number-of-states* 10)
;;; Cell "neighborhood" to use for automata
(defvar *neighborhood* :neumann)
;;; --- Simple Tools ---
;;; Function to display the grid
(defun view (&optional (width 8) (height 5))
(ppp *automata-grid* :mode :grid :end (list width height)))
;;; Functions to read/write individual cells
(defun read-cell (x y)
(pref *automata-grid* (grid x y)))
(defun set-cell (x y newvalue)
(*setf (pref *automata-grid* (grid x y)) newvalue))
;;; Function to set value of entire grid
(defun set-grid (newvalue)
(*set *automata-grid*
(mod!! newvalue *total-number-of-states*)))
;;; Function to randomly set the value of each cell
(defun random-grid ()
(set-grid (random!! *total-number-of-states*)))
;;; Tools to set up a fixed pattern:
(defun set-cells (cell-list value)
(dolist (cell cell-list)
(set-cell (car cell) (cadr cell) value)))
;;; Clear grid, set up "r-pentomino" pattern, and display
(defun init ()
(set-grid 0)
(set-cells '((2 2) (3 1) (3 2) (3 3) (4 1))
1)
(view))
;;; Tools to get information about neighboring cells.
;;; Count non-zero Von Neumann neighbors
(defun neumann-count (grid)
(+!! (news!! grid 0 -1) ;; north
(news!! grid 0 1) ;; south
(news!! grid -1 0) ;; west
(news!! grid 1 0) ;; east
))
;;; Count non-zero Moore neighbors
(defun moore-count (grid)
(+!! (news!! grid 0 -1) ;; north
(news!! grid 0 1) ;; south
(news!! grid -1 0) ;; west
(news!! grid 1 0) ;; east
(news!! grid -1 -1) ;; northwest
(news!! grid -1 1) ;; southwest
(news!! grid 1 -1) ;; northeast
(news!! grid 1 1) ;; southeast
))
;;; Count neighbors for current *neighborhood*
(defun neighbor-count ()
(*let ((grid (signum!! *automata-grid*)))
(ecase *neighborhood*
(:moore (moore-count grid))
(:neumann (neumann-count grid)))))
;;; Function to run the automata defined by the function one-step.
(defun simple-run (&optional (n 1))
(dotimes (i n)
(set-grid (one-step))))
;;; Function to run automata for n steps, and then display the grid.
(defun view-step (&optional (n 1))
(run n)
(view))
;;; Tool to check whether all of the cells are "dead" (zero).
(defun deadp ()
(zerop (*sum (signum!! *automata-grid*))))
;;; --- Simple Automaton Example ---
;;; This automaton obeys the following rules:
;;; If a cell is:
;;; EVEN - divide its value by 2
;;; ODD - add 1 to its value and multiply by 2
;(defun one-step ()
; (if!! (evenp!! *automata-grid*)
; (floor!! *automata-grid* 2)
; (*!! (1+!! *automata-grid*) 2)))
;;;; --- "9 Life" automata, based on Conway's Game of Life ---
;;;; Obeys the following rules:
;;;; For each cell, count the number of non-zero neighbors.
;;;; If it is <1, or >3, subtract 1 (zero cells remain zero).
;;;; If it is 2 or 3, add 1
;;;; Otherwise, do nothing
(defun one-step ()
(*let ((count (neighbor-count)))
(cond!!
;; When count is <1 or >3, subtract 1 if not zero.
((or!! (<!! count 1) (>!! count 3))
(if!! (zerop!! *automata-grid*)
*automata-grid*
(1-!! *automata-grid*)))
;; When count is 2 or 3, add 1
((<=!! 2 count 3) (1+!! *automata-grid*))
;; Otherwise, leave cells unchanged
(t *automata-grid*))))
;;; Extension of Material in Chapter 1:
;;; Tools to define and run generic automata:
;;; Macro that defines a named automaton
;;; as a list of two function objects.
;;; Init function sets up the *automata-grid*
;;; Step function calculates and returns single "step" of automata
(defmacro defautomaton (name &key init step)
`(progn
(defvar ,name)
(setq ,name (list '(lambda ,@init)
'(lambda ,@step)))
',name))
(defun init-function (automaton) (car automaton))
(defun step-function (automaton) (cadr automaton))
;;; Definitions for the two automata we've already written
(defautomaton four2one
;;; init function takes no arguments, and randomizes the grid.
:init ((&rest ignore)
(setq *total-number-of-states* 10)
(random-grid))
;;; step function takes no arguments, and calculates one step
:step ((&rest ignore)
(if!! (evenp!! *automata-grid*)
(floor!! *automata-grid* 2)
(*!! (1+!! *automata-grid*) 2))))
(defautomaton 9life
;;; init function takes optional arguments defining
;;; the current neighborhood, the initial pattern,
;;; and the value stored into cells that are part of the pattern
:init ((&optional (neighborhood *neighborhood*)
(start-pattern '((2 2) (3 1) (3 2) (3 3) (4 1)))
(start-value 1))
(setq *neighborhood* neighborhood
*total-number-of-states* 10)
(set-grid 0)
(set-cells start-pattern start-value))
;;; step function takes no arguments, and
;;; calculates a single step of the automaton
:step ((&rest ignore)
(*let ((count (neighbor-count)))
(cond!! ((or!! (<!! count 1) (>!! count 3))
(if!! (zerop!! *automata-grid*)
*automata-grid*
(1-!! *automata-grid*)))
((<=!! 2 count 3) (1+!! *automata-grid*))
(t *automata-grid*)))))
;;; --- Tools used to select an automaton to run ---
;;; Currently-selected automaton
(defvar *current-automaton*)
;;; Function to select an automaton and initialize the grid
(defun setup (automaton &rest init-args)
(setq *current-automaton* automaton)
(initialize init-args))
;;; Function to call the init function of the current automaton,
;;; and display the initial state of the grid.
(defun initialize (&optional init-args)
(apply (init-function *current-automaton*) init-args)
(view))
;;; Function to run the automaton for n steps and display the grid
(defun run (&optional (n 1) &rest step-args)
(dotimes (i n)
(set-grid
(apply (step-function *current-automaton*)
step-args)))
(view))
;;; The following sample session shows how to set up and
;;; run the automata defined by the above extensions:
;
;> (setup four2one) ;; Simple 4-2-1 loop automata
;
;5 7 9 3 2 3 1 9
;7 0 3 4 3 3 0 3
;9 4 2 3 8 9 2 5
;7 3 3 5 8 2 9 3
;1 7 9 1 8 6 9 6
;
;> (run)
;
;2 6 0 8 1 8 4 0
;6 0 8 2 8 8 0 8
;0 2 1 8 4 0 1 2
;6 8 8 2 4 1 0 8
;4 6 0 4 4 3 0 3
;
;> (run 50)
;
;4 1 0 2 2 2 1 0
;1 0 2 4 2 2 0 2
;0 4 2 2 1 0 2 4
;1 2 2 4 1 2 0 2
;1 1 0 1 1 4 0 4
;
;> (setup 9life :neumann) ;; 9 Life, Neumann neighborhood
;
;0 0 0 0 0 0 0 0
;0 0 0 1 1 0 0 0
;0 0 1 1 0 0 0 0
;0 0 0 1 0 0 0 0
;0 0 0 0 0 0 0 0
;
;> (run)
;
;0 0 0 0 0 0 0 0
;0 0 1 2 1 0 0 0
;0 0 1 2 1 0 0 0
;0 0 1 1 0 0 0 0
;0 0 0 0 0 0 0 0
;
;> (run 50)
;
;0 0 0 0 0 0 0 0
;0 0 0 8 3 0 0 0
;0 0 5 0 7 0 0 0
;0 0 4 5 9 0 0 0
;0 0 0 0 0 0 0 0
;
;> (setup 9life :moore) ;; 9 Life, Moore neighborhood
;
;0 0 0 0 0 0 0 0
;0 0 0 1 1 0 0 0
;0 0 1 1 0 0 0 0
;0 0 0 1 0 0 0 0
;0 0 0 0 0 0 0 0
;
;> (run)
;
;0 0 0 1 1 0 0 0
;0 0 1 2 2 0 0 0
;0 0 2 0 0 0 0 0
;0 0 1 2 1 0 0 0
;0 0 0 0 0 0 0 0
;
;> (run 50)
;
;0 0 0 0 4 1 0 1
;5 7 7 2 1 1 1 0
;0 0 0 0 1 1 5 2
;4 4 9 2 1 0 4 0
;0 0 0 0 1 2 0 2