![]() | ![]() | ![]() | Add Another KS | ![]() |
The last exercise made it easy to initialize and run our application
repeatedly by simply starting the Agenda Shell. We also specified the
dimensionality of our known-world
location
location
tutorial-example.lisp
(in-package :gbbopen-user) (define-unit-class location () (x y) (:dimensional-values (x :point x) (y :point y)) (:initial-space-instances (known-world))) (defmethod print-instance-slots ((location location) stream) (call-next-method) (when (and (slot-boundp location 'x) (slot-boundp location 'y)) (format stream " (~s ~s)" (x-of location) (y-of location)))) ;;; ==================================================================== ;;; Startup KS (defun startup-ks-function (ksa) (declare (ignore ksa)) ;; Create an initial location unit instance at (0,0): (make-instance 'location :x 0 :y 0)) (define-ks startup-ks :trigger-events ((control-shell-started-event)) :execution-function 'startup-ks-function) ;;; ==================================================================== ;;; Initializations (run at Agenda Shell startup) (defun initializations (event-name &key &allow-other-keys) (declare (ignore event-name)) ;; Clean up any previous run: (delete-blackboard-repository) ;; Make a new known-world space instance: (make-space-instance '(known-world) :dimensions (dimensions-of 'location))) (add-event-function 'initializations 'control-shell-started-event ;; Initializations should be done first! :priority 100)
:agenda-shell-user
It's time we introduce the notion of time to our application. Edit the
location
tutorial-example.lisp
time
, to the
location
time
dimensional value:
(define-unit-class location () (time x y) (:dimensional-values (time :point time) (x :point x) (y :point y)) (:initial-space-instances (known-world)))
Recall that we specified that the dimensions of the known-world
initializations
location
(make-space-instance '(known-world) :dimensions (dimensions-of 'location)))Therefore, we don't need to modify our call to
make-space-instance
time
as a dimension
of known-world
Next, modify startup-ks-function
tutorial-example.lisp
location
(defun startup-ks-function (ksa) (declare (ignore ksa)) ;; Create an initial location unit instance at (0,0) at time 0: (make-instance 'location :time 0 :x 0 :y 0))
Let's verify our work. Compile and load the
tutorial-example.lisp
C-c C-k
C-c C-b
gbbopen-user> (start-control-shell) ;; Control shell 1 started ;; No executable KSAs remain, exiting control shell ;; Control shell 1 exited: 3 cycles completed ;; Run time: 0 seconds ;; Elapsed time: 0 seconds :quiescence gbbopen-user>
Check that the initial location
time
zero:
gbbopen-user> (describe-instance (find-instance-by-name 1 'location)) Location #<location 1 (0 0)> Instance name: 1 Space instances: ((known-world)) Dimensional values: time: 0 x: 40 y: 60 Non-link slots: time: 0 x: 40 y : 60 Link slots: None gbbopen-user>
Define a KS called random-walk-ks
location
random-walk-ks-function
location
random-walk-ks
location
Begin implementing the random-walk-ks
tutorial-example.lisp
;;; ==================================================================== ;;; Random-walk KS (defun add-linear-variance (value max-variance) ;;; Returns a new random value in the interval ;;; [(- value max-variance), (+ value max-variance)] (+ value (- (random (1+ (* max-variance 2))) max-variance)))Then compile the definition (using
C-c C-c
C-c C-x
gbbopen-user> (dotimes (i 15) (printv (add-linear-variance 0 10))) ;; (add-linear-variance 0 10) => 8 ;; (add-linear-variance 0 10) => 9 ;; (add-linear-variance 0 10) => 4 ;; (add-linear-variance 0 10) => 3 ;; (add-linear-variance 0 10) => -4 ;; (add-linear-variance 0 10) => -10 ;; (add-linear-variance 0 10) => -1 ;; (add-linear-variance 0 10) => 0 ;; (add-linear-variance 0 10) => 4 ;; (add-linear-variance 0 10) => 5 ;; (add-linear-variance 0 10) => 8 ;; (add-linear-variance 0 10) => -5 ;; (add-linear-variance 0 10) => -3 ;; (add-linear-variance 0 10) => 7 ;; (add-linear-variance 0 10) => 6 nil gbbopen-user>
Because add-linear-variance
gbbopen-user> (printv "Some multiple values" (values 1 2) "Some more" (values 3 4 5)) ;; Some multiple values ;; (values 1 2) => 1; 2 ;; Some more ;; (values 3 4 5) => 3; 4; 5 4 5 6 gbbopen-user>
random-walk-ks
execution function
Next add the following KS-execution function to the end of your
tutorial-example.lisp
(defun random-walk-ks-function (ksa) ;;; Move to the next (random) location in the world (let* ((trigger-instance (sole-trigger-instance-of ksa)) ;; The new time is one greater than the stimulus's time: (time (1+ (time-of trigger-instance)))) (cond ;; If the maximum time value (75) is reached, tell the user we've ;; walked too long: ((>= time 75) (format t "~2&Walked too long.~%")) (t ;; The new location is +/- 10 of the stimulus's location: (let ((x (add-linear-variance (x-of trigger-instance) 10)) (y (add-linear-variance (y-of trigger-instance) 10))) (cond ;; Check that the new location is within the known-world ;; boundaries. If so, create the new location instance: ((and (<= -50 x 50) (<= -50 y 50)) (make-instance 'location :time time :x x :y y)) ;; Otherwise, tell the user that we've walked too far away: (t (format t "~2&Walked off the world: (~d, ~d).~%" x y))))))))
Unlike the KS-execution functions that we have defined previously,
random-walk-ks-function
ksa
argument.
Instead, it calls ksa
unit-instance argument in order to obtain the location
random-walk-ks
definition
Finally, add this define-ks
tutorial-example.lisp
random-walk-ks
(define-ks random-walk-ks :trigger-events ((instance-created-event location)) :rating 100 :execution-function 'random-walk-ks-function)
Compile and load the random-walk-ks
gbbopen-user> (start-control-shell) ;; Control shell 1 started Walked off the world: (23, 55). ;; No executable KSAs remain, exiting control shell ;; Control shell 1 exited: 64 cycles completed ;; Run time: 0.01 seconds ;; Elapsed time: 0 seconds :quiescence gbbopen-user>
It looks like something happened! (Again, because
add-linear-variance
location
gbbopen-user> :dsbb Space Instance Contents -------------- -------- known-world 61 instances (61 location) Unit Class Instances ---------- --------- control-shell 1 * ks 1 + ksa-queue 2 + location 61 ordered-ksa-queue 1 + standard-space-instance 1 --------- 67 instances gbbopen-user>
The 61 location
location
initial-ks
location
random-walk-ks
location
It would be interesting to see where our random walk has taken us. We could
use GBBopen's location
gbbopen-user> (map-instances-of-class #'print 'location)
#<location 58 (5 31)>
#<location 13 (-7 10)>
#<location 26 (-40 35)>
#<location 39 (-4 3)>
#<location 52 (2 23)>
#<location 7 (3 17)>
#<location 20 (2 27)>
#<location 33 (-25 6)>
#<location 46 (-2 32)>
...
#<location 31 (-22 18)>
#<location 44 (-7 14)>
#<location 57 (2 41)>
#<location 12 (-15 15)>
#<location 25 (-32 38)>
#<location 38 (-10 -4)>
#<location 51 (-2 16)>
#<location 6 (10 27)>
#<location 19 (-1 17)>
#<location 32 (-25 12)>
#<location 45 (-7 23)>
nil
gbbopen-user>
Unfortunately, the order that unit instances are supplied to the print
function is not controllable. Our walk would be much clearer if we printed
the location
We might consider taking advantage of the instance names that GBBopen assigns to unit instances. We could do something like the following:
gbbopen-user> (dotimes (i 76)
(let ((location (find-instance-by-name i 'location)))
(when location
(print location))))
#<location 1 (0 0)>
#<location 2 (10 4)>
#<location 3 (19 10)>
#<location 4 (14 9)>
#<location 5 (14 18)>
#<location 6 (10 27)>
#<location 7 (3 17)>
#<location 8 (-6 20)>
#<location 9 (4 15)>
#<location 10 (-5 14)>
...
#<location 50 (5 26)>
#<location 51 (-2 16)>
#<location 52 (2 23)>
#<location 53 (9 33)>
#<location 54 (7 43)>
#<location 55 (-2 36)>
#<location 56 (0 46)>
#<location 57 (2 41)>
#<location 58 (5 31)>
#<location 59 (13 39)>
#<location 60 (17 41)>
#<location 61 (21 50)>
nil
gbbopen-user>
This is a bad idea for several reasons. First, we are looking up every
location
location
location
time
value of the locations. We should find a way to
sequence location
time
values
directly.
GBBopen provides a variant of :key
accessor function
that suits our needs:
gbbopen-user> (map-sorted-instances-of-class #'print 'location #'<
:key #'time-of)
#<location 1 (0 0)>
#<location 2 (10 4)>
#<location 3 (19 10)>
#<location 4 (14 9)>
#<location 5 (14 18)>
#<location 6 (10 27)>
#<location 7 (3 17)>
#<location 8 (-6 20)>
#<location 9 (4 15)>
#<location 10 (-5 14)>
...
#<location 50 (5 26)>
#<location 51 (-2 16)>
#<location 52 (2 23)>
#<location 53 (9 33)>
#<location 54 (7 43)>
#<location 55 (-2 36)>
#<location 56 (0 46)>
#<location 57 (2 41)>
#<location 58 (5 31)>
#<location 59 (13 39)>
#<location 60 (17 41)>
#<location 61 (21 50)>
nil
gbbopen-user>
Using
If we run the application a few more times, we eventually encounter a case
where we create the allotted 75 location
known-world
gbbopen-user> (start-control-shell) ;; Control shell 1 started Walked too long. ;; No executable KSAs remain, exiting control shell ;; Control shell 1 exited: 78 cycles completed ;; Run time: 0.04 seconds ;; Elapsed time: 0 seconds :quiescence gbbopen-user>
Here is one such random walk:
gbbopen-user> (map-sorted-instances-of-class #'print 'location #'<
:key #'time-of)
#<location 1 (0 0)>
#<location 2 (2 7)>
#<location 3 (-1 5)>
#<location 4 (-1 0)>
#<location 5 (3 -2)>
#<location 6 (13 -7)>
#<location 7 (8 -5)>
#<location 8 (1 2)>
#<location 9 (8 0)>
#<location 10 (5 8)>
...
#<location 70 (-13 -11)>
#<location 71 (-13 -6)>
#<location 72 (-9 -6)>
#<location 73 (1 -4)>
#<location 74 (-8 -11)>
#<location 75 (-13 -15)>
nil
gbbopen-user>
The GBBopen Project
![]() | ![]() | ![]() | Add Another KS | ![]() |