Move from github, collapse gltk and strictmath, add candle
[clnl] / docs / CLNL-as-a-lisp-library.md
diff --git a/docs/CLNL-as-a-lisp-library.md b/docs/CLNL-as-a-lisp-library.md
new file mode 100644 (file)
index 0000000..d4d6dab
--- /dev/null
@@ -0,0 +1,235 @@
+One way to use CLNL is as a library loaded into a common lisp instance.  Once you have the source release from the releases page loaded up into however you configure asdf, you can start writing models using the clnl-* packages.  However, a good place to begin is by letting CLNL convert a model to common lisp so you can start from a known place.
+
+Everything that the generated code could use is external in the proper packages, so that you could optionally write the code in pure common lisp without using the generator.
+
+## Converting Wolf Sheep Predation as a headless model
+
+This code creates the wolf sheep common lisp file:
+
+```lisp
+(asdf:load-system :clnl)
+
+(with-open-file (out "wolfsheep.lisp" :direction :output)
+ (mapcar
+  (lambda (form) (pprint form out))
+  (with-open-file (str "resources/clnl/models/Wolf Sheep Predation.nlogo")
+   (clnl:nlogo->lisp str :wolfsheep 'boot))))
+```
+
+From there, you can boot the model with
+
+```
+sbcl --eval '(asdf:load-system :clnl)' --load 'wolfsheep.lisp' --eval '(wolfsheep::boot)'
+```
+
+Then you can run functions headlessly from the REPL with:
+
+```lisp
+(wolfsheep::setup)
+
+(wolfsheep::go)
+```
+You can then mix common lisp forms and functions from your model
+
+```lisp
+(loop :repeat 50 :do (wolfsheep::go))
+```
+
+Then you can start doing headless analysis on your model by using clnl-nvm primitives
+
+```lisp
+(wolfsheep::setup)
+
+(loop
+ :repeat 50 :do (wolfsheep::go)
+ :collect (list (clnl-nvm:count :sheep) (clnl-nvm:count :wolves)))
+```
+
+## Wolf Sheep as a non headless model
+
+If, however, you'd rather run with the interface as well, you would use
+
+```lisp
+(asdf:load-system :clnl)
+
+(with-open-file (out "wolfsheep.lisp" :direction :output)
+ (mapcar
+  (lambda (form) (pprint form out))
+  (with-open-file (str "resources/clnl/models/Wolf Sheep Predation.nlogo")
+   (clnl:nlogo->lisp str :wolfsheep 'boot :initialize-interface t))))
+```
+
+Then load up the model as such:
+
+```
+sbcl --eval '(asdf:load-system :clnl)' --load 'wolfsheep.lisp' --eval '(wolfsheep::boot)'
+```
+
+Then start the interface
+
+```lisp
+(sb-thread:make-thread #'clnl-interface:run)
+```
+
+Then you can do your normal model functions
+
+```lisp
+(wolfsheep::setup)
+
+(wolfsheep::go)
+```
+
+## Wolf sheep as a common lisp file after generation
+
+The following is a hand edited modification of wolf sheep to remove unnecessary things, and clean it up
+
+```lisp
+
+(defpackage :wolfsheep (:use :common-lisp) (:shadow :go))
+(in-package :wolfsheep)
+
+(defvar initial-number-sheep 100.0d0)
+(defvar sheep-gain-from-food 4.0d0)
+(defvar sheep-reproduce 4.0d0)
+(defvar initial-number-wolves 50.0d0)
+(defvar wolf-gain-from-food 20.0d0)
+(defvar wolf-reproduce 5.0d0)
+(defvar grass? nil)
+(defvar grass-regrowth-time 30.0d0)
+(defvar show-energy? nil)
+(defvar grass 0.0d0)
+
+(defun setup ()
+ (clnl-nvm:clear-all)
+ (clnl-nvm:ask (clnl-nvm:patches)
+  (lambda () (setf (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :green))))
+ (when grass?
+  (clnl-nvm:ask (clnl-nvm:patches)
+   (lambda ()
+    (setf (clnl-nvm:agent-value :pcolor) (clnl-nvm:one-of (list (clnl-nvm:lookup-color :green) (clnl-nvm:lookup-color :brown))))
+    (if (equalp (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :green))
+     (setf (clnl-nvm:agent-value :countdown) grass-regrowth-time)
+     (setf (clnl-nvm:agent-value :countdown) (clnl-nvm:random grass-regrowth-time))))))
+ (clnl-nvm:set-default-shape :sheep "sheep")
+ (clnl-nvm:create-turtles initial-number-sheep :sheep
+  (lambda ()
+   (setf (clnl-nvm:agent-value :color) (clnl-nvm:lookup-color :white))
+   (setf (clnl-nvm:agent-value :size) 1.5d0)
+   (setf (clnl-nvm:agent-value :label-color) (- (clnl-nvm:lookup-color :blue) 2.0d0))
+   (setf (clnl-nvm:agent-value :energy) (clnl-nvm:random (* 2.0d0 sheep-gain-from-food)))
+                            (clnl-nvm:setxy
+                             (clnl-nvm:random-xcor)
+                             (clnl-nvm:random-ycor))))
+ (clnl-nvm:set-default-shape :wolves "wolf")
+ (clnl-nvm:create-turtles initial-number-wolves :wolves
+  (lambda ()
+   (setf (clnl-nvm:agent-value :color) (clnl-nvm:lookup-color :black))
+   (setf (clnl-nvm:agent-value :size) 2.0d0)
+   (setf (clnl-nvm:agent-value :energy) (clnl-nvm:random (* 2.0d0 wolf-gain-from-food)))
+   (clnl-nvm:setxy (clnl-nvm:random-xcor) (clnl-nvm:random-ycor))))
+ (display-labels)
+ (setf grass (clnl-nvm:count (clnl-nvm:with (clnl-nvm:patches) (lambda () (equalp (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :green))))))
+ (clnl-nvm:reset-ticks)
+ :undefined)
+
+(defun go ()
+ (clnl-nvm:with-stop-handler
+  (when (not (> (clnl-nvm:count (clnl-nvm:turtles)) 0)) (clnl-nvm:stop))
+  (clnl-nvm:ask :sheep
+   (lambda ()
+    (move)
+    (when grass?  (setf (clnl-nvm:agent-value :energy) (- (clnl-nvm:agent-value :energy) 1.0d0)) (eat-grass))
+    (death)
+    (reproduce-sheep)))
+  (clnl-nvm:ask :wolves
+   (lambda ()
+    (move)
+    (setf (clnl-nvm:agent-value :energy) (- (clnl-nvm:agent-value :energy) 1.0d0))
+    (catch-sheep)
+    (death)
+    (reproduce-wolves)))
+  (when grass?  (clnl-nvm:ask (clnl-nvm:patches) (lambda () (grow-grass))))
+  (setf grass (clnl-nvm:count (clnl-nvm:with (clnl-nvm:patches) (lambda () (equalp (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :green))))))
+  (clnl-nvm:tick) (display-labels)
+  :undefined))
+
+(defun move ()
+ (clnl-nvm:turn-right (clnl-nvm:random 50.0d0))
+ (clnl-nvm:turn-left (clnl-nvm:random 50.0d0))
+ (clnl-nvm:forward 1.0d0)
+ :undefined)
+
+(defun eat-grass ()
+ (when (equalp (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :green))
+  (setf (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :brown))
+  (setf (clnl-nvm:agent-value :energy) (+ (clnl-nvm:agent-value :energy) sheep-gain-from-food)))
+ :undefined)
+
+(defun reproduce-sheep ()
+ (when (< (clnl-nvm:random-float 100.0d0) sheep-reproduce)
+  (setf (clnl-nvm:agent-value :energy) (/ (clnl-nvm:agent-value :energy) 2.0d0))
+  (clnl-nvm:hatch 1.0d0
+   (lambda ()
+    (clnl-nvm:turn-right (clnl-nvm:random-float 360.0d0))
+    (clnl-nvm:forward 1.0d0))))
+ :undefined)
+
+(defun reproduce-wolves ()
+ (when (< (clnl-nvm:random-float 100.0d0) wolf-reproduce)
+  (setf (clnl-nvm:agent-value :energy) (/ (clnl-nvm:agent-value :energy) 2.0d0))
+  (clnl-nvm:hatch 1.0d0
+   (lambda ()
+    (clnl-nvm:turn-right (clnl-nvm:random-float 360.0d0))
+    (clnl-nvm:forward 1.0d0))))
+ :undefined)
+
+(defun catch-sheep ()
+ (let*
+  ((prey (clnl-nvm:one-of (clnl-nvm:turtles-here :sheep))))
+  (when (not (equalp prey :nobody))
+   (clnl-nvm:ask prey (lambda () (clnl-nvm:die)))
+   (setf (clnl-nvm:agent-value :energy) (+ (clnl-nvm:agent-value :energy) wolf-gain-from-food))))
+ :undefined)
+
+(defun death ()
+ (when (< (clnl-nvm:agent-value :energy) 0.0d0) (clnl-nvm:die))
+ :undefined)
+
+(defun grow-grass ()
+ (when (equalp (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :brown))
+  (if (<= (clnl-nvm:agent-value :countdown) 0.0d0)
+   (progn
+    (setf (clnl-nvm:agent-value :pcolor) (clnl-nvm:lookup-color :green))
+    (setf (clnl-nvm:agent-value :countdown) grass-regrowth-time))
+   (setf (clnl-nvm:agent-value :countdown) (- (clnl-nvm:agent-value :countdown) 1.0d0))))
+ :undefined)
+
+(defun display-labels ()
+ (clnl-nvm:ask (clnl-nvm:turtles) (lambda () (setf (clnl-nvm:agent-value :label) "")))
+ (when show-energy?
+  (clnl-nvm:ask :wolves
+   (lambda () (setf (clnl-nvm:agent-value :label) (ffloor (+ (clnl-nvm:agent-value :energy) 0.5d0)))))
+  (when grass?
+   (clnl-nvm:ask :sheep
+    (lambda () (setf (clnl-nvm:agent-value :label) (ffloor (+ (clnl-nvm:agent-value :energy) 0.5d0)))))))
+ :undefined)
+
+(defun boot ()
+ (clnl-random:set-seed 15)
+ (clnl-nvm:create-world :dims
+                        '(:xmin -25 :xmax 25 :ymin -25 :ymax 25 :patch-size 9.0d0)
+                        :globals
+                        (list
+                         (list :initial-number-sheep (lambda () initial-number-sheep))
+                         (list :sheep-gain-from-food (lambda () sheep-gain-from-food))
+                         (list :sheep-reproduce (lambda () sheep-reproduce))
+                         (list :initial-number-wolves (lambda () initial-number-wolves))
+                         (list :wolf-gain-from-food (lambda () wolf-gain-from-food))
+                         (list :wolf-reproduce (lambda () wolf-reproduce))
+                         (list :grass? (lambda () grass?))
+                         (list :grass-regrowth-time (lambda () grass-regrowth-time))
+                         (list :show-energy? (lambda () show-energy?))
+                         (list :grass (lambda () grass)))
+                        :turtles-own-vars '(:energy) :patches-own-vars '(:countdown) :breeds '(:sheep :wolves))
+ (clnl-interface:initialize :dims '(:xmin -25 :xmax 25 :ymin -25 :ymax 25 :patch-size 9.0d0)))
+```