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 lookup-color (color)
21 "LOOKUP-COLOR COLOR => COLOR-NUMBER
25 COLOR: a symbol representing a color
26 COLOR-NUMBER: the NetLogo color integer
30 Returns the number used to represent colors in NetLogo.
32 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#Constants"
51 (defun create-turtle ()
58 :who (coerce *current-id* 'double-float)
59 :color (coerce (+ 5 (* 10 (clnl-random:next-int 14))) 'double-float)
60 :heading (coerce (clnl-random:next-int 360) 'double-float)
70 RESULT: undefined, commands don't return
74 The turtle or link dies
76 A dead agent ceases to exist. The effects of this include:
77 - The agent will not execute any further code.
78 - The agent will disappear from any agentsets it was in, reducing the size of those agentsets by one.
79 - Any variable that was storing the agent will now instead have nobody in it.
80 - If the dead agent was a turtle, every link connected to it also dies.
81 - If the observer was watching or following the agent, the observer's perspective resets.
83 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#die"
84 (when (not (turtle-p *self*)) (error "Gotta call die in turtle scope, dude (~A)" *self*))
85 (setf (turtle-who *self*) -1)
86 (setf *turtles* (remove *self* *turtles*)))
89 "PATCHES => ALL-PATCHES
93 ALL-PATCHES: a NetLogo agentset, all patches
97 Reports the agentset consisting of all the patches.
99 This agentset is special in that it represents the living patches
100 each time it's used, so changes depending on the state of the engine.
102 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#patches"
106 "TURTLES => ALL-TURTLES
108 ARGUMENTS AND VALUES:
110 ALL-TURTLES: a NetLogo agentset, all turtles
114 Reports the agentset consisting of all the turtles.
116 This agentset is special in that it represents the living turtles
117 each time it's used, so changes depending on the state of the engine.
119 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtles"
122 (defun ask (agent-set fn)
123 "ASK AGENT-SET FN => RESULT
125 ARGUMENTS AND VALUES:
127 AGENT-SET: a NetLogo agentset
128 FN: a function, run on each agent
129 RESULT: undefined, commands don't return
133 ASK is equivalent to ask in NetLogo.
135 The specified AGENT-SET runs the given FN. The order in which the agents
136 are run is random each time, and only agents that are in the set at the
137 beginning of the call.
139 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ask"
141 ((iter (shufflerator (agent-set-list agent-set))))
143 :for agent := (funcall iter)
145 :do (let ((*myself* *self*) (*self* agent)) (funcall fn)))))
147 (defun count (agent-set)
148 "COUNT AGENT-SET => N
150 ARGUMENTS AND VALUES:
152 AGENT-SET: a NetLogo agentset
157 COUNT is equivalent to count in NetLogo. Returns N, the number of
160 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#count"
161 (coerce (length (agent-set-list agent-set)) 'double-float))
163 (defun of (fn agent-set)
164 "OF FN AGENT-SET => RESULT
166 ARGUMENTS AND VALUES:
168 FN: a function, run on each agent
169 AGENT-SET: a NetLogo agentset
174 OF is equivalent to of in NetLogo.
176 The specified AGENT-SET runs the given FN. The order in which the agents
177 are run is random each time, and only agents that are in the set at the
178 beginning of the call. A list is returned of the returned valuse of
181 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#of"
183 ((iter (shufflerator (agent-set-list agent-set))))
185 :for agent := (funcall iter)
187 :collect (let ((*myself* *self*) (*self* agent)) (funcall fn)))))
189 (defun shufflerator (agent-set-list)
191 ((copy (copy-list agent-set-list))
197 ((idx (when (< i (1- (length copy))) (+ i (clnl-random:next-int (- (length copy) i))))))
198 (when idx (setf agent (nth idx copy)))
199 (when idx (setf (nth idx copy) (nth i copy)))
201 (fetch) ; we pre-fetch because netlogo does, rng sync hype!
204 ((> i (length copy)) nil)
205 ((= i (length copy)) (incf i) (car (last copy)))
206 (t (let ((result agent)) (fetch) result)))))))
208 (defun random-float (n)
209 "RANDOM-FLOAT N => RANDOM-NUMBER
211 ARGUMENTS AND VALUES:
213 N: a double, the upper bound of the random float
214 RANDOM-NUMBER: a double, the random result
218 Returns a random number strictly closer to zero than N.
220 If number is positive, returns a random floating point number greater than
221 or equal to 0 but strictly less than number.
223 If number is negative, returns a random floating point number less than or equal
224 to 0, but strictly greater than number.
226 If number is zero, the result is always 0.
228 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-float"
229 (clnl-random:next-double n))
232 "RANDOM N => RANDOM-NUMBER
234 ARGUMENTS AND VALUES:
236 N: an integer, the upper bound of the random
237 RANDOM-NUMBER: an integer, the random result
241 Returns a random number strictly closer to zero than N.
243 If number is positive, returns a random integer greater than or equal to 0,
244 but strictly less than number.
246 If number is negative, returns a random integer less than or equal to 0,
247 but strictly greater than number.
249 If number is zero, the result is always 0.
251 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random"
252 (coerce (clnl-random:next-long (truncate n)) 'double-float))
254 (defun random-xcor ()
255 "RANDOM-XCOR => RANDOM-NUMBER
257 ARGUMENTS AND VALUES:
259 RANDOM-NUMBER: a float, the random result
263 Returns a random floating point number in the allowable range of turtle
264 coordinates along the x axis.
266 These range from min-pxcor - 0.5 (inclusive) to max-pxcor + 0.5 (exclusive)
268 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
270 ((min (- (min-pxcor) 0.5d0))
271 (max (+ (max-pxcor) 0.5d0)))
272 (+ min (clnl-random:next-double (- max min)))))
274 (defun random-ycor ()
275 "RANDOM-YCOR => RANDOM-NUMBER
277 ARGUMENTS AND VALUES:
279 RANDOM-NUMBER: a float, the random result
283 Returns a random floating point number in the allowable range of turtle
284 coordinates along the y axis.
286 These range from min-pycor - 0.5 (inclusive) to max-pycor + 0.5 (exclusive)
288 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
290 ((min (- (min-pycor) 0.5d0))
291 (max (+ (max-pycor) 0.5d0)))
292 (+ min (clnl-random:next-double (- max min)))))
294 (defun one-of (agent-set)
295 "ONE-OF AGENT-SET => RESULT
297 RESULT: RANDOM-AGENT | :nobody
299 ARGUMENTS AND VALUES:
301 AGENT-SET: An agent set
302 RANDOM-AGENT: an agent if AGENT-SET is non empty
306 From an agentset, returns a random agent. If the agentset is empty, returns nobody.
308 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#one-of"
310 ((agent-set-list (agent-set-list agent-set))
311 (length (length agent-set-list)))
312 (if (zerop length) :nobody (nth (clnl-random:next-int length) agent-set-list))))
315 (when (not (turtle-p *self*)) (error "Gotta call jump in turtle scope, dude (~A)" *self*))
319 (+ (turtle-xcor *self*) (* n (using-cached-sin (turtle-heading *self*))))))
323 (+ (turtle-ycor *self*) (* n (using-cached-cos (turtle-heading *self*)))))))
328 ARGUMENTS AND VALUES:
336 Sets the x-coordinate and y-coordinate for the turle. Equivalent to
337 set xcor x set ycor y, except it happens in one step inside of two.
339 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#setxy"
340 (when (not (turtle-p *self*)) (error "Gotta call setxy in turtle scope, dude (~A)" *self*))
341 (setf (turtle-xcor *self*) (wrap-x *topology* x))
342 (setf (turtle-ycor *self*) (wrap-y *topology* y)))
347 ARGUMENTS AND VALUES:
349 N: a double, the amount the turtle moves forward
354 Moves the current turtle forward N steps, one step at a time.
356 This moves forward one at a time in order to make the view updates look
357 good in the case of a purposefully slow running instance. If the number
358 is negative, the turtle moves backward.
360 If the current agent is not a turtle, it raises an error.
362 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#forward"
363 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
367 ((< (abs i) 3.2e-15) nil)
368 ((< (abs i) 1d0) (jump i))
369 (t (jump (if (> i 0d0) 1d0 -1d0)) (internal (- i (if (> i 0d0) 1d0 -1d0)))))))
372 (defun turn-right (n)
373 "TURN-RIGHT N => RESULT
375 ARGUMENTS AND VALUES:
377 N: a double, the amount the turtle turns
382 The turtle turns right by number degrees. (If number is negative, it turns left.)
384 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
385 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
387 ((new-heading (+ (turtle-heading *self*) n)))
388 (setf (turtle-heading *self*)
390 ((< new-heading 0) (+ (mod new-heading -360) 360))
391 ((>= new-heading 360) (mod new-heading 360))
395 "TURN-LEFT N => RESULT
397 ARGUMENTS AND VALUES:
399 N: a double, the amount the turtle turns
404 The turtle turns left by number degrees. (If number is negative, it turns right.)
406 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
409 (defun create-turtles (n)
410 "CREATE-TURTLES N => RESULT
412 ARGUMENTS AND VALUES:
414 N: an integer, the numbers of turtles to create
419 Creates number new turtles at the origin.
421 New turtles have random integer headings and the color is randomly selected
422 from the 14 primary colors. If commands are supplied, the new turtles
423 immediately run them (unimplemented).
425 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#create-turtles"
426 (loop :for i :from 1 :to n :do (create-turtle)))
428 (defun reset-ticks ()
429 "RESET-TICKS => RESULT
431 ARGUMENTS AND VALUES:
437 Resets the tick counter to zero, sets up all plots, then updates all plots.
439 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#reset-ticks"
443 "RESET-TICKS => RESULT
445 ARGUMENTS AND VALUES:
451 Advances the tick counter by one and updates all plots.
453 If the tick counter has not been started yet with reset-ticks, an error results.
455 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#tick"
457 (when (not *ticks*) (error "reset-ticks must be called"))
461 "TICKS => CURRENT-TICKS
463 ARGUMENTS AND VALUES:
465 CURRENT-TICKS: A positiv double, representing the current number of ticks
469 Reports the current value of the tick counter. The result is always a number and never negative.
471 If the tick counter has not been started yet with reset-ticks, an error results.
473 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ticks"
474 (when (not *ticks*) (error "reset-ticks must be called"))
477 (defun create-world (&key dims)
478 "CREATE-WORLD &key DIMS => RESULT
480 DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
482 ARGUMENTS AND VALUES:
485 XMIN: An integer representing the minimum patch coord in X
486 XMAX: An integer representing the maximum patch coord in X
487 YMIN: An integer representing the minimum patch coord in Y
488 YMAX: An integer representing the maximum patch coord in Y
492 Initializes the world in the NVM.
494 This should be called before using the engine in any real capacity. If
495 called when an engine is already running, it may do somethign weird."
496 (setf *dimensions* dims)
500 :for y :from (max-pycor) :downto (min-pycor)
502 :for x :from (min-pxcor) :to (max-pxcor)
504 :xcor (coerce x 'double-float)
505 :ycor (coerce y 'double-float)
508 (setf *current-id* 0))
510 ; These match netlogo's dump
511 (defgeneric dump-object (o))
513 (defmethod dump-object ((n double-float))
514 (multiple-value-bind (int rem) (floor n)
516 (format nil "~A" int)
518 ((output (format nil "~D" n)))
519 ; Someday we'll have d<posint>, but this is not that day!
520 (cl-ppcre:regex-replace "d-" (cl-ppcre:regex-replace "d0" output "") "E-")))))
522 (defmethod dump-object ((o string)) (format nil "~A" (cl-ppcre:regex-replace-all "\"" (format nil "~S" o) "\"\"")))
524 (defmethod dump-object ((o (eql t))) "true")
525 (defmethod dump-object ((o (eql nil))) "false")
527 (defmethod dump-object ((o list)) (format nil "[~{~A~^ ~}]" (mapcar #'dump-object o)))
529 (defmethod dump-object ((o patch))
530 (format nil "(patch ~A ~A)" (dump-object (patch-xcor o)) (dump-object (patch-ycor o))))
532 (defmethod dump-object ((o turtle)) (format nil "(turtle ~A)" (dump-object (turtle-who o))))
533 (defmethod dump-object ((o (eql :nobody))) (format nil "nobody"))
535 (defun current-state ()
536 "CURRENT-STATE => WORLD-STATE
538 ARGUMENTS AND VALUES:
540 WORLD-STATE: A list, the current state of the whole world
544 Dumps out the state of the world.
546 This is useful for visualizations and also storing in a common lisp
547 data structure for easy usage in a common lisp instance. It's preferable
548 to use this when working with the nvm than the output done by export-world.
550 Currently this only dumps out turtle and patch information.
552 This is called CURRENT-STATE because export-world is an actual primitive
558 :color (turtle-color turtle)
559 :xcor (turtle-xcor turtle)
560 :ycor (turtle-ycor turtle)
561 :heading (turtle-heading turtle)
562 :size (turtle-size turtle)))
567 :color (patch-color patch)
568 :xcor (patch-xcor patch)
569 :ycor (patch-ycor patch)))
572 (defun export-turtles ()
577 "\"who\",\"color\",\"heading\",\"xcor\",\"ycor\",\"shape\",\"label\",\"label-color\","
578 "\"breed\",\"hidden?\",\"size\",\"pen-size\",\"pen-mode\""))
582 "\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",\"\"\"default\"\"\",\"~A\",\"~A\",\"{all-turtles}\",\"false\",\"~A\",~A"
583 (dump-object (turtle-who turtle))
584 (dump-object (turtle-color turtle))
585 (dump-object (turtle-heading turtle))
586 (dump-object (turtle-xcor turtle))
587 (dump-object (turtle-ycor turtle))
588 (dump-object (turtle-label turtle))
589 (dump-object (turtle-label-color turtle))
590 (dump-object (turtle-size turtle))
591 "\"1\",\"\"\"up\"\"\""))
594 (defun export-patches ()
598 "\"pxcor\",\"pycor\",\"pcolor\",\"plabel\",\"plabel-color\"")
602 "\"~A\",\"~A\",\"~A\",\"\"\"\"\"\",\"9.9\""
603 (dump-object (patch-xcor patch))
604 (dump-object (patch-ycor patch))
605 (dump-object (patch-color patch))))
608 (defun export-world ()
609 "EXPORT-WORLD => WORLD-CSV
611 ARGUMENTS AND VALUES:
613 WORLD-CSV: A string, the csv of the world
617 Dumps out a csv matching NetLogo's export world.
619 This is useful for serializing the current state of the engine in order
620 to compare against NetLogo or to reimport later. Contains everything needed
621 to boot up a NetLogo instance in the exact same state."
622 (format nil "~{~A~%~}"
624 (format nil "~S" "RANDOM STATE")
625 (format nil "~S" (clnl-random:export))
627 (format nil "~S" "GLOBALS")
629 "\"min-pxcor\",\"max-pxcor\",\"min-pycor\",\"max-pycor\",\"perspective\",\"subject\","
630 "\"nextIndex\",\"directed-links\",\"ticks\",")
631 (format nil "\"~A\",\"~A\",\"~A\",\"~A\",\"0\",\"nobody\",\"~A\",\"\"\"NEITHER\"\"\",\"-1\""
632 (min-pxcor) (max-pxcor) (min-pycor) (max-pycor) *current-id*)
634 (format nil "~{~A~^~%~}" (export-turtles))
636 (format nil "~{~A~^~%~}" (export-patches))
638 (format nil "~S" "LINKS")
639 "\"end1\",\"end2\",\"color\",\"label\",\"label-color\",\"hidden?\",\"breed\",\"thickness\",\"shape\",\"tie-mode\""