![]() | ![]() | ![]() | Making Connections | ![]() |
We finally did some walking in the last exercise and learned how to display
the location
tutorial-example.lisp
(in-package :gbbopen-user) (define-unit-class location () (time x y) (:dimensional-values (time :point time) (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 :time 0 :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) ;;; ==================================================================== ;;; 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))) (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)))))))) (define-ks random-walk-ks :trigger-events ((instance-created-event location)) :rating 100 :execution-function 'random-walk-ks-function)
:agenda-shell-user
In the last exercise, we used location
location
A link is a bidirectional relationship between two unit instances that is implemented by two pointers. From the perspective of a particular unit instance, each link consists of an outgoing, or direct, pointer to another unit instance and an incoming, or inverse, pointer that is stored in unit instance pointed to by the direct pointer. GBBopen automatically maintains the bidirectional-link consistency of these pointers when creating new links, deleting existing links, or deleting unit instances. Links remove the possibility of “one-sided” relationships or “dangling” pointers to deleted unit instances.
Edit the location
tutorial-example.lisp
next-location
previous-location
location
(define-unit-class location () (time x y (next-location :link (location previous-location :singular t) :singular t) (previous-location :link (location next-location :singular t) :singular t)) (:dimensional-values (time :point time) (x :point x) (y :point y)) (:initial-space-instances (known-world)))
Each link-slot specification is a list whose first element is the name of the
link slot. This is followed by the link slot option :link
and a
concise specification of the inverse link slot associated with that link slot.
In this case, the next-location
/previous-location
location
Links can be many-to-many, many-to-one, one-to-many, or one-to-one. In this
case, the next-location
/previous-location
:singular t
:singular t
location
... (next-locations :link (location previous-location :singular t))) (previous-location :link (location next-locations) :singular t) ...
We've followed the natural GBBopen convention of giving singular link slots a
singular name (such as previous-location
next-locations
:singular
option is
associated with the previous-location
previous-location
previous-location
next-locations
The concise inverse-link-slot specification supplied by the :link
slot
option provides a “double entry” redundancy that is useful when links are
between instances of different unit classes, as the link can be understood by
viewing either class definition. The redundancy also helps GBBopen recognize
inconsistencies in link specifications. The function
tutorial-example.lisp
next-location
previous-location
gbbopen-user> (check-link-definitions) ;; All link definitions are consistent. t gbbopen-user>GBBopen reports that all link definitions are consistent.
Suppose that we had forgotten to add the previous-location
location
location
tutorial-example.lisp
#+ignore
previous-location
(define-unit-class location () (time x y (next-location :link (location previous-location :singular t) :singular t) #+ignore (previous-location :link (location next-location :singular t) :singular t)) (:dimensional-values (time :point time) (x :point x) (y :point y)) (:initial-space-instances (known-world)))
The #+ignore
ignore
is not an element of the feature list
*features*
ignore
is never added to
*features*
#+ignore
is a handy mechanism for
temporarily “commenting out” a single form.
Compile the now-defective definition (using C-c C-c
C-c C-x
gbbopen-user> (check-link-definitions) Warning: The inverse of link slot next-location in unit class location refers to link slot previous-location which is not present in unit class location. nil gbbopen-user>As expected, GBBopen alerts us to the problem.
Remove the #+ignore
:singular t
next-location
(define-unit-class location () (time x y (next-location :link (location previous-location) ; :singular t) :singular t) #+ignore (previous-location :link (location next-location :singular t) :singular t)) (:dimensional-values (time :point time) (x :point x) (y :point y)) (:initial-space-instances (known-world)))Compile the again-defective definition (using
C-c C-c
C-c C-x
gbbopen-user> (check-link-definitions) Warning: Link slot next-location in unit class location incorrectly declares its inverse link slot previous-location in unit class location as not singular. nil gbbopen-user>Once again, GBBopen has alerted us to the problem.
Restore the :singular t
next-location
(define-unit-class location () (time x y (next-location :link (location previous-location) ; :singular t) :singular t) (previous-location :link (location next-location :singular t) :singular t)) (:dimensional-values (time :point time) (x :point x) (y :point y)) (:initial-space-instances (known-world)))Then recompile and recheck link consistency:
gbbopen-user> (check-link-definitions) ;; All link definitions are consistent. t gbbopen-user>
Let's use our newly defined
next-location
/previous-location
location
random-walk-ks-function
tutorial-example.lisp
:previous-location
(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 :previous-location trigger-instance)) ;; Otherwise, tell the user that we've walked too far away: (t (format t "~2&Walked off the world: (~d, ~d).~%" x y))))))))
Compile the random-walk-ks-function
C-c C-c
C-c C-x
gbbopen-user> (start-control-shell) ;; Control shell 1 started Walked off the world: (55, 35). ;; No executable KSAs remain, exiting control shell ;; Control shell 1 exited: 66 cycles completed ;; Run time: 0.01 seconds ;; Elapsed time: 0 seconds :quiescence gbbopen-user>
Let's describe a couple of location
location
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: next-location: #<location 2 (-10 10)> previous-location: nil gbbopen-user>Note that the
next-location
location
gbbopen-user> (describe-instance (find-instance-by-name 2 'location)) Location #<location 2 (-10 10)> Instance name: 2 Space instances: ((known-world)) Dimensional values: time: 1 x: -10 y: 10 Non-link slots: time: 1 x: -10 y: 10 Link slots: next-location: #<location 3 (-6 19)> previous-location: #<location 1 (0 0)> gbbopen-user>The
next-location
location
2
points to the third location
previous-location
location
We can now follow the links to display the random walk:
gbbopen-user> (loop with location = (find-instance-by-name 1 'location)
do (print location)
while (setf location (next-location-of location)))
#<location 1 (0 0)>
#<location 2 (-10 10)>
#<location 3 (-6 19)>
#<location 4 (0 14)>
#<location 5 (-1 14)>
#<location 6 (8 10)>
#<location 7 (17 3)>
#<location 8 (7 -6)>
#<location 9 (10 4)>
#<location 10 (5 -5)>
...
#<location 60 (29 17)>
#<location 61 (31 21)>
#<location 62 (40 23)>
#<location 63 (45 28)>
nil
gbbopen-user>
Let's add a new KS, print-walk-ks
tutorial-example.lisp
;;; ==================================================================== ;;; Print-walk KS (defun print-walk-ks-function (ksa) ;;; Starting with the initial location instance, print the instance ;;; name and location of the walk (declare (ignore ksa)) (format t "~2&The random walk:~%") (let ((instance (find-instance-by-name 1 'location))) (while instance (format t "~s (~s ~s)~%" (instance-name-of instance) (x-of instance) (y-of instance)) (setf instance (next-location-of instance)))) ;; Tell the Agenda Shell to exit: ':stop) (define-ks print-walk-ks :trigger-events ((quiescence-event)) :rating 100 :execution-function 'print-walk-ks-function)The
print-walk-ks
quiescence-event
print-walk-ks
random-walk-ks
location
The print-walk-ks-function
next-location
/previous-location
:stop
. The
Agenda Shell checks the value returned by a KS execution function for this
special indicator and, if it is returned, the control shell is exited. If we
did not return :stop
, the print-walk-ks
quiescence-event
quiescence-event
Let's compile our latest changes and then run our application with the new
print-walk-ks
gbbopen-user> (start-control-shell)
;; Control shell 1 started
Walked off the world: (54, 15).
The random walk:
1 (0 0)
2 (-6 9)
3 (-14 8)
4 (-5 6)
5 (-13 5)
6 (-11 13)
7 (-11 4)
8 (-17 8)
9 (-21 15)
10 (-12 14)
...
35 (40 28)
36 (50 22)
37 (49 12)
38 (47 10)
;; Explicit :stop issued by KS print-walk-ks
;; Control shell 1 exited: 41 cycles completed
;; Run time: 0.01 seconds
;; Elapsed time: 0 seconds
:stop
gbbopen-user>
The GBBopen Project
![]() | ![]() | ![]() | Making Connections | ![]() |