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 :shape (breed-default-shape :turtles)
62 :xcor (if base-turtle (turtle-xcor base-turtle) 0d0)
63 :ycor (if base-turtle (turtle-ycor base-turtle) 0d0))))
65 ((patch (patch-at (turtle-xcor new-turtle) (turtle-ycor new-turtle))))
66 (setf (patch-turtles patch) (nconc (patch-turtles patch) (list new-turtle))))
67 (setf *turtles* (nconc *turtles* (list new-turtle)))
76 RESULT: undefined, commands don't return
80 The turtle or link dies
82 A dead agent ceases to exist. The effects of this include:
83 - The agent will not execute any further code.
84 - The agent will disappear from any agentsets it was in, reducing the size of those agentsets by one.
85 - Any variable that was storing the agent will now instead have nobody in it.
86 - If the dead agent was a turtle, every link connected to it also dies.
87 - If the observer was watching or following the agent, the observer's perspective resets.
89 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#die"
90 (when (not (turtle-p *self*)) (error "Gotta call die in turtle scope, dude (~A)" *self*))
91 (setf (turtle-who *self*) -1)
92 (setf *turtles* (remove *self* *turtles*)))
95 "PATCHES => ALL-PATCHES
99 ALL-PATCHES: a NetLogo agentset, all patches
103 Reports the agentset consisting of all the patches.
105 This agentset is special in that it represents the living patches
106 each time it's used, so changes depending on the state of the engine.
108 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#patches"
112 "TURTLES => ALL-TURTLES
114 ARGUMENTS AND VALUES:
116 ALL-TURTLES: a NetLogo agentset, all turtles
120 Reports the agentset consisting of all the turtles.
122 This agentset is special in that it represents the living turtles
123 each time it's used, so changes depending on the state of the engine.
125 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtles"
128 (defun turtles-here ()
129 "TURTLES-HERE => TURTLES
131 ARGUMENTS AND VALUES:
137 Returns the agentset consisting of all the turtles sharing the patch
138 with the agent in by *self*
140 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtles-here"
141 (when (not (turtle-p *self*)) (error "Gotta call turtles-here with a turtle"))
142 (list->agentset (patch-turtles (patch-at (turtle-xcor *self*) (turtle-ycor *self*))) :turtles))
144 (defun ask (agent-or-agentset fn)
145 "ASK AGENT-OR-AGENTSET FN => RESULT
147 AGENT-OR-AGENTSET: AGENT | AGENTSET
149 ARGUMENTS AND VALUES:
151 FN: a function, run on each agent
152 RESULT: undefined, commands don't return
153 AGENT: a NetLogo agent
154 AGENTSET: a NetLogo agentset
158 ASK is equivalent to ask in NetLogo.
160 The specified AGENTSET or AGENT runs the given FN. In the case of an
161 AGENTSET, the order in which the agents are run is random each time,
162 and only agents that are in the set at the beginning of the call.
164 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ask"
166 ((agentset-p agent-or-agentset)
168 ((iter (shufflerator (agentset-list agent-or-agentset))))
170 :for agent := (funcall iter)
172 :do (let ((*myself* *self*) (*self* agent)) (with-stop-handler (funcall fn))))))
173 ((agent-p agent-or-agentset)
174 (let ((*myself* *self*) (*self* agent-or-agentset)) (with-stop-handler (funcall fn))))
176 (error "Ask requires an agentset or agent but got: ~A" agent-or-agentset))))
178 (defun count (agentset)
181 ARGUMENTS AND VALUES:
183 AGENTSET: a NetLogo agentset
188 COUNT is equivalent to count in NetLogo. Returns N, the number of
191 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#count"
192 (coerce (length (agentset-list agentset)) 'double-float))
197 ARGUMENTS AND VALUES:
203 Clears ticks, turtles, patches, globals (unimplemented).
205 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#clear-all"
213 ARGUMENTS AND VALUES:
219 As of yet, this does nothing. A placeholder method for forced dipslay
220 updates from the engine.
222 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#display"
228 ARGUMENTS AND VALUES:
234 Returns from the current stop block, which will halt the currently running
235 thing, be that the program, current ask block, or procedure. Stop has odd
236 semantics that are best gleaned from the actual NetLogo manual.
238 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#stop"
239 (error (make-condition 'stop)))
241 (defun of (fn agent-or-agentset)
242 "OF FN AGENT-OR-AGENTSET => RESULT
244 AGENT-OR-AGENTSET: AGENT | AGENTSET
245 RESULT: RESULT-LIST | RESULT-VALUE
247 ARGUMENTS AND VALUES:
249 FN: a function, run on each agent
250 AGENT: a NetLogo agent
251 AGENTSET: a NetLogo agentset
253 RESULT-VALUE: a single value
257 OF is equivalent to of in NetLogo.
259 The specified AGENTSET or AGENT runs the given FN. In the case of an
260 AGENTSET, the order in which the agents are run is random each time,
261 and only agents that are in the set at the beginning of the call.
263 RESULT-LIST is returned when the input is an AGENTSET, but RESULT-VALUE
264 is returned when only passed an AGENT.
266 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#of"
268 ((agentset-p agent-or-agentset)
270 ((iter (shufflerator (agentset-list agent-or-agentset))))
272 :for agent := (funcall iter)
274 :collect (let ((*myself* *self*) (*self* agent)) (funcall fn)))))
275 ((agent-p agent-or-agentset)
276 (let ((*myself* *self*) (*self* agent-or-agentset)) (funcall fn)))
278 (error "Of requires an agentset or agent but got: ~A" agent-or-agentset))))
280 (defun with (agentset fn)
281 "WITH AGENTSET FN => RESULT-AGENTSET
283 ARGUMENTS AND VALUES:
285 AGENTSET: a NetLogo agentset
286 FN: a boolean function, run on each agent to determine if included
287 RESULT-AGENTSET: an agentset of valid agents
291 WITH is equivalent to with in NetLogo.
293 Returns a new agentset containing only those agents that reported true
296 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#with"
300 (let ((*myself* *self*) (*self* agent)) (funcall fn)))
301 (agentset-list agentset))
302 (agentset-breed agentset)))
304 (defun shufflerator (agentset-list)
306 ((copy (copy-list agentset-list))
312 ((idx (when (< i (1- (length copy))) (+ i (clnl-random:next-int (- (length copy) i))))))
313 (when idx (setf agent (nth idx copy)))
314 (when idx (setf (nth idx copy) (nth i copy)))
316 (fetch) ; we pre-fetch because netlogo does, rng sync hype!
319 ((> i (length copy)) nil)
320 ((= i (length copy)) (incf i) (car (last copy)))
321 (t (let ((result agent)) (fetch) result)))))))
323 (defun random-float (n)
324 "RANDOM-FLOAT N => RANDOM-NUMBER
326 ARGUMENTS AND VALUES:
328 N: a double, the upper bound of the random float
329 RANDOM-NUMBER: a double, the random result
333 Returns a random number strictly closer to zero than N.
335 If number is positive, returns a random floating point number greater than
336 or equal to 0 but strictly less than number.
338 If number is negative, returns a random floating point number less than or equal
339 to 0, but strictly greater than number.
341 If number is zero, the result is always 0.
343 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-float"
344 (clnl-random:next-double n))
347 "RANDOM N => RANDOM-NUMBER
349 ARGUMENTS AND VALUES:
351 N: an integer, the upper bound of the random
352 RANDOM-NUMBER: an integer, the random result
356 Returns a random number strictly closer to zero than N.
358 If number is positive, returns a random integer greater than or equal to 0,
359 but strictly less than number.
361 If number is negative, returns a random integer less than or equal to 0,
362 but strictly greater than number.
364 If number is zero, the result is always 0.
366 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random"
367 (coerce (clnl-random:next-long (truncate n)) 'double-float))
369 (defun random-xcor ()
370 "RANDOM-XCOR => RANDOM-NUMBER
372 ARGUMENTS AND VALUES:
374 RANDOM-NUMBER: a float, the random result
378 Returns a random floating point number in the allowable range of turtle
379 coordinates along the x axis.
381 These range from min-pxcor - 0.5 (inclusive) to max-pxcor + 0.5 (exclusive)
383 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
385 ((min (- (min-pxcor) 0.5d0))
386 (max (+ (max-pxcor) 0.5d0)))
387 (+ min (clnl-random:next-double (- max min)))))
389 (defun random-ycor ()
390 "RANDOM-YCOR => RANDOM-NUMBER
392 ARGUMENTS AND VALUES:
394 RANDOM-NUMBER: a float, the random result
398 Returns a random floating point number in the allowable range of turtle
399 coordinates along the y axis.
401 These range from min-pycor - 0.5 (inclusive) to max-pycor + 0.5 (exclusive)
403 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
405 ((min (- (min-pycor) 0.5d0))
406 (max (+ (max-pycor) 0.5d0)))
407 (+ min (clnl-random:next-double (- max min)))))
409 (defun one-of (list-or-agentset)
410 "ONE-OF LIST-OR-AGENTSET => RESULT
412 LIST-OR-AGENTSET: LIST | AGENTSET
413 RESULT: RANDOM-VALUE | RANDOM-AGENT | :nobody
415 ARGUMENTS AND VALUES:
418 AGENTSET: An agent set
419 RANDOM-VALUE: a value in LIST
420 RANDOM-AGENT: an agent if AGENTSET is non empty
424 From an AGENTSET, returns a RANDOM-AGENT. If the agentset is empty, returns :nobody.
425 From a list, returns a RANDOM-VALUE. If the list is empty, an error occurs.
427 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#one-of"
429 ((agentset-p list-or-agentset)
431 ((agentset-list (agentset-list list-or-agentset))
432 (length (length agentset-list)))
433 (if (zerop length) :nobody (nth (clnl-random:next-int length) agentset-list))))
434 ((listp list-or-agentset)
436 ((length (length list-or-agentset)))
438 (error "one-of requires a nonempty list")
439 (nth (clnl-random:next-int length) list-or-agentset))))
440 (t (error "one-of requires a list or agentset"))))
443 (when (not (turtle-p *self*)) (error "Gotta call jump in turtle scope, dude (~A)" *self*))
444 (with-patch-update *self*
448 (+ (turtle-xcor *self*) (* n (using-cached-sin (turtle-heading *self*))))))
452 (+ (turtle-ycor *self*) (* n (using-cached-cos (turtle-heading *self*))))))))
457 ARGUMENTS AND VALUES:
465 Sets the x-coordinate and y-coordinate for the turle. Equivalent to
466 set xcor x set ycor y, except it happens in one step inside of two.
468 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#setxy"
469 (when (not (turtle-p *self*)) (error "Gotta call setxy in turtle scope, dude (~A)" *self*))
470 (setf (turtle-xcor *self*) (wrap-x *topology* x))
471 (setf (turtle-ycor *self*) (wrap-y *topology* y)))
473 (defun set-default-shape (breed shape)
474 "SET-DEFAULT-SHAPE BREED SHAPE => RESULT
476 ARGUMENTS AND VALUES:
484 Specifies a default initial shape for a BREED. When a turtle, or it changes breeds,
485 its shape is set to the given shape.
487 SET-DEFAULT-SHAPE doesn't affect existing agents, only agents you create afterwards.
489 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#set-default-shape"
490 (when (not (breed-p breed)) (error "Need a valid breed"))
491 (setf (breed-default-shape breed) shape))
496 ARGUMENTS AND VALUES:
498 N: a double, the amount the turtle moves forward
503 Moves the current turtle forward N steps, one step at a time.
505 This moves forward one at a time in order to make the view updates look
506 good in the case of a purposefully slow running instance. If the number
507 is negative, the turtle moves backward.
509 If the current agent is not a turtle, it raises an error.
511 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#forward"
512 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
516 ((< (abs i) 3.2e-15) nil)
517 ((< (abs i) 1d0) (jump i))
518 (t (jump (if (> i 0d0) 1d0 -1d0)) (internal (- i (if (> i 0d0) 1d0 -1d0)))))))
521 (defun turn-right (n)
522 "TURN-RIGHT N => RESULT
524 ARGUMENTS AND VALUES:
526 N: a double, the amount the turtle turns
531 The turtle turns right by number degrees. (If number is negative, it turns left.)
533 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
534 (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
536 ((new-heading (+ (turtle-heading *self*) n)))
537 (setf (turtle-heading *self*)
539 ((< new-heading 0) (+ (mod new-heading -360) 360))
540 ((>= new-heading 360) (mod new-heading 360))
544 "TURN-LEFT N => RESULT
546 ARGUMENTS AND VALUES:
548 N: a double, the amount the turtle turns
553 The turtle turns left by number degrees. (If number is negative, it turns right.)
555 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
558 (defun create-turtles (n &optional fn)
559 "CREATE-TURTLES N &optional FN => RESULT
561 ARGUMENTS AND VALUES:
563 N: an integer, the numbers of turtles to create
564 FN: A function, applied to each turtle after creation
569 Creates number new turtles at the origin.
571 New turtles have random integer headings and the color is randomly selected
572 from the 14 primary colors. If a function is supplied, the new turtles
575 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#create-turtles"
577 ((new-turtles (loop :repeat n :collect (create-turtle))))
578 (when fn (ask (list->agentset new-turtles :turtles) fn))))
580 (defun hatch (n &optional fn)
581 "HATCH N &optional FN => RESULT
583 ARGUMENTS AND VALUES:
585 N: an integer, the numbers of turtles to hatch
586 FN: A function, applied to each turtle after creation
591 The turtle in *self* creates N new turtles. Each new turtle inherits of all its
592 variables, including its location, from self.
594 If FN is supplied, the new turtles immediately run it.
596 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#hatch"
597 (when (not (turtle-p *self*)) (error "Can only hatch from turtle scope"))
599 ((new-turtles (loop :repeat n :collect (create-turtle *self*))))
600 (when fn (ask (list->agentset new-turtles :turtles) fn))))
602 (defun reset-ticks ()
603 "RESET-TICKS => RESULT
605 ARGUMENTS AND VALUES:
611 Resets the tick counter to zero, sets up all plots, then updates all plots.
613 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#reset-ticks"
617 "RESET-TICKS => RESULT
619 ARGUMENTS AND VALUES:
625 Advances the tick counter by one and updates all plots.
627 If the tick counter has not been started yet with reset-ticks, an error results.
629 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#tick"
631 (when (not *ticks*) (error "reset-ticks must be called"))
635 "TICKS => CURRENT-TICKS
637 ARGUMENTS AND VALUES:
639 CURRENT-TICKS: A positiv double, representing the current number of ticks
643 Reports the current value of the tick counter. The result is always a number and never negative.
645 If the tick counter has not been started yet with reset-ticks, an error results.
647 See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#ticks"
648 (when (not *ticks*) (error "reset-ticks must be called"))
651 (defun clear-patches ()
655 :for y :from (max-pycor) :downto (min-pycor)
657 :for x :from (min-pxcor) :to (max-pxcor)
659 :xcor (coerce x 'double-float)
660 :ycor (coerce y 'double-float)
663 (defun clear-turtles ()
665 (setf *current-id* 0))
667 (defun clear-ticks ()
670 (defun create-world (&key dims globals turtles-own-vars patches-own-vars)
671 "CREATE-WORLD &key DIMS GLOBALS TURTLES-OWN-VARS PATCHES-OWN-VARS => RESULT
673 DIMS: (:xmin XMIN :xmax XMAX :ymin YMIN :ymax YMAX)
675 TURTLES-OWN-VARS: TURTLES-OWN-VAR*
676 PATCHES-OWN-VARS: PATCHES-OWN-VAR*
677 GLOBAL: (GLOBAL-NAME GLOBAL-ACCESS-FUNC)
679 ARGUMENTS AND VALUES:
682 XMIN: An integer representing the minimum patch coord in X
683 XMAX: An integer representing the maximum patch coord in X
684 YMIN: An integer representing the minimum patch coord in Y
685 YMAX: An integer representing the maximum patch coord in Y
686 TURTLES-OWN-VAR: Symbol for the turtles own variable in the keyword package
687 PATCHES-OWN-VAR: Symbol for the patches own variable in the keyword package
688 GLOBAL-NAME: Symbol for the global in the keyword package
689 GLOBAL-ACCESS-FUNC: Function to get the value of the global
693 Initializes the world in the NVM.
695 This should be called before using the engine in any real capacity. If
696 called when an engine is already running, it may do somethign weird."
697 (setf *turtles-own-vars* turtles-own-vars)
698 (setf *patches-own-vars* patches-own-vars)
699 (setf *dimensions* dims)
700 (setf *globals* globals)
701 (setf *breeds* (list (list :turtles "default")))
706 ; These match netlogo's dump
707 (defgeneric dump-object (o))
709 (defmethod dump-object ((n double-float))
710 (multiple-value-bind (int rem) (floor n)
712 (format nil "~A" int)
714 ((output (format nil "~D" n)))
715 ; Someday we'll have d<posint>, but this is not that day!
716 (cl-ppcre:regex-replace "d-" (cl-ppcre:regex-replace "d0" output "") "E-")))))
718 (defmethod dump-object ((o string)) (format nil "~A" (cl-ppcre:regex-replace-all "\"" (format nil "~S" o) "\"\"")))
720 (defmethod dump-object ((o (eql t))) "true")
721 (defmethod dump-object ((o (eql nil))) "false")
723 (defmethod dump-object ((o list))
725 ((agentset-p o) (format nil "(agentset, ~A ~A)" (dump-object (count o)) (string-downcase (agentset-breed o))))
726 (t (format nil "[~{~A~^ ~}]" (mapcar #'dump-object o)))))
728 (defmethod dump-object ((o patch))
729 (format nil "(patch ~A ~A)" (dump-object (patch-xcor o)) (dump-object (patch-ycor o))))
731 (defmethod dump-object ((o turtle)) (format nil "(turtle ~A)" (dump-object (turtle-who o))))
732 (defmethod dump-object ((o (eql :nobody))) (format nil "nobody"))
734 (defun current-state ()
735 "CURRENT-STATE => WORLD-STATE
737 ARGUMENTS AND VALUES:
739 WORLD-STATE: A list, the current state of the whole world
743 Dumps out the state of the world.
745 This is useful for visualizations and also storing in a common lisp
746 data structure for easy usage in a common lisp instance. It's preferable
747 to use this when working with the nvm than the output done by export-world.
749 Currently this only dumps out turtle and patch information.
751 This is called CURRENT-STATE because export-world is an actual primitive
757 :color (turtle-color turtle)
758 :xcor (turtle-xcor turtle)
759 :ycor (turtle-ycor turtle)
760 :heading (turtle-heading turtle)
761 :size (turtle-size turtle)))
766 :color (patch-color patch)
767 :xcor (patch-xcor patch)
768 :ycor (patch-ycor patch)))
771 (defun export-turtles ()
775 (format nil "~A~A~{,\"~A\"~}"
776 "\"who\",\"color\",\"heading\",\"xcor\",\"ycor\",\"shape\",\"label\",\"label-color\","
777 "\"breed\",\"hidden?\",\"size\",\"pen-size\",\"pen-mode\""
778 (mapcar #'string-downcase *turtles-own-vars*)))
782 "\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",\"~A\",\"{all-turtles}\",\"false\",\"~A\",~A~{,\"~A\"~}"
783 (dump-object (turtle-who turtle))
784 (dump-object (turtle-color turtle))
785 (dump-object (turtle-heading turtle))
786 (dump-object (turtle-xcor turtle))
787 (dump-object (turtle-ycor turtle))
788 (dump-object (turtle-shape turtle))
789 (dump-object (turtle-label turtle))
790 (dump-object (turtle-label-color turtle))
791 (dump-object (turtle-size turtle))
792 "\"1\",\"\"\"up\"\"\""
793 (mapcar #'dump-object (mapcar (lambda (var) (agent-value-inner turtle var)) *turtles-own-vars*))))
796 (defun export-patches ()
800 (format nil "\"pxcor\",\"pycor\",\"pcolor\",\"plabel\",\"plabel-color\"~{,\"~A\"~}"
801 (mapcar #'string-downcase *patches-own-vars*)))
805 "\"~A\",\"~A\",\"~A\",\"\"\"\"\"\",\"9.9\"~{,\"~A\"~}"
806 (dump-object (patch-xcor patch))
807 (dump-object (patch-ycor patch))
808 (dump-object (patch-color patch))
809 (mapcar #'dump-object (mapcar (lambda (var) (agent-value-inner patch var)) *patches-own-vars*))))
812 (defun export-world ()
813 "EXPORT-WORLD => WORLD-CSV
815 ARGUMENTS AND VALUES:
817 WORLD-CSV: A string, the csv of the world
821 Dumps out a csv matching NetLogo's export world.
823 This is useful for serializing the current state of the engine in order
824 to compare against NetLogo or to reimport later. Contains everything needed
825 to boot up a NetLogo instance in the exact same state."
826 (format nil "~{~A~%~}"
828 (format nil "~S" "RANDOM STATE")
829 (format nil "~S" (clnl-random:export))
831 (format nil "~S" "GLOBALS")
832 (format nil "~A~A~{\"~A\"~^,~}"
833 "\"min-pxcor\",\"max-pxcor\",\"min-pycor\",\"max-pycor\",\"perspective\",\"subject\","
834 "\"nextIndex\",\"directed-links\",\"ticks\","
835 (mapcar #'string-downcase (mapcar #'car *globals*)))
836 (format nil "\"~A\",\"~A\",\"~A\",\"~A\",\"0\",\"nobody\",\"~A\",\"\"\"NEITHER\"\"\",\"~A\"~{,\"~A\"~}"
837 (min-pxcor) (max-pxcor) (min-pycor) (max-pycor) *current-id* (dump-object (or *ticks* -1d0))
838 (mapcar #'dump-object (mapcar #'funcall (mapcar #'cadr *globals*))))
840 (format nil "~{~A~^~%~}" (export-turtles))
842 (format nil "~{~A~^~%~}" (export-patches))
844 (format nil "~S" "LINKS")
845 "\"end1\",\"end2\",\"color\",\"label\",\"label-color\",\"hidden?\",\"breed\",\"thickness\",\"shape\",\"tie-mode\""