1 (in-package #:clnl-nvm)
3 ; Implementations of all the things the nvm can do.
10 VALUE: a NetLogo value
15 A command that prints the given NetLogo value to the command center.
17 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#show"
18 (format t "Showing: ~A~%" (dump-object value)))
20 (defun create-turtle ()
28 :color (coerce (+ 5 (* 10 (clnl-random:next-int 14))) 'double-float)
29 :heading (coerce (clnl-random:next-int 360) 'double-float)
39 RESULT: undefined, commands don't return
43 The turtle or link dies
45 A dead agent ceases to exist. The effects of this include:
46 - The agent will not execute any further code.
47 - The agent will disappear from any agentsets it was in, reducing the size of those agentsets by one.
48 - Any variable that was storing the agent will now instead have nobody in it.
49 - If the dead agent was a turtle, every link connected to it also dies.
50 - If the observer was watching or following the agent, the observer's perspective resets.
52 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#die"
53 (when (not (turtle-p *self*)) (error "Gotta call die in turtle scope, dude (~A)" *self*))
54 (setf (turtle-who *self*) -1)
55 (setf *turtles* (remove *self* *turtles*)))
58 "TURTLES => ALL-TURTLES
62 ALL-TURTLES: a NetLogo agentset, all turtles
66 Reports the agentset consisting of all the turtles.
68 This agentset is special in that it represents the living turtles
69 each time it's used, so changes depending on the state of the engine.
71 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtles"
74 (defun ask (agent-set fn)
75 "ASK AGENT-SET FN => RESULT
79 AGENT-SET: a NetLogo agentset
80 FN: a function, run on each agent
81 RESULT: undefined, commands don't return
85 ASK is equivalent to ask in NetLogo.
87 The specified AGENT-SET runs the given FN. The order in which the agents
88 are run is random each time, and only agents that are in the set at the
89 beginning of the call.
91 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ask"
93 ((iter (shufflerator agent-set)))
95 :for agent := (funcall iter)
97 :do (let ((*myself* *self*) (*self* agent)) (funcall fn)))))
99 (defun shufflerator (agent-set)
101 ((copy (copy-list agent-set))
107 ((idx (when (< i (1- (length copy))) (+ i (clnl-random:next-int (- (length copy) i))))))
108 (when idx (setf agent (nth idx copy)))
109 (when idx (setf (nth idx copy) (nth i copy)))
111 (fetch) ; we pre-fetch because netlogo does, rng sync hype!
114 ((> i (length copy)) nil)
115 ((= i (length copy)) (incf i) (car (last copy)))
116 (t (let ((result agent)) (fetch) result)))))))
118 (defun random-float (n)
119 "RANDOM-FLOAT N => RANDOM-NUMBER
121 ARGUMENTS AND VALUES:
123 N: a double, the upper bound of the random float
124 RANDOM-NUMBER: a double, the random result
128 Returns a random number strictly closer to zero than N.
130 If number is positive, returns a random floating point number greater than
131 or equal to 0 but strictly less than number.
133 If number is negative, returns a random floating point number less than or equal
134 to 0, but strictly greater than number.
136 If number is zero, the result is always 0.
138 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-float"
139 (clnl-random:next-double n))
142 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
146 (+ (turtle-xcor *self*) (* n (using-cached-sin (turtle-heading *self*))))))
150 (+ (turtle-ycor *self*) (* n (using-cached-cos (turtle-heading *self*)))))))
155 ARGUMENTS AND VALUES:
157 N: a double, the amount the turtle moves forward
162 Moves the current turtle forward N steps, one step at a time.
164 This moves forward one at a time in order to make the view updates look
165 good in the case of a purposefully slow running instance. If the number
166 is negative, the turtle moves backward.
168 If the current agent is not a turtle, it raises an error.
170 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#forward"
171 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
175 ((< (abs i) 3.2e-15) nil)
176 ((< (abs i) 1d0) (jump i))
177 (t (jump (if (> i 0d0) 1d0 -1d0)) (internal (- i (if (> i 0d0) 1d0 -1d0)))))))
180 (defun turn-right (n)
181 "TURN-RIGHT N => RESULT
183 ARGUMENTS AND VALUES:
185 N: a double, the amount the turtle turns
190 The turtle turns right by number degrees. (If number is negative, it turns left.)
192 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
193 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
195 ((new-heading (+ (turtle-heading *self*) n)))
196 (setf (turtle-heading *self*)
198 ((< new-heading 0) (+ (mod new-heading -360) 360))
199 ((>= new-heading 360) (mod new-heading 360))
203 "TURN-LEFT N => RESULT
205 ARGUMENTS AND VALUES:
207 N: a double, the amount the turtle turns
212 The turtle turns left by number degrees. (If number is negative, it turns right.)
214 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
217 (defun create-turtles (n)
218 "CREATE-TURTLES N => RESULT
220 ARGUMENTS AND VALUES:
222 N: an integer, the numbers of turtles to create
227 Creates number new turtles at the origin.
229 New turtles have random integer headings and the color is randomly selected
230 from the 14 primary colors. If commands are supplied, the new turtles
231 immediately run them (unimplemented).
233 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#create-turtles"
234 (loop :for i :from 1 :to n :do (create-turtle)))
236 (defun create-world (&key dims)
237 "CREATE-WORLD &key DIMS => RESULT
239 DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
241 ARGUMENTS AND VALUES:
244 XMIN: An integer representing the minimum patch coord in X
245 XMAX: An integer representing the maximum patch coord in X
246 YMIN: An integer representing the minimum patch coord in Y
247 YMAX: An integer representing the maximum patch coord in Y
251 Initializes the world in the NVM.
253 This should be called before using the engine in any real capacity. If
254 called when an engine is already running, it may do somethign weird."
255 (setf *dimensions* dims)
257 (setf *current-id* 0))
259 ; These match netlogo's dump
260 (defgeneric dump-object (o))
262 (defmethod dump-object ((n double-float))
263 (multiple-value-bind (int rem) (floor n)
265 (format nil "~A" int)
267 ((output (format nil "~D" n)))
268 ; Someday we'll have d<posint>, but this is not that day!
269 (cl-ppcre:regex-replace "d-" (cl-ppcre:regex-replace "d0" output "") "E-")))))
271 (defmethod dump-object ((o string)) o)
273 (defmethod dump-object ((o (eql t))) "true")
274 (defmethod dump-object ((o (eql nil))) "false")
276 (defun current-state ()
277 "CURRENT-STATE => WORLD-STATE
279 ARGUMENTS AND VALUES:
281 WORLD-STATE: A list, the current state of the whole world
285 Dumps out the state of the world.
287 This is useful for visualizations and also storing in a common lisp
288 data structure for easy usage in a common lisp instance. It's preferable
289 to use this when working with the nvm than the output done by export-world.
291 Currently this only dumps out turtle information.
293 This is called CURRENT-STATE because export-world is an actual primitive
298 :color (turtle-color turtle)
299 :xcor (turtle-xcor turtle)
300 :ycor (turtle-ycor turtle)
301 :heading (turtle-heading turtle)))
304 (defun export-patches ()
306 "\"pxcor\",\"pycor\",\"pcolor\",\"plabel\",\"plabel-color\""
307 "\"-1\",\"1\",\"0\",\"\"\"\"\"\",\"9.9\""
308 "\"0\",\"1\",\"0\",\"\"\"\"\"\",\"9.9\""
309 "\"1\",\"1\",\"0\",\"\"\"\"\"\",\"9.9\""
310 "\"-1\",\"0\",\"0\",\"\"\"\"\"\",\"9.9\""
311 "\"0\",\"0\",\"0\",\"\"\"\"\"\",\"9.9\""
312 "\"1\",\"0\",\"0\",\"\"\"\"\"\",\"9.9\""
313 "\"-1\",\"-1\",\"0\",\"\"\"\"\"\",\"9.9\""
314 "\"0\",\"-1\",\"0\",\"\"\"\"\"\",\"9.9\""
315 "\"1\",\"-1\",\"0\",\"\"\"\"\"\",\"9.9\""))
317 (defun export-world ()
318 "EXPORT-WORLD => WORLD-CSV
320 ARGUMENTS AND VALUES:
322 WORLD-CSV: A string, the csv of the world
326 Dumps out a csv matching NetLogo's export world.
328 This is useful for serializing the current state of the engine in order
329 to compare against NetLogo or to reimport later. Contains everything needed
330 to boot up a NetLogo instance in the exact same state."
331 (format nil "~{~A~%~}"
333 (format nil "~S" "RANDOM STATE")
334 (format nil "~S" (clnl-random:export))
336 (format nil "~S" "GLOBALS")
338 "\"min-pxcor\",\"max-pxcor\",\"min-pycor\",\"max-pycor\",\"perspective\",\"subject\","
339 "\"nextIndex\",\"directed-links\",\"ticks\",")
340 (format nil "\"~A\",\"~A\",\"~A\",\"~A\",\"0\",\"nobody\",\"~A\",\"\"\"NEITHER\"\"\",\"-1\""
341 (min-pxcor) (max-pxcor) (min-pycor) (max-pycor) *current-id*)
343 (format nil "~S" "TURTLES")
345 "\"who\",\"color\",\"heading\",\"xcor\",\"ycor\",\"shape\",\"label\",\"label-color\","
346 "\"breed\",\"hidden?\",\"size\",\"pen-size\",\"pen-mode\"")
347 (format nil "~{~A~%~}"
351 "\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",~A"
353 (dump-object (turtle-color turtle))
354 (dump-object (turtle-heading turtle))
355 (dump-object (turtle-xcor turtle))
356 (dump-object (turtle-ycor turtle))
357 "\"\"\"default\"\"\",\"\"\"\"\"\",\"9.9\",\"{all-turtles}\",\"false\",\"1\",\"1\",\"\"\"up\"\"\""))
359 (format nil "~S" "PATCHES")
360 (format nil "~{~A~^~%~}" (export-patches))
362 (format nil "~S" "LINKS")
363 "\"end1\",\"end2\",\"color\",\"label\",\"label-color\",\"hidden?\",\"breed\",\"thickness\",\"shape\",\"tie-mode\""