;;;  -*- mode: LISP; Package: CL-USER; Syntax: COMMON-LISP;  Base: 10 -*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 
;;; Author      : Mike Byrne
;;; Copyright   : (c)1998-2000 Rice U./CMU/Mike Byrne, All Rights Reserved
;;; Availability: public domain
;;; Address     : Rice University
;;;             : Psychology Department
;;;             : Houston, TX 77251-1892
;;;             : byrne@acm.org
;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 
;;; Filename    : acl-test-suite.actr
;;; Version     : 2.0b1
;;; 
;;; Description : This is the ACT-R source file containing the productions which
;;;             : will drive the test-suite example for use with ACL.  The task is 
;;;             : a fairly complex one with multiple steps:
;;;             : [1] A "Start" button appears on the screen.  ACT-R/PM finds
;;;             :     that button and clicks on it.
;;;             : [2] After a short random interval, a tone is generated
;;;             :     (this will be heard as a regular beep), and ACT-R/PM
;;;             :     responds "high" if it is a high tone and "low" if it
;;;             :     is a low tone.
;;;             : [3] A random letter will appear at a random location on the
;;;             :     screen.  ACT-R/PM will respond by typing that letter.
;;;             : [4] Either the word "up" or the word "down" will appear at
;;;             :     a random location on the screen.  If the word is "up,"
;;;             :     ACT-R/PM will respond by pressing the "u" key.  If it is
;;;             :     "down," the "m" key is pressed.
;;;             : [5] The phrase "key left" or "key right" will appear on the
;;;             :     screen.  Again, a key response of "s" or "l" is 
;;;             :     generated.
;;;             : [6] Two objects appear on the screen, a star and a circle.
;;;             :     These objects will move.  ACT-R/PM will start to track
;;;             :     the one on the left, and when it reaches the top third
;;;             :     of the window, tracking ends with a mouse click.
;;;             : Note also that there are two right-hand moves here, from
;;;             : the mouse to the keyboard and back.
;;;             : 
;;;             : ACT-R/PM does this task very quickly--this is intended to
;;;             : be a demonstration more than a model, though I suspect
;;;             : that an expert on this task would be about as fast.
;;;             : 
;;;             : To run the simulation, first load the "acl-test-suite.lisp"
;;;             : file.  Then LOAD (don't just evaluate) this file.  Call
;;;             : the (startup) function and then call (sim) or (pm-step).
;;; 
;;; To do       : No present plans.
;;; 
;;; ----- History -----
;;; 98.06.05 mdb
;;;             : Documentation genesis.
;;; 98.08.25 mdb
;;;             : Added some new tests to the CLICK production (it now waits
;;;             : until it can verify the cursor and button are in the same
;;;             : location) and made the task states chunks instead of strings.
;;; 99.04.08 mdb
;;;             : Made some small changes for beta 6 compatibility.
;;; 00.07.17 mdb
;;;             : Some changes for 2.0/EMMA compatibility.
;;; 
;;; 00.09.05 Dan Bothell
;;;             : Some general cleaning up. 
;;;             : Added the :text keyword to the speak commands 
;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;;; ---------------------------------------------------------------------- ;;;;
;;;; Lisp functions

;;; STARTUP      [Function]
;;; Description : Several things have to be done to run an ACT-R/PM model.
;;;             : First, the ACT-R part of the model is reloaded.
;;;             : Then RPM is reset and the window is created and installed.
;;;             : RPM global parameters are set.  Note the setting of the 
;;;             : SPEECH-HOOK-FCT, which is necessary for the environment to
;;;             : "hear" ACT-R/PM's speech.  Note also the setting of 
;;;             : VISUAL-MOVEMENT-TOLERANCE--this should be set to some value
;;;             : greater than 0 if objects are going to be moving around.
;;;             : Also, gotta start the hands at the mouse and process the
;;;             : screen.

  
(defun startup ()
  "Setup the test-suite simulation."
  (setf *experiment* (make-window 'test :device 'test-window))
  (reload)
  (pm-install-window *experiment*)
  (pm-proc-screen :with-cursor t)
  (window-select (devel::debug-window *system*)))

(defun sim (&optional (time 25.))
  "Run the test-suite simulation."
  (window-select *experiment*)
  (pm-run time)
  (window-select (devel::debug-window *system*)))


;;;; ---------------------------------------------------------------------- ;;;;
;;;; Chunk definitions and set-up

(clear-all)
(pm-reset)
(sgp :era t)

(chunk-type test-suite state)
(chunk-type task-state)
(chunk-type (star (:include visual-object)))
;; don't need an OVAL chunk-type because that's already in RPM

(add-dm 
 (start isa task-state) (sound isa task-state)
 (key1 isa task-state) (key2 isa task-state)
 (key3 isa task-state) (find-track isa task-state)
 (tracking isa task-state) (done isa task-state)
 (goal1 isa test-suite state start)
 (cursor isa chunk) (star isa chunk)
 )
(goal-focus goal1)

(pm-set-params :randomize-time t
               :output-speech t
               :visual-movement-tolerance 2.0
               :conservative-update-visual nil
               :process-cursor t
               :optimize-visual t
)
(pm-set-params :trace-modules t)
(pm-start-hand-at-mouse)
(pm-attend-location 10 10)
(pm-prepare-motor punch :hand right :finger index)
(pm-set-params :show-focus t)


;;;; ---------------------------------------------------------------------- ;;;;
;;;; Productions and production parameters

;;; START
;;; Description : First thing to do is find the button that's on the screen,
;;;             : and move the mouse over to it.

(p start
   =goal>
     isa test-suite
     state start
   =loc>
     isa visual-location
     time now
     attended nil
     value oval
   =state>
     isa module-state
     module :motor
     modality free
   =state2>
     isa module-state
     module :vision
     modality free
==>
   !send-command! :vision move-attention :location =loc
   !send-command! :motor move-cursor :loc =loc)


;;; CLICK
;;; Description : One the mouse move is complete (which is the MODULE-STATE
;;;             : test), then we can click the mouse.  Then we can start
;;;             : waiting for the sound.  Run a CLEAR on Vision as well, 
;;;             : since we no longer care about attending the current location.

(p click
   =goal>
     isa test-suite
     state start
   =btn>
      isa VISUAL-OBJECT
      value OVAL
      time now
      screen-pos =loc
   =loc>
      isa VISUAL-LOCATION
      kind CURSOR
      time now
   =state>
     isa module-state
     module :motor
     modality free
==>
   !send-command! :motor click-mouse
   !send-command! :vision clear
   =goal>
     state sound)


;;; HOME-HANDS
;;; Description : While we're waiting for the sound, move the right hand 
;;;             : to the keyboard, since we'll need that later.  This is
;;;             : a good time to do this since it won't interfere with
;;;             : hearing or speech production.

(p home-hands
   =goal>
     isa test-suite
     state sound
   =state>
     isa module-state
     module :motor
     last-command click-mouse
     modality free
==>
   !send-command! :motor hand-to-home)


;;; DETECTED-SOUND
;;; Description : When we've detected that there is a sound, attend it
;;;             : so we can tell what it is.

(p detected-sound
   =goal>
     isa test-suite
     state sound
   =event>
     isa audio-event
     time now
     attended nil
==>
   !send-command! :audio attend-sound :event =event)



;;; SOUND-RESPOND-LOW
;;; Description : When the sound we have is a low tone, respond by
;;;             : speaking "low" and note the change in task state.

(p sound-respond-low
   =goal>
     isa test-suite
     state sound
   =sound>
     isa sound
     time now
     content 800
==>
   !send-command! :speech speak :text "low"
   =goal>
     state key1)


;;; SOUND-RESPOND-HIGH
;;; Description : Same as the previous but for a high tone.

(p sound-respond-high
   =goal>
     isa test-suite
     state sound
   =sound>
     isa sound
     time now
     content 2000
==>
   !send-command! :speech speak :text "high"
   =goal>
     state key1)



;;; LOOK-FOR-LETTER
;;; Description : When the letter appears, this production will fire to
;;;             : have the Vision Module go and attend it.

(p look-for-letter
   =goal>
     isa test-suite
     state key1
   =loc>
     isa visual-location
     time now
     attended nil
     kind text
   =state>
     isa module-state
     module :vision
     modality free
==>
   !send-command! :vision move-attention :location =loc :scale letter)


;;; DO-KEY1
;;; Description : Once we've seen the letter, press the key for that 
;;;             : letter and note the change in state.

(p do-key1
   =goal>
     isa test-suite
     state key1
   =obj>
     isa visual-object
     time now
     value =key
   =state>
     isa module-state
     module :motor
     modality free
==>
   !send-command! :motor press-key =key
   =goal>
     state key2)



;;; LOOK-FOR-WORD
;;; Description : This will fire when the word appears, and sends Vision
;;;             : off to attend that word.

(p look-for-word
   =goal>
     isa test-suite
     state key2
   =loc>
     isa visual-location
     time now
     attended nil
     kind text
   =state>
     isa module-state
     module :vision
     modality free
==>
   !send-command! :vision move-attention :location =loc :scale word)


;;; DO-KEY2-UP and DO-KEY2-DOWN
;;; Description : When the word has been attended, press the appropriate
;;;             : key and note the change in task state.

(p do-key2-up
   =goal>
     isa test-suite
     state key2
   =obj>
     isa visual-object
     time now
     value "up"
   =state>
     isa module-state
     module :motor
     preparation free
==>
   !send-command! :motor press-key "u"
   =goal>
     state key3)


(p do-key2-down
   =goal>
     isa test-suite
     state key2
   =obj>
     isa visual-object
     time now
     value "down"
   =state>
     isa module-state
     module :motor
     preparation free
==>
   !send-command! :motor press-key "m"
   =goal>
     state key3)


;;; LOOK-FOR-PHRASE
;;; Description : Pretty much the same as LOOK-FOR-WORD.

(p look-for-phrase
   =goal>
     isa test-suite
     state key3
   =loc>
     isa visual-location
     time now
     attended nil
     kind text
   =state>
     isa module-state
     module :vision
     modality free
==>
   !send-command! :vision move-attention :location =loc :scale phrase)


;;; DO-KEY3-LEFT and DO-KEY3-RIGHT
;;; Description : Again, about the same as the DO-KEY2 productions, though
;;;             : we want to run a CLEAR here so that there is no current
;;;             : visual object and the tracking productions won't start
;;;             : firing.

(p do-key3-left
   =goal>
     isa test-suite
     state key3
   =obj>
     isa phrase!
     time now
     value "key left"
   =state>
     isa module-state
     module :motor
     preparation free
==>
   !send-command! :motor press-key "s"
   !send-command! :vision clear
   =goal>
     state find-track)


(p do-key3-right
   =goal>
     isa test-suite
     state key3
   =obj>
     isa phrase!
     time now
     value "key right"
   =state>
     isa module-state
     module :motor
     preparation free
==>
   !send-command! :motor press-key "l"
   !send-command! :vision clear
   =goal>
     state find-track)


;;; FIND-TRACK-STAR and FIND-TRACK-OVAL
;;; Description : OK, first thing to do when you want to track an object is
;;;             : to find it and attend it (you can't track an object that 
;;;             : isn't the current focus of attention).  
;;;             : There are two productions for a reason:  there is a third
;;;             : object on the screen, the cursor, and you don't want to
;;;             : start trying to track the cursor.

(p find-track-star
   =goal>
     isa test-suite
     state find-track
   !bind! =x 150
   =loc>
     isa visual-location
     time now
     screen-x (less-than =x)
     value star
     attended nil
   =state>
     isa module-state
     module :vision
     modality free
==>
   !send-command! :vision move-attention :location =loc)


(p find-track-oval
   =goal>
     isa test-suite
     state find-track
   =loc>
     isa visual-location
     time now
     screen-x (less-than 150)
     value oval
     attended nil
   =state>
     isa module-state
     module :vision
     modality free
==>
   !send-command! :vision move-attention :location =loc)


;;; BEGIN-TRACKING
;;; Description : Once we have an object in the focus of visual attention,
;;;             : start tracking it and note that we're tracking.

(p begin-tracking
   =goal>
     isa test-suite
     state find-track
   =obj>
     isa visual-object
     time now
     value =val
   =state>
     isa module-state
     module :vision
     execution free
==>
   !send-command! :vision start-tracking =obj
   !output! ("ее Tracking the ~S" =val)
   =goal>
     state tracking)


;;; RETURN-HAND-TO-MOUSE
;;; Description : When the tracked object is responded to, it's with a click.
;;;             : So move the hands back to the mouse.  Note the test of the
;;;             : previous motor command--without it, this production will get
;;;             : called over and over.

(p return-hand-to-mouse
   =goal>
     isa test-suite
     state tracking
   =vision>
     isa module-state
     module :vision
     execution busy
   =motor>
     isa module-state
     module :motor
     modality free
     last-command press-key
==>
   !send-command! :motor hand-to-mouse)




;;; RESPOND-TO-TRACK
;;; Description : The last action, click in response to the position of the
;;;             : tracked object.  (This is an illustration of how you get
;;;             : the current position of a tracked object.)

(p respond-to-track
   =goal>
     isa test-suite
     state tracking
   =vision>
     isa module-state
     module :vision
     execution busy
   =obj>
     isa visual-object
     time now
     screen-pos =loc
   =loc>
     isa visual-location
     screen-y =y
   =motor>
     isa module-state
     module :motor
     modality free
   !eval! (< =y 100)
==>
   !send-command! :motor click-mouse
   !send-command! :vision clear
   =goal>
     state done)


;;; FINISHED
;;; Description : When everything is done, pop the top goal.  Note that this
;;;             : production checks the state of the Motor Module.  Why? 
;;;             : If not, this production will fire immediately after the 
;;;             : last command is issued, not after it is completed.

(p finished
   =goal>
     isa test-suite
     state done
   =state>
     isa module-state
     module :motor
     modality free
==>
   !pop!)