Move from github, collapse gltk and strictmath, add candle
[clnl] / src / main / clnl / nvm / turtles.lisp
diff --git a/src/main/clnl/nvm/turtles.lisp b/src/main/clnl/nvm/turtles.lisp
new file mode 100644 (file)
index 0000000..49c1834
--- /dev/null
@@ -0,0 +1,255 @@
+(in-package #:clnl-nvm)
+
+(defcommand create-turtles (n &optional breed fn)
+ "CREATE-TURTLES N &optional BREED FN => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  N: an integer, the numbers of turtles to create
+  BREED: a breed
+  FN: A function, applied to each turtle after creation
+
+DESCRIPTION:
+
+  Creates N new turtles at the origin.
+
+  New turtles have random integer headings and the color is randomly selected
+  from the 14 primary colors.  If FN is supplied, the new turtles immediately
+  run it.  If a BREED is supplied, that is the breed the new turtles are set
+  to.
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#create-turtles"
+ (let
+  ((new-turtles (loop :repeat n :collect (create-turtle breed))))
+  (when fn (ask (list->agentset new-turtles :turtles) fn))))
+
+(defcommand die ()
+ "DIE => RESULT
+
+  RESULT: :undefined
+
+DESCRIPTION:
+
+  The turtle or link dies
+
+  A dead agent ceases to exist. The effects of this include:
+  - The agent will not execute any further code.
+  - The agent will disappear from any agentsets it was in, reducing the size of those agentsets by one.
+  - Any variable that was storing the agent will now instead have nobody in it.
+  - If the dead agent was a turtle, every link connected to it also dies.
+  - If the observer was watching or following the agent, the observer's perspective resets.
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#die"
+ (when (not (turtle-p *self*)) (error "Gotta call die in turtle scope, dude (~A)" *self*))
+ (setf (turtle-who *self*) -1)
+ (setf *turtles* (remove *self* *turtles*))
+ (let
+  ((patch (patch-at (turtle-xcor *self*) (turtle-ycor *self*))))
+  (setf (patch-turtles patch) (remove *self* (patch-turtles patch))))
+ (error (make-condition 'death)))
+
+(defcommand hatch (n &optional fn)
+ "HATCH N &optional FN => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  N: an integer, the numbers of turtles to hatch
+  FN: A function, applied to each turtle after creation
+
+DESCRIPTION:
+
+  The turtle in *self* creates N new turtles. Each new turtle inherits of all its
+  variables, including its location, from self.
+
+  If FN is supplied, the new turtles immediately run it.
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#hatch"
+ (when (not (turtle-p *self*)) (error "Can only hatch from turtle scope"))
+ (let
+  ((new-turtles (loop :repeat n :collect (create-turtle nil *self*))))
+  (when fn (ask (list->agentset new-turtles :turtles) fn))))
+
+(defcommand forward (n)
+ "FORWARD N => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  N: a double, the amount the turtle moves forward
+
+DESCRIPTION:
+
+  Moves the current turtle forward N steps, one step at a time.
+
+  This moves forward one at a time in order to make the view updates look
+  good in the case of a purposefully slow running instance.  If the number
+  is negative, the turtle moves backward.
+
+  If the current agent is not a turtle, it raises an error.
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#forward"
+ (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
+ (labels
+  ((internal (i)
+    (cond
+     ((< (abs i) 3.2e-15) nil)
+     ((< (abs i) 1d0) (jump i))
+     (t (jump (if (> i 0d0) 1d0 -1d0)) (internal (- i (if (> i 0d0) 1d0 -1d0)))))))
+  (internal n)))
+
+(defun jump (n)
+ (when (not (turtle-p *self*)) (error "Gotta call jump in turtle scope, dude (~A)" *self*))
+ (with-patch-update *self*
+  (setf
+   (turtle-xcor *self*)
+   (wrap-x *topology*
+    (+ (turtle-xcor *self*) (* n (using-cached-sin (turtle-heading *self*))))))
+  (setf
+   (turtle-ycor *self*)
+   (wrap-y *topology*
+    (+ (turtle-ycor *self*) (* n (using-cached-cos (turtle-heading *self*))))))))
+
+(defun random-xcor ()
+ "RANDOM-XCOR => RANDOM-NUMBER
+
+ARGUMENTS AND VALUES:
+
+  RANDOM-NUMBER: a float, the random result
+
+DESCRIPTION:
+
+  Returns a random floating point number in the allowable range of turtle
+  coordinates along the x axis.
+
+  These range from min-pxcor - 0.5 (inclusive) to max-pxcor + 0.5 (exclusive)
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
+ (let
+  ((min (- (min-pxcor) 0.5d0))
+   (max (+ (max-pxcor) 0.5d0)))
+  (+ min (clnl-random:next-double (- max min)))))
+
+(defun random-ycor ()
+ "RANDOM-YCOR => RANDOM-NUMBER
+
+ARGUMENTS AND VALUES:
+
+  RANDOM-NUMBER: a float, the random result
+
+DESCRIPTION:
+
+  Returns a random floating point number in the allowable range of turtle
+  coordinates along the y axis.
+
+  These range from min-pycor - 0.5 (inclusive) to max-pycor + 0.5 (exclusive)
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#random-cor"
+ (let
+  ((min (- (min-pycor) 0.5d0))
+   (max (+ (max-pycor) 0.5d0)))
+  (+ min (clnl-random:next-double (- max min)))))
+
+(defcommand set-default-shape (breed shape)
+ "SET-DEFAULT-SHAPE BREED SHAPE => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  BREED: a valid breed
+  SHAPE: a string
+
+DESCRIPTION:
+
+  Specifies a default initial shape for a BREED. When a turtle, or it changes breeds,
+  its shape is set to the given shape.
+
+  SET-DEFAULT-SHAPE doesn't affect existing agents, only agents you create afterwards.
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#set-default-shape"
+ (when (not (breed-p breed)) (error "Need a valid breed"))
+ (setf (breed-default-shape breed) shape))
+
+(defcommand setxy (x y)
+ "SETXY X Y => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  X: a double
+  Y: a double
+
+DESCRIPTION:
+
+  Sets the x-coordinate and y-coordinate for the turle.  Equivalent to
+  set xcor x set ycor y, except it happens in one step inside of two.
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#setxy"
+ (when (not (turtle-p *self*)) (error "Gotta call setxy in turtle scope, dude (~A)" *self*))
+ (setf (turtle-xcor *self*) (wrap-x *topology* x))
+ (setf (turtle-ycor *self*) (wrap-y *topology* y)))
+
+(defun turtles-here (&optional breed)
+ "TURTLES-HERE => TURTLES
+
+ARGUMENTS AND VALUES:
+
+  TURTLES: an agentset
+
+DESCRIPTION:
+
+  Returns the agentset consisting of all the turtles sharing the patch
+  with the agent in by *self*
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#turtles-here"
+ (when (not (turtle-p *self*)) (error "Gotta call turtles-here with a turtle"))
+ (let
+  ((patch-turtles (patch-turtles (patch-at (turtle-xcor *self*) (turtle-ycor *self*)))))
+  (list->agentset
+   (if breed (remove breed patch-turtles :key #'turtle-breed :test-not #'eql) patch-turtles)
+   (or breed :turtles))))
+
+(defcommand turn-right (n)
+ "TURN-RIGHT N => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  N: a double, the amount the turtle turns
+
+DESCRIPTION:
+
+  The turtle turns right by number degrees. (If number is negative, it turns left.)
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
+ (when (not (turtle-p *self*)) (error "Gotta call fd in turtle scope, dude (~A)" *self*))
+ (let
+  ((new-heading (+ (turtle-heading *self*) n)))
+  (setf (turtle-heading *self*)
+   (cond
+    ((< new-heading 0) (+ (mod new-heading -360) 360))
+    ((>= new-heading 360) (mod new-heading 360))
+    (t new-heading)))))
+
+(defcommand turn-left (n)
+ "TURN-LEFT N => RESULT
+
+  RESULT: :undefined
+
+ARGUMENTS AND VALUES:
+
+  N: a double, the amount the turtle turns
+
+DESCRIPTION:
+
+  The turtle turns left by number degrees. (If number is negative, it turns right.)
+
+  See http://ccl.northwestern.edu/netlogo/docs/dictionary.html#right"
+ (turn-right (- n)))