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 (&optional base-turtle)
53 ((new-turtle (make-turtle
54 :who (coerce *current-id* 'double-float)
55 :color (if base-turtle
56 (turtle-color base-turtle)
57 (coerce (+ 5 (* 10 (clnl-random:next-int 14))) 'double-float))
58 :heading (if base-turtle
59 (turtle-heading base-turtle)
60 (coerce (clnl-random:next-int 360) 'double-float))
61 :xcor (if base-turtle (turtle-xcor base-turtle) 0d0)
62 :ycor (if base-turtle (turtle-ycor base-turtle) 0d0))))
63 (setf *turtles* (nconc *turtles* (list new-turtle)))
72 RESULT: undefined, commands don't return
76 The turtle or link dies
78 A dead agent ceases to exist. The effects of this include:
79 - The agent will not execute any further code.
80 - The agent will disappear from any agentsets it was in, reducing the size of those agentsets by one.
81 - Any variable that was storing the agent will now instead have nobody in it.
82 - If the dead agent was a turtle, every link connected to it also dies.
83 - If the observer was watching or following the agent, the observer's perspective resets.
85 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#die"
86 (when (not (turtle-p *self*)) (error "Gotta call die in turtle scope, dude (~A)" *self*))
87 (setf (turtle-who *self*) -1)
88 (setf *turtles* (remove *self* *turtles*)))
91 "PATCHES => ALL-PATCHES
95 ALL-PATCHES: a NetLogo agentset, all patches
99 Reports the agentset consisting of all the patches.
101 This agentset is special in that it represents the living patches
102 each time it's used, so changes depending on the state of the engine.
104 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#patches"
108 "TURTLES => ALL-TURTLES
110 ARGUMENTS AND VALUES:
112 ALL-TURTLES: a NetLogo agentset, all turtles
116 Reports the agentset consisting of all the turtles.
118 This agentset is special in that it represents the living turtles
119 each time it's used, so changes depending on the state of the engine.
121 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtles"
124 (defun ask (agent-or-agentset fn)
125 "ASK AGENT-OR-AGENTSET FN => RESULT
127 AGENT-OR-AGENTSET: AGENT | AGENTSET
129 ARGUMENTS AND VALUES:
131 FN: a function, run on each agent
132 RESULT: undefined, commands don't return
133 AGENT: a NetLogo agent
134 AGENTSET: a NetLogo agentset
138 ASK is equivalent to ask in NetLogo.
140 The specified AGENTSET or AGENT runs the given FN. In the case of an
141 AGENTSET, the order in which the agents are run is random each time,
142 and only agents that are in the set at the beginning of the call.
144 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ask"
146 ((agentset-p agent-or-agentset)
148 ((iter (shufflerator (agentset-list agent-or-agentset))))
150 :for agent := (funcall iter)
152 :do (let ((*myself* *self*) (*self* agent)) (funcall fn)))))
153 ((agent-p agent-or-agentset)
154 (let ((*myself* *self*) (*self* agent-or-agentset)) (funcall fn)))
156 (error "Ask requires an agentset or agent but got: ~A" agent-or-agentset))))
158 (defun count (agentset)
161 ARGUMENTS AND VALUES:
163 AGENTSET: a NetLogo agentset
168 COUNT is equivalent to count in NetLogo. Returns N, the number of
171 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#count"
172 (coerce (length (agentset-list agentset)) 'double-float))
174 (defun of (fn agent-or-agentset)
175 "OF FN AGENT-OR-AGENTSET => RESULT
177 AGENT-OR-AGENTSET: AGENT | AGENTSET
178 RESULT: RESULT-LIST | RESULT-VALUE
180 ARGUMENTS AND VALUES:
182 FN: a function, run on each agent
183 AGENT: a NetLogo agent
184 AGENTSET: a NetLogo agentset
186 RESULT-VALUE: a single value
190 OF is equivalent to of in NetLogo.
192 The specified AGENTSET or AGENT runs the given FN. In the case of an
193 AGENTSET, the order in which the agents are run is random each time,
194 and only agents that are in the set at the beginning of the call.
196 RESULT-LIST is returned when the input is an AGENTSET, but RESULT-VALUE
197 is returned when only passed an AGENT.
199 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#of"
201 ((agentset-p agent-or-agentset)
203 ((iter (shufflerator (agentset-list agent-or-agentset))))
205 :for agent := (funcall iter)
207 :collect (let ((*myself* *self*) (*self* agent)) (funcall fn)))))
208 ((agent-p agent-or-agentset)
209 (let ((*myself* *self*) (*self* agent-or-agentset)) (funcall fn)))
211 (error "Of requires an agentset or agent but got: ~A" agent-or-agentset))))
213 (defun with (agentset fn)
214 "WITH AGENTSET FN => RESULT-AGENTSET
216 ARGUMENTS AND VALUES:
218 AGENTSET: a NetLogo agentset
219 FN: a boolean function, run on each agent to determine if included
220 RESULT-AGENTSET: an agentset of valid agents
224 WITH is equivalent to with in NetLogo.
226 Returns a new agentset containing only those agents that reported true
229 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#with"
233 (let ((*myself* *self*) (*self* agent)) (funcall fn)))
234 (agentset-list agentset))
235 (agentset-breed agentset)))
237 (defun shufflerator (agentset-list)
239 ((copy (copy-list agentset-list))
245 ((idx (when (< i (1- (length copy))) (+ i (clnl-random:next-int (- (length copy) i))))))
246 (when idx (setf agent (nth idx copy)))
247 (when idx (setf (nth idx copy) (nth i copy)))
249 (fetch) ; we pre-fetch because netlogo does, rng sync hype!
252 ((> i (length copy)) nil)
253 ((= i (length copy)) (incf i) (car (last copy)))
254 (t (let ((result agent)) (fetch) result)))))))
256 (defun random-float (n)
257 "RANDOM-FLOAT N => RANDOM-NUMBER
259 ARGUMENTS AND VALUES:
261 N: a double, the upper bound of the random float
262 RANDOM-NUMBER: a double, the random result
266 Returns a random number strictly closer to zero than N.
268 If number is positive, returns a random floating point number greater than
269 or equal to 0 but strictly less than number.
271 If number is negative, returns a random floating point number less than or equal
272 to 0, but strictly greater than number.
274 If number is zero, the result is always 0.
276 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-float"
277 (clnl-random:next-double n))
280 "RANDOM N => RANDOM-NUMBER
282 ARGUMENTS AND VALUES:
284 N: an integer, the upper bound of the random
285 RANDOM-NUMBER: an integer, the random result
289 Returns a random number strictly closer to zero than N.
291 If number is positive, returns a random integer greater than or equal to 0,
292 but strictly less than number.
294 If number is negative, returns a random integer less than or equal to 0,
295 but strictly greater than number.
297 If number is zero, the result is always 0.
299 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random"
300 (coerce (clnl-random:next-long (truncate n)) 'double-float))
302 (defun random-xcor ()
303 "RANDOM-XCOR => RANDOM-NUMBER
305 ARGUMENTS AND VALUES:
307 RANDOM-NUMBER: a float, the random result
311 Returns a random floating point number in the allowable range of turtle
312 coordinates along the x axis.
314 These range from min-pxcor - 0.5 (inclusive) to max-pxcor + 0.5 (exclusive)
316 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
318 ((min (- (min-pxcor) 0.5d0))
319 (max (+ (max-pxcor) 0.5d0)))
320 (+ min (clnl-random:next-double (- max min)))))
322 (defun random-ycor ()
323 "RANDOM-YCOR => RANDOM-NUMBER
325 ARGUMENTS AND VALUES:
327 RANDOM-NUMBER: a float, the random result
331 Returns a random floating point number in the allowable range of turtle
332 coordinates along the y axis.
334 These range from min-pycor - 0.5 (inclusive) to max-pycor + 0.5 (exclusive)
336 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
338 ((min (- (min-pycor) 0.5d0))
339 (max (+ (max-pycor) 0.5d0)))
340 (+ min (clnl-random:next-double (- max min)))))
342 (defun one-of (list-or-agentset)
343 "ONE-OF LIST-OR-AGENTSET => RESULT
345 LIST-OR-AGENTSET: LIST | AGENTSET
346 RESULT: RANDOM-VALUE | RANDOM-AGENT | :nobody
348 ARGUMENTS AND VALUES:
351 AGENTSET: An agent set
352 RANDOM-VALUE: a value in LIST
353 RANDOM-AGENT: an agent if AGENTSET is non empty
357 From an AGENTSET, returns a RANDOM-AGENT. If the agentset is empty, returns :nobody.
358 From a list, returns a RANDOM-VALUE. If the list is empty, an error occurs.
360 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#one-of"
362 ((agentset-p list-or-agentset)
364 ((agentset-list (agentset-list list-or-agentset))
365 (length (length agentset-list)))
366 (if (zerop length) :nobody (nth (clnl-random:next-int length) agentset-list))))
367 ((listp list-or-agentset)
369 ((length (length list-or-agentset)))
371 (error "one-of requires a nonempty list")
372 (nth (clnl-random:next-int length) list-or-agentset))))
373 (t (error "one-of requires a list or agentset"))))
376 (when (not (turtle-p *self*)) (error "Gotta call jump in turtle scope, dude (~A)" *self*))
380 (+ (turtle-xcor *self*) (* n (using-cached-sin (turtle-heading *self*))))))
384 (+ (turtle-ycor *self*) (* n (using-cached-cos (turtle-heading *self*)))))))
389 ARGUMENTS AND VALUES:
397 Sets the x-coordinate and y-coordinate for the turle. Equivalent to
398 set xcor x set ycor y, except it happens in one step inside of two.
400 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#setxy"
401 (when (not (turtle-p *self*)) (error "Gotta call setxy in turtle scope, dude (~A)" *self*))
402 (setf (turtle-xcor *self*) (wrap-x *topology* x))
403 (setf (turtle-ycor *self*) (wrap-y *topology* y)))
408 ARGUMENTS AND VALUES:
410 N: a double, the amount the turtle moves forward
415 Moves the current turtle forward N steps, one step at a time.
417 This moves forward one at a time in order to make the view updates look
418 good in the case of a purposefully slow running instance. If the number
419 is negative, the turtle moves backward.
421 If the current agent is not a turtle, it raises an error.
423 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#forward"
424 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
428 ((< (abs i) 3.2e-15) nil)
429 ((< (abs i) 1d0) (jump i))
430 (t (jump (if (> i 0d0) 1d0 -1d0)) (internal (- i (if (> i 0d0) 1d0 -1d0)))))))
433 (defun turn-right (n)
434 "TURN-RIGHT N => RESULT
436 ARGUMENTS AND VALUES:
438 N: a double, the amount the turtle turns
443 The turtle turns right by number degrees. (If number is negative, it turns left.)
445 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
446 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
448 ((new-heading (+ (turtle-heading *self*) n)))
449 (setf (turtle-heading *self*)
451 ((< new-heading 0) (+ (mod new-heading -360) 360))
452 ((>= new-heading 360) (mod new-heading 360))
456 "TURN-LEFT N => RESULT
458 ARGUMENTS AND VALUES:
460 N: a double, the amount the turtle turns
465 The turtle turns left by number degrees. (If number is negative, it turns right.)
467 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
470 (defun create-turtles (n &optional fn)
471 "CREATE-TURTLES N &optional FN => RESULT
473 ARGUMENTS AND VALUES:
475 N: an integer, the numbers of turtles to create
476 FN: A function, applied to each turtle after creation
481 Creates number new turtles at the origin.
483 New turtles have random integer headings and the color is randomly selected
484 from the 14 primary colors. If a function is supplied, the new turtles
487 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#create-turtles"
489 ((new-turtles (loop :repeat n :collect (create-turtle))))
490 (when fn (ask (list->agentset new-turtles :turtles) fn))))
492 (defun hatch (n &optional fn)
493 "HATCH N &optional FN => RESULT
495 ARGUMENTS AND VALUES:
497 N: an integer, the numbers of turtles to hatch
498 FN: A function, applied to each turtle after creation
503 The turtle in *self* creates N new turtles. Each new turtle inherits of all its
504 variables, including its location, from self.
506 If FN is supplied, the new turtles immediately run it.
508 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#hatch"
509 (when (not (turtle-p *self*)) (error "Can only hatch from turtle scope"))
511 ((new-turtles (loop :repeat n :collect (create-turtle *self*))))
512 (when fn (ask (list->agentset new-turtles :turtles) fn))))
514 (defun reset-ticks ()
515 "RESET-TICKS => RESULT
517 ARGUMENTS AND VALUES:
523 Resets the tick counter to zero, sets up all plots, then updates all plots.
525 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#reset-ticks"
529 "RESET-TICKS => RESULT
531 ARGUMENTS AND VALUES:
537 Advances the tick counter by one and updates all plots.
539 If the tick counter has not been started yet with reset-ticks, an error results.
541 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#tick"
543 (when (not *ticks*) (error "reset-ticks must be called"))
547 "TICKS => CURRENT-TICKS
549 ARGUMENTS AND VALUES:
551 CURRENT-TICKS: A positiv double, representing the current number of ticks
555 Reports the current value of the tick counter. The result is always a number and never negative.
557 If the tick counter has not been started yet with reset-ticks, an error results.
559 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ticks"
560 (when (not *ticks*) (error "reset-ticks must be called"))
563 (defun create-world (&key dims)
564 "CREATE-WORLD &key DIMS => RESULT
566 DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
568 ARGUMENTS AND VALUES:
571 XMIN: An integer representing the minimum patch coord in X
572 XMAX: An integer representing the maximum patch coord in X
573 YMIN: An integer representing the minimum patch coord in Y
574 YMAX: An integer representing the maximum patch coord in Y
578 Initializes the world in the NVM.
580 This should be called before using the engine in any real capacity. If
581 called when an engine is already running, it may do somethign weird."
582 (setf *dimensions* dims)
586 :for y :from (max-pycor) :downto (min-pycor)
588 :for x :from (min-pxcor) :to (max-pxcor)
590 :xcor (coerce x 'double-float)
591 :ycor (coerce y 'double-float)
594 (setf *current-id* 0))
596 ; These match netlogo's dump
597 (defgeneric dump-object (o))
599 (defmethod dump-object ((n double-float))
600 (multiple-value-bind (int rem) (floor n)
602 (format nil "~A" int)
604 ((output (format nil "~D" n)))
605 ; Someday we'll have d<posint>, but this is not that day!
606 (cl-ppcre:regex-replace "d-" (cl-ppcre:regex-replace "d0" output "") "E-")))))
608 (defmethod dump-object ((o string)) (format nil "~A" (cl-ppcre:regex-replace-all "\"" (format nil "~S" o) "\"\"")))
610 (defmethod dump-object ((o (eql t))) "true")
611 (defmethod dump-object ((o (eql nil))) "false")
613 (defmethod dump-object ((o list))
615 ((agentset-p o) (format nil "(agentset, ~A ~A)" (dump-object (count o)) (string-downcase (agentset-breed o))))
616 (t (format nil "[~{~A~^ ~}]" (mapcar #'dump-object o)))))
618 (defmethod dump-object ((o patch))
619 (format nil "(patch ~A ~A)" (dump-object (patch-xcor o)) (dump-object (patch-ycor o))))
621 (defmethod dump-object ((o turtle)) (format nil "(turtle ~A)" (dump-object (turtle-who o))))
622 (defmethod dump-object ((o (eql :nobody))) (format nil "nobody"))
624 (defun current-state ()
625 "CURRENT-STATE => WORLD-STATE
627 ARGUMENTS AND VALUES:
629 WORLD-STATE: A list, the current state of the whole world
633 Dumps out the state of the world.
635 This is useful for visualizations and also storing in a common lisp
636 data structure for easy usage in a common lisp instance. It's preferable
637 to use this when working with the nvm than the output done by export-world.
639 Currently this only dumps out turtle and patch information.
641 This is called CURRENT-STATE because export-world is an actual primitive
647 :color (turtle-color turtle)
648 :xcor (turtle-xcor turtle)
649 :ycor (turtle-ycor turtle)
650 :heading (turtle-heading turtle)
651 :size (turtle-size turtle)))
656 :color (patch-color patch)
657 :xcor (patch-xcor patch)
658 :ycor (patch-ycor patch)))
661 (defun export-turtles ()
666 "\"who\",\"color\",\"heading\",\"xcor\",\"ycor\",\"shape\",\"label\",\"label-color\","
667 "\"breed\",\"hidden?\",\"size\",\"pen-size\",\"pen-mode\""))
671 "\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",\"\"\"default\"\"\",\"~A\",\"~A\",\"{all-turtles}\",\"false\",\"~A\",~A"
672 (dump-object (turtle-who turtle))
673 (dump-object (turtle-color turtle))
674 (dump-object (turtle-heading turtle))
675 (dump-object (turtle-xcor turtle))
676 (dump-object (turtle-ycor turtle))
677 (dump-object (turtle-label turtle))
678 (dump-object (turtle-label-color turtle))
679 (dump-object (turtle-size turtle))
680 "\"1\",\"\"\"up\"\"\""))
683 (defun export-patches ()
687 "\"pxcor\",\"pycor\",\"pcolor\",\"plabel\",\"plabel-color\"")
691 "\"~A\",\"~A\",\"~A\",\"\"\"\"\"\",\"9.9\""
692 (dump-object (patch-xcor patch))
693 (dump-object (patch-ycor patch))
694 (dump-object (patch-color patch))))
697 (defun export-world ()
698 "EXPORT-WORLD => WORLD-CSV
700 ARGUMENTS AND VALUES:
702 WORLD-CSV: A string, the csv of the world
706 Dumps out a csv matching NetLogo's export world.
708 This is useful for serializing the current state of the engine in order
709 to compare against NetLogo or to reimport later. Contains everything needed
710 to boot up a NetLogo instance in the exact same state."
711 (format nil "~{~A~%~}"
713 (format nil "~S" "RANDOM STATE")
714 (format nil "~S" (clnl-random:export))
716 (format nil "~S" "GLOBALS")
718 "\"min-pxcor\",\"max-pxcor\",\"min-pycor\",\"max-pycor\",\"perspective\",\"subject\","
719 "\"nextIndex\",\"directed-links\",\"ticks\",")
720 (format nil "\"~A\",\"~A\",\"~A\",\"~A\",\"0\",\"nobody\",\"~A\",\"\"\"NEITHER\"\"\",\"-1\""
721 (min-pxcor) (max-pxcor) (min-pycor) (max-pycor) *current-id*)
723 (format nil "~{~A~^~%~}" (export-turtles))
725 (format nil "~{~A~^~%~}" (export-patches))
727 (format nil "~S" "LINKS")
728 "\"end1\",\"end2\",\"color\",\"label\",\"label-color\",\"hidden?\",\"breed\",\"thickness\",\"shape\",\"tie-mode\""