<taskModel about="urn:cetask.wpi.edu:examples:Inquiry" xmlns="http://ce.org/cea-2018">

 <!-- 
      Mike Sao Pedro
      mikesp@wpi.edu
      
      Main task: perform all parts of the scientific method.
      
      Here, I envision perhaps noticing what window the user clicks
      (hypoth spec, experiment spec, conclusion spec) in order to do
      the decomposition. 
  -->
  <task id="PerformExperiment">
  	<input name="experiment" type="Experiment"/>
    <output name="hypothesis" type="Hypothesis"/>
    
  	<!-- 
  	   Have student perform all parts of the scientific method. 
  	   Here the student will be asked to fill in specific tests.
  	-->
  	<subtasks id="allScientificMethod">
  	
  	   <step name="formHypothesis" task="FormHypothesis"/>
  	   <step name="runExperiments" task="RunExperiments"/>
  	   <!--  
  	   <step name="formConclusions" task="FormConclusions"/>
  	   -->
  	   
  	   <!-- The experiments are geared towards the hypothesis selected -->
  	   <binding slot="$formHypothesis.simulation" value="$this.experiment.simulation"/>
  	   <binding slot="$this.hypothesis" value="$formHypothesis.hypothesis"/>
  	   <binding slot="$runExperiments.hypothesis" value="$this.hypothesis"/>
  	            
  	   <!-- 
  	   <binding slot="$formConclusions.table" 
  	            value="$runExperiments.table"/>
  	   <binding slot="$formConclusions.hypothesis"
  	            value="$formHypothesis.hypothesis"/>
  	     -->
  	</subtasks>
  </task>
  
  <!-- 
      Given a simulation specification, make a student fill in
      a hypothesis. Forming a hypothesis consists of seeing how
      and independent variable affects an outcome variable.
   -->
  <task id="FormHypothesis">
  	<input name="simulation" type="Simulation"/>
  	<output name="independentVarValue" type="IndependentVariable"/>
  	<output name="comparatorValue" type="string"/>
  	<output name="outcomeVarValue" type="OutcomeVariable"/>
  	<output name="hypothesis" type="Hypothesis"/>

  	<!-- 
  	   Fill in hypothesis object. Note that it is done this way because
       <binding slot="$this.hypothesis" value="Hypothesis($independentVar.selection, $outcomeVar.selection, $comparator.selection)"/>
       Does not work.
  	 -->
  	<postcondition>
       // if statement prevents the newly created variables from propagating throughout system, causing
       // contradiction error. Recall that postconditions are run when a task is live.
       if($this.independentVarValue != undefined &amp;&amp; $this.outcomeVarValue != undefined &amp;&amp; 
          $this.comparatorValue != undefined &amp;&amp; $this.hypothesis == null)
       {
         print("...assigning Hypothesis...");
         $this.hypothesis = new Hypothesis($this.independentVarValue, $this.outcomeVarValue, $this.comparatorValue);
         true; // needed to prevent ClassCastException
       }  	
  	</postcondition>

  	<subtasks id="fillHypothesisTemplate">
  	   <step name="independentVar" task="SelectIndependentVar" />
  	   <step name="comparator" task="SelectComparator" />
  	   <step name="outcomeVar" task="SelectOutcomeVar" />
  	   
  	   <!-- input bindings: indep/dep vars come from simulation def. -->
  	   <binding slot="$independentVar.choices" value="$this.simulation.independentVariables"/>
  	   <binding slot="$outcomeVar.choices" value="$this.simulation.outcomeVariables"/>
  	   <binding slot="$this.comparatorValue" value="$comparator.selection"/>
  	   <binding slot="$this.independentVarValue" value="$independentVar.selection"/>
  	   <binding slot="$this.outcomeVarValue" value="$outcomeVar.selection"/>
  	</subtasks>
  	
  </task>
  
  <!-- Side effect: will fill in IndependentVariable for hypothesis -->
  <task id="SelectIndependentVar">
  	<input name="choices" type="VariableCollection"/>
  	<output name="selection" type="IndependentVariable"/>
  	
  	<postcondition>
  	  $this.selection != null;
  	  if($this.selection != null) { $this.selection != undefined; }
  	  contains($this.choices.vars, $this.selection);
  	</postcondition>
  	
  	<!-- 
  	   If user can't do the step for whatever reason, this script will do it.
  	   This enables the task to be done by the pedagogical agent instead of the user
  	   (maybe if it's in a demonstration mode) 
  	 -->
  	<script>
  	  $this.selection = random_selection($this.choices.vars);
  	</script>
  </task>
  
  <!-- Side effect: will fill in OutcomeVariable for hypothesis -->
  <task id="SelectOutcomeVar">
  	<input name="choices" type="VariableCollection"/>
  	<output name="selection" type="String"/>
  	
  	<postcondition>
  	  $this.selection != undefined;
  	  contains($this.choices.vars, $this.selection);
  	</postcondition>
  	
  	<script>
  	  $this.selection = random_selection($this.choices.vars);
  	</script>	
  </task>
  
  <!-- Side effect: will fill in Comparator for hypothesis -->
  <task id="SelectComparator">
  	<output name="selection" type="Variable"/>
  	
  	<postcondition>
  	  $this.selection != undefined;
  	  contains(comparators, $this.selection);
  	</postcondition>
  	
  	<script>
  	  $this.selection = random_selection(comparators);
  	</script>
  	
  </task>
  
<!-- ********************************************************************** -->
<!-- ********************************************************************** -->
  
  <!-- 
     The "meat": how will a student set up their experiments?
     For now, I am assuming they will run TWO experiments to do CVS 
     (just like Klahr's paper).
     
     Setup2 is the more important since it must be compared to Setup1  
   -->
  <task id="RunExperiments">
    <input name="hypothesis" type="Hypothesis"/>
    <output name="trial1" type="Trial"/>
    <output name="trial2" type="Trial"/>
    
    <!-- May need tail recursion on SetupTrialCheckCVS instead? -->
    <subtasks id="fillInTrials">
       <step name="setup1" task="SetupTrial"/>
       <step name="outcome1" task="RecordOutcome"/>
       <step name="setup2" task="SetupTrialCheckCVS" minOccurs="1" maxOccurs="unbounded"/>
       <step name="outcome2" task="RecordOutcome"/>
       
       <binding slot="$this.trial1" value="Trial($setup1.variables, $outcome1.variables)"/>
       <binding slot="$this.trial2" value="Trial($setup2.variables, $outcome2.variables)"/>
       <binding slot="$setup2.hypothesis" value="$this.hypothesis"/>
       <binding slot="$setup2.checkVars" value="$setup1.variables"/>
    </subtasks>    
  </task>
  
  <!-- 
     Each of these will be set by the GUI elements
     For convenience, aggregate all the variables at the end for other tasks to use 
   -->
  <task id="SetupTrial">
    <input name="leftRampAngleValue" type="string"/>
    <input name="ballStartPositionValue" type="string"/>
    <input name="leftRampFrictionValue" type="string"/>
    <input name="ballTypeValue" type="string"/>
    <output name="leftRampAngle" type="IndependentVariable"/>
    <output name="ballStartPosition" type="IndependentVariable"/>
    <output name="leftRampFriction" type="IndependentVariable"/>
    <output name="ballType" type="IndependentVariable"/>
    <output name="variables" type="VariableCollection"/>
    
    <postcondition>
       // if statement prevents the newly created variables from propagating throughout system, causing
       // contradiction error. Recall that postconditions are run when a task is live.
       if($this.leftRampAngleValue != undefined &amp;&amp; $this.ballStartPositionValue != undefined &amp;&amp; 
          $this.leftRampFrictionValue != undefined &amp;&amp; $this.ballTypeValue != undefined)
       {
         print("...assigning SetupTrial...");
         $this.leftRampAngle = new IndependentVariable("leftRampAngle", $this.leftRampAngleValue);
         $this.ballStartPosition = new IndependentVariable("ballStartPosition", $this.ballStartPositionValue);
         $this.leftRampFriction = new IndependentVariable("leftRampFriction", $this.leftRampFrictionValue);
         $this.ballType = new IndependentVariable("ballType", $this.ballTypeValue);
         $this.variables = new VariableCollection([$this.leftRampAngle, $this.ballStartPosition, $this.leftRampFriction, $this.ballType]);
         true; // needed to prevent ClassCastException
       }
      
    </postcondition>
  </task>
  
  <!-- 
     Record the results of running an experiment. The system always does this.
     For convenience, aggregate all the variables at the end for other tasks to use
     
     This task is capable of automatically recording the data to the graphical table.       
   -->
  <task id="RecordOutcome">
  	<output name="distanceValue" type="number"/>
  	<output name="timeValue" type="number"/>
  	<output name="distance" type="OutcomeVariable"/>
  	<output name="time" type="OutcomeVariable"/>
  	<output name="variables" type="VariableCollection"/>
  	
  	<postcondition>
  	
  	   // --- Creation of appropriate output variables
       if($this.distance != undefined &amp;&amp; $time != undefined)
       {
         print("...assigning RecordOutcome...");
  		 $this.distance = new OutcomeVariable("distance", $this.distanceValue);
  		 $this.time = new OutcomeVariable("time", $this.timeValue);
         $this.variables = new VariableCollection([$this.distance, $this.time]);
       
         true;
       }
       
  	</postcondition>
  	
  	<script>
  	   // --- Get the distance and time from the GUI
  	   importClass(Packages.edu.assistment.experiments.pedagogy.PedagogicalAgent);
  	   
  	   // --- Nasty; wait for the simulation thread to stop (yielding results)
  	   while(!PedagogicalAgent.simController.resultsAvailable());
  	   
  	   // --- Get the results
  	   var distance = PedagogicalAgent.simModel.getDistanceRolledFromIncline();
  	   var time = PedagogicalAgent.simModel.getCurrentTime();
  	   
  	   // --- Put them in the table
  	   PedagogicalAgent.trialTable.getModel().setValueAt(distance, trial-1, 5);
  	   PedagogicalAgent.trialTable.getModel().setValueAt(time, trial-1, 6);
  	   $this.distanceValue = distance;
  	   $this.timeValue = time;
  	   
        // --- Increment the trial counter since we finished a setup/record set
        trial++;  	     	   
  	</script>	

  </task>
  
  <!-- If CVS strategy satisfied, this task will not be needed -->
  <task id="SetupTrialCheckCVS">
     <input name="hypothesis" type="Hypothesis"/>
     <input name="checkVars" type="VariableCollection"/>
     <output name="leftRampAngle" type="IndependentVariable"/>
     <output name="ballStartPosition" type="IndependentVariable"/>
     <output name="leftRampFriction" type="IndependentVariable"/>
     <output name="ballType" type="IndependentVariable"/>
     <output name="variables" type="VariableCollection"/>
     
     <subtasks id="performAndCheck">
     	<step name="setup" task="SetupTrial"/>
     	<step name="check" task="CheckCVS"/>
     	
     	<binding slot="$this.leftRampAngle" value="$setup.leftRampAngle"/>
     	<binding slot="$this.ballStartPosition" value="$setup.ballStartPosition"/>
     	<binding slot="$this.leftRampFriction" value="$setup.leftRampFriction"/>
     	<binding slot="$this.ballType" value="$setup.ballType"/>
     	<binding slot="$this.variables" value="$setup.variables"/>
     	<binding slot="$check.hypothesis" value="$this.hypothesis"/>
     	<binding slot="$check.checkVars" value="$this.checkVars"/>
     	<binding slot="$check.setupVars" value="$setup.variables"/>
     </subtasks>
  </task>
  
  <!-- 
      Determine if the two experiments the user has set up is correct. 
      Engage in scaffolding if not corrected.
   -->
  <task id="CheckCVS">
  	<input name="hypothesis" type="Hypothesis"/>
  	<input name="checkVars" type="VariableCollection"/>
  	<input name="setupVars" type="VariableCollection"/>

    <!-- The hypothesis value was not changed between the two trials -->  	
  	<subtasks id="hypothesisValueUnchanged">
  	    <step name="repair" task="RepairUnchangedHypothesisValue"/>
  	    
  		<applicable>
  		    print("hyp unchanged");
  		    print($this.checkVars);
  		    print($this.setupVars);
  			findVariable($this.hypothesis.independentVariable.name, $this.checkVars.vars).value ==
  			findVariable($this.hypothesis.independentVariable.name, $this.setupVars.vars).value;
  		</applicable>
  		
  		<binding slot="$repair.var1" value="findVariable($this.hypothesis.independentVariable.name, $this.setupVars.vars)"/>
  		<binding slot="$repair.var2" value="findVariable($this.hypothesis.independentVariable.name, $this.checkVars.vars)"/>
  	</subtasks>

    <!-- The other variables not mentioned in the hypothesis were changed -->  	  	
  	<subtasks id="variablesNotControlled">
  	    <step name="repair" task="RepairVariablesNotControlled"/>

  		<applicable>
  		    print("not controlled");
  		    changedVariables($this.hypothesis.independentVariable.name, $this.checkVars.vars, $this.setupVars.vars);
  		</applicable>
  	</subtasks>
  	
  </task>
    
  <!-- Make sure the variables have different values -->  
  <task id="RepairUnchangedHypothesisValue">
     <input name="var1" type="IndependentVariable"/>
     <input name="var2" type="IndependentVariable"/>
     
     <postcondition sufficient="true">
       $this.var1.value != $this.var2.value; 
     </postcondition>
  </task>
      
  <task id="RepairVariablesNotControlled">
     <input name="repairError" type="number"/>
  </task>
  
  
<!-- ********************************************************************** -->
<!-- ********************************************************************** -->    
    
  <task id="FormConclusions">
    <input name="hypothesis" type="Hypothesis"/>
    <input name="trial1" type="Trial"/>
    <input name="trial2" type="Trial"/>
  </task>

  
  <!-- Define all the elements associated with the system -->
  <script init="true">
  
    var IndependentVariableNames = ["leftRampAngle", "rightRampAngle", "ballStartPosition", "leftRampFriction", "rightRampFriction"];
    var OutcomeVariableNames = ["distance", "time"];
    var comparators = ["increases", "decreases", "no_effect"];    

    function IndependentVariable (name, value) 
    {
      this.name = name;
      this.value = value;
    }
    
    function OutcomeVariable (name, value) 
    {
      this.name = name;
      this.value = value;
    }
    
    function Simulation (indepVars, outcomeVars)
    {
      this.independentVariables = indepVars;
      this.outcomeVariables = outcomeVars;
    }
    
    function Hypothesis (indepVar, outcomeVar, comp)
    {
      print("**** Making a hypotehsis ****");
      print(indepVar);
      print(outcomeVar);
      print(comp);
      
      this.comparator = comp;
      this.independentVariable = indepVar;
      this.outcomeVariable = outcomeVar;
    }
    
    function Experiment (sim)
    {
      this.simulation = sim;
    }
    
    function Trial (inputVars, outputVars)
    {
      this.inputVariables = inputVars;
      this.outputVariables = outputVars;
    }
    
    function TrialSet(trials)
    {
      this.trials = trials;
    }
    
    function VariableCollection(vs)
    {
      this.vars = vs;
    }
        
    // +list: list of elements,
    // +elt: element potentially in list
    //
    // return: true if elt in list, false otherwise.
    function contains (list, elt) 
    {
       //print(list)
       //print(elt)
       if(elt == undefined) return false;
       
       return list.indexOf(elt) != -1;
    }

    // return: random element in the list
    function random_selection(list)
    {
       return list[(Math.floor( Math.random() * (list.length)) % list.length)];
    }
    
    // Return a variable in the list with matching name
    // Return: IndependentVariable
    function findVariable(name, varlist)
    {
       print('name: ' + name);
       for(i=0; i &lt; varlist.length; i++)
       {
          print('varlist ' + i + '  name: ' + varlist[i].name);
          if(varlist[i].name == name)
          {
             print('match!');
             return varlist[i];
          }
       }
       print('ERROR! NO MATCH in findVariable for: ' + name);
    }
    
    // Check if any values other than the one w/ name given as input have the same values
    // Return: true if a variable is changed, false otherwise
    function changedVariables(name, vars1, vars2)
    {
       print('changedVariables');
       print('name: ' + name);
       print(vars1);
       print(vars2);
       for(i=0; i &lt; vars1.length; i++)
       {
          // Don't use the hypothesis variable since we DO want that to differ!
          if(vars1[i].name != name)
          {
             matchVar = findVariable(vars1[i].name, vars2);
             print(matchVar.value + " =?= " + vars1[i].value);
             if(matchVar.value != vars1[i].value)
             {
                print("Changed var! Returning true.");
                return true;
             }
          } 
       }
       
       print("CVS done correctly -- returning false");
       return false;
    }
    
    // --- TEST OBJECTS ---
    var leftRampAngle = new IndependentVariable("leftRampAngle", 0.0);
    var rightRampAngle = new IndependentVariable("rightRampAngle", 0.0);
    var ballStartPosition = new IndependentVariable("ballStartPosition", 0.0);
    var leftRampSurfaceType = new IndependentVariable("leftRampSurfaceType", 0.0);
    var rightRampSurfaceType = new IndependentVariable("rightRampSurfaceType", 0.0);
    var ballType = new IndependentVariable("ballType", "golf");
    var distance = new OutcomeVariable("distance", 0.0);
    var time = new OutcomeVariable("time", 0.0);
    
    var indepVars = 
       new VariableCollection([leftRampAngle, rightRampAngle, ballStartPosition, 
                               leftRampSurfaceType, rightRampSurfaceType, ballType]);
    var outcomeVars = new VariableCollection([distance, time]);
                      
    var sim = new Simulation(indepVars, outcomeVars);
    
    var exp = new Experiment(sim);
    
    //var hyp = new Hypothesis(leftRampAngle, distance, "increases");
    
    // --- Global counter for trial number
    var trial = 1;
  </script>

</taskModel>