( x . y )
email: frank@consxy.com | resume (pdf)

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 (link)

This code creates the wolf sheep common lisp file:

(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:

(wolfsheep::setup)

(wolfsheep::go)

You can then mix common lisp forms and functions from your model

(loop :repeat 50 :do (wolfsheep::go))

Then you can start doing headless analysis on your model by using clnl-nvm primitives

(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 (link)

If, however, you'd rather run with the interface as well, you would use

(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

(sb-thread:make-thread #'clnl-interface:run)

Then you can do your normal model functions

(wolfsheep::setup)

(wolfsheep::go)

Wolf sheep as a common lisp file after generation (link)

The following is a hand edited modification of wolf sheep to remove unnecessary things, and clean it up


(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)))