;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; ACT-R Button Press Model
;;; works with ACT-R 4.0 (2/6/97 or newer)
;;;
;;; coded by: Dan Bothell
;;; 
;;; This file contains an ACT-R model
;;; of the Friedman, Burke, Cole, Keller, 
;;; Millward, and Estes (1964) button 
;;; choice experiment.
;;;
;;; In the experiment subjects were to 
;;; press one of two buttons on each
;;; trial, and were informed after 
;;; choosing whether it was the correct
;;; button.  The correct button to press
;;; was randomly chosen on each trial, 
;;; with a probability p for button 1
;;; and 1-p for button 2.
;;; 
;;; A command line interface, 
;;; and a WWW interface are included.
;;;
;;; To use the command line interface,
;;; set the parameters with setf, and then
;;; call (button-press-friedman n) to simulate n button
;;; presses. 
;;;
;;;
;;; To use the WWW interface, you need to run
;;; the ACT-R on the Web application (follow the
;;; instructions provided with it), or use a
;;; web browser to connect to a site that has
;;; the model installed.

;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This section contains the LISP functions to simulate
;;; the experiment, implement the interface, collect the
;;; data, and display the results
;;;
;;; The ACT-R Model starts further down
;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Global Variables 
   

(defvar *act-r-button-press* 1)   ;; set by the model to the current button pressed (0 for button 1, 1 for button 2)

;;; the rest are the parameters for the model, and are set by the interfaces

(defvar *choice-g* 1.54)
(defvar *egn* 1.64)
(defvar *v* nil)
(defvar *button-press-runs* 1)
(defvar *text*)
(defvar *graphic*)
(defvar *overlay*)

(setf *text* t)
(setf *graphic* nil)
(setf *overlay* nil)

;;; p button1 = .1 .2 .3 .4 .6 .7 .8 .9
(defparameter *friedman-data* '(0.1625 0.302 0.39 0.39 0.5165 0.6025 0.732 0.7845))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; This section contains the interface for the WWW using the
;;; ACT-R on the Web application by Elmar Schwarz

(defvar *WWW-interface*)

(setf  *WWW-interface* 
      '((:heading "Simulation of Friedman, Burke, Cole, Keller, Millward, and Estes (1964) Experiment Model" 2)
        (:table)
        
        (:table)
        "Noise variance (sigma squared): "    (:string :sy *egn*             1.64) (:new-row)
        "G: "                                 (:string :sy *choice-g*          1.54) (:new-row)
        "Presses per condition (max 1000): "  (:string :sy *button-press-runs* 500)
        (:table-end)
        
        (:table)
        
        (:checkbox "Trace"                                            :sy *v*       nil) (:new-row)
        (:checkbox "Text output"                                      :sy *text*    t)   (:new-row)
        (:checkbox "Graphic output"                                   :sy *graphic* nil) (:new-row)
        (:checkbox "Show simulation and experiment data on one graph" :sy *overlay* nil) 
        
        (:table-end)
        (:table-end)
        
        (:new-para)
        (:button "Show Experiment Results" "(display-friedman *friedman-data* nil)")
        (:new-para)
        (:button "Run model" "(if (and (numberp *choice-g*) (numberp *egn*) (numberp *button-press-runs*))
                                  (button-press-friedman (min 1000 (max *button-press-runs* 1)))
                                  (format *standard-output* \"All parameters must be numbers.~%\"))")
        (:reset "Default values")
        (:button "Production Rules" "(let ((prods (no-output (pp))))
                                       (dolist (x prods)
                                         (pp-fct (list x))
                                         (spp-fct (list x))
                                         (format *standard-output* \"~%\")))")

        (:button "Chunk types" "(chunk-type)")
        (:button "Chunks" "(dm)")
         (:new-para)
          "TIME and SIZE:"
        (:new-para)
        "- It usually takes less than 1 minute for a run of 500 presses per condition"
        (:new-line)
        "- The trace of 1 run of 500 presses per condition is approximatly 350k (250 pages) in size"))



;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; button-press-friedman takes one parameter which specifies how many button presses to 
;;; perform per condition of a simulation of the Friedman et al experiment.
;;;  First, the ACT-R parameters are set according to the values given in
;;; the global variables (as set by the interface). Then, the model is called to
;;; press a button, and the button press is recorded.
;;; After all of the button presses have been done a summary of the experiment results 
;;; is displayed.

(defun button-press-friedman (n)
  (let ((result nil)
        (b1-pressed 0))
    
    (dolist (b1-p '(.1 .2 .3 .4 .6 .7 .8 .9))
      
      (when *v* (format *standard-output* "~%~%Probability of button1= ~S :~%~%" b1-p))

      (reset)
      
      (sgp-fct (list :ol t
                     :era t
                     :er t
                     :mt nil
                     :lt nil
                     :g  *choice-g*
                     :egn *egn*
                     :v *v*))
    
      (parameters-fct 'choose-button-1 
                      (list
                       :r b1-p 
                       :q 1 
                       :a .05 
                       :b 0))
    
      (parameters-fct 'choose-button-2 
                      (list
                       :r (- 1 b1-p) 
                       :q 1 
                       :a .05 
                       :b 0))
    
      (setf b1-pressed 0)

      (dotimes (i n)
        (wmfocus goal)
        (run)
      
        (when (=  *act-r-button-press* 0 ) (incf b1-pressed)))
      (setf result (cons (/ b1-pressed n) result)))
    
    (format *standard-output* "~%~%Simulation parameters: (~S ~S ~S)~%" *egn* *choice-g* n)

    (display-friedman (reverse result) t)))


;;; display-friedman takes two parameters
;;; the results data to display and a flag to specify if the
;;; data is for a simulation
;;; and outputs a display of % choice for button 1,
;;; in either text, a graph (on the web),
;;; or both, depending on the settings of *text* and *graphic*


(defun display-friedman (data simulation)
  
  (when *text*
    (format *standard-output* "~%~%~a data:~%" (if simulation "Simulation" "Experimental"))
    (format t "~%p of Button 1        button 1 chosen~%")  
    (do ((x data (cdr x))
         (p '(.1 .2 .3 .4 .6 .7 .8 .9) (cdr p)))
        ((null x))
      (format *standard-output* "~7,1F              ~9,3F~%" (car p) (car x)))

    (format *standard-output* "~%")
    
    (when (and simulation *overlay*)
      (format *standard-output* "~%~%Experimental data:~%")
      (format t "~%p of Button 1        button 1 chosen~%")  
      (do ((x *friedman-data* (cdr x))
           (p '(.1 .2 .3 .4 .6 .7 .8 .9) (cdr p)))
          ((null x))
        (format *standard-output* "~7,1F              ~9,3F~%" (car p) (car x)))
      
      (format *standard-output* "~%"))
    
    (unless *graphic* (format *standard-output* 
                              "~%</pre>If your browser supports JAVA, you 
                               can display the data in a graph by checking 
                               the Graphic output box on the interface page.<pre>~%~%")))
  (when *graphic*
    (format *standard-output* " 
        <applet 
        code = \"DansGraphs.class\" 
        width = 500 
        height = 400> 

        <PARAM name=\"title\" value=\"Data for Friedman et al\">
        <PARAM name=\"longestline\" value=\"8\">
        <PARAM name=\"numlines\" value=\"~S\">
        <PARAM name=\"xmin\" value=\"0\">
        <PARAM name=\"xmax\" value=\"1.0\">
        <PARAM name=\"ymax\" value=\"1.0\">
        <PARAM name=\"yspacing\" value=\".2\">
        <PARAM name=\"xspacing\" value=\".1\">
        <PARAM name=\"ymin\" value=\"0\">
        <PARAM name=\"xdiv\" value=\".1\">
        <PARAM name=\"ydiv\" value=\"0.1\">
        <PARAM name=\"xval0\" value=\"0.1;0.2;0.3;0.4;0.6;0.7;0.8;0.9;\">
        <PARAM name=\"xname\" value=\"p of Button 1\">
        <PARAM name=\"lcolor0\" value=\"0\">
        <PARAM name=\"lstyle0\" value=\"~s\">
        <PARAM name=\"yname\" value=\"Button 1 chosen\">
        <PARAM name=\"name0\" value=\"~a\">" 
            (if (and simulation *overlay*) 2 1)
            (if simulation 2 6553)
            (if simulation "Simulation Data" "Experiment Data"))
    
    (format *standard-output* "<PARAM name=\"yval0\" value=\"")
    
    (dolist (x data)
      (format *standard-output* "~f;" x))
    
    (format *standard-output* "\">")
    
    (when (and *overlay* simulation)
      (format *standard-output* "
        <PARAM name=\"lcolor1\" value=\"0\">
        <PARAM name=\"lstyle1\" value=\"6553\">
        <PARAM name=\"xval1\" value=\"0.1;0.2;0.3;0.4;0.6;0.7;0.8;0.9;\">
        <PARAM name=\"yval1\" value=\"")
      (dolist (x *friedman-data*)
        (format *standard-output* "~f;" x))
      
      (format *standard-output* "\"> 
          <PARAM name=\"name1\" value=\"Experiment Data\">"))
    (format *standard-output* "
             <HR> Your browser does not support JAVA, so you cannot view the graphs.~%
             </HR></applet>")))









;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; The rest of the file contains the 
;;; actual ACT-R model to guess the button to press.
;;; There are 2 productions, one to press button 1, and
;;; one to press button 2.  When the goal is set to
;;; press a button, either one can fire.  The one which
;;; fires is based on the (noisy) PG-C values for the productions.
;;;
   
(clearall)

(sgp :g 1.54 :era t :egn 1.64 :v nil :pl nil)

(chunk-type press-button "The type for the only goal")

(add-dm (goal 
        "The goal is to press a button" 
        isa press-button))


(p choose-button-1
" 
   IF the goal is to press a button 
   THEN press button 1
      and pop the goal
"
   =goal>
      isa press-button
==>
  !output! ("~%pressed: button 1~% ")

  !eval! (setf *act-r-button-press* 0)

  !pop!
)

(parameters choose-button-1 :r .5 :q 1.0 :a .2 :b 0)


(p choose-button-2
" 
   IF the goal is to press a button
   THEN press button 2
      and pop the goal
"
   =goal>
      isa press-button
==>
   !output! ("~%pressed: button 2~%")

   !eval! (setf *act-r-button-press* 1)

   !pop!
)

(parameters choose-button-2 :r .5 :q 1.0 :a .2 :b 0)