diff TestController.h @ 0:a223551fdc1f

First commit - copy from tweakathlon.
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Fri, 10 Oct 2014 11:46:42 +0100
parents
children 851833072cf1
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TestController.h	Fri Oct 10 11:46:42 2014 +0100
@@ -0,0 +1,657 @@
+//
+//  TestController.h
+//  tweakathlon
+//
+//  Created by Robert Tubb on 16/01/2014.
+//
+//
+
+#ifndef __tweakathlon__TestController__
+#define __tweakathlon__TestController__
+
+#include <iostream>
+#include "ofMain.h"
+#include "globalVariables.h"
+#include "boost/foreach.hpp"
+#include "boost/bind.hpp"
+#include "boost/function.hpp"
+#include "timeController.h"
+#include <functional>
+#include <algorithm>    // std::transform
+
+//-------------------------------------------------------------
+//-------------------------------------------------------------
+template <class T>
+class randomiseCCVal : public unary_function<T, T>{
+public:
+    T operator()(T a){
+        return ofRandom(0,127);
+        
+    };
+};
+template <class T>
+class randomiseCCValIf : public binary_function<T, bool, T>{
+public:
+    T operator()(T a, bool doit ){
+        if (doit)
+            return ofRandom(0,127);
+        return a;
+        
+    };
+};
+template <class T>
+class printThing : public unary_function<T, void>{
+public:
+    void operator()(T a){
+        cout << a << endl;
+        
+    };
+};
+
+template <class T>
+class difference : public binary_function<T, T, T>{
+public:
+    T operator()(T a, T b ){
+        return abs(a - b);
+    };
+};
+template <class T>
+class squared : public unary_function<T, T>{
+public:
+    T operator()(T a ){
+        return a*a;
+    };
+};
+
+//-------------------------------------------------------------
+
+struct TestResult{
+    float realDistanceToTarget;
+    int targetBandHit; // eg bullseye = 0 edge = 7
+    TimerMillisec timeTaken;
+    int timeWindowHit; // eg < 5s  = 0
+    int dimensionMult;
+    int score;
+    string displayText;
+};
+
+//-------------------------------------------------------------
+// tests, once genrated by the testcontroller get passed around and used to set synths and so on
+//-------------------------------------------------------------
+class Test {
+public:
+    Test(){
+        //?
+        identifyingLetter = '?';
+        timeGiven = 0;
+        panelType= SEQUENTIAL;
+        numDimensions = 0;
+        
+        generateListOfControls();
+        makeRandomTarget();
+        setCandidateToTarget();
+        randomParamSelection(numDimensions);
+        randomiseCandidate();
+        scored = true;
+        withHint = false;
+        memoryTest = false;
+        targetPlaysLeft = 2; // only decr if memory test
+    };
+    // test with random params
+    Test(char letter, int atimeGiven, controlPanelType apanelType, int anumDimensions){
+        // init the stuff
+        //cout << "Making test " << letter << endl;
+        
+        identifyingLetter = letter;
+        timeGiven = atimeGiven;
+        panelType= apanelType;
+        numDimensions = anumDimensions;
+
+        generateListOfControls();
+        makeRandomTarget();
+        setCandidateToTarget();
+        randomParamSelection(numDimensions);
+        randomiseCandidate();
+        scored = true;
+    };
+    // constructor that copies params from other test, pass em in
+    // also serves as a constructor that passes in specific settings
+    Test(char letter, int atimeGiven, controlPanelType apanelType, int anumDimensions, vector<int> aTargetValues, vector<int> aCandidateValues, vector<bool> adjustables){
+        identifyingLetter = letter;
+        timeGiven = atimeGiven;
+        panelType= apanelType;
+        numDimensions = anumDimensions;
+        
+        whichCandidateParamAdjustable = adjustables;
+        generateListOfControls();
+        targetParams = aTargetValues;
+        candidateParams = aCandidateValues;
+        scored = true;
+        
+    }
+    // constructot that enables you to set the space the test is carried out in but not start
+    Test(char letter, int aTimeGiven, controlPanelType pType, int aNumDimensions, vector<bool> adjustables){
+        identifyingLetter = letter;
+        timeGiven = aTimeGiven;
+        panelType= pType;
+        numDimensions = aNumDimensions;
+        
+        generateListOfControls();
+        makeRandomTarget();
+        setCandidateToTarget();
+        whichCandidateParamAdjustable = adjustables;
+        randomiseCandidate();
+        scored = true;
+    }
+    
+
+    
+    // constructor that takes target values, but leaves those not adjustable
+    Test(char letter, int aTimeGiven, controlPanelType pType, int aNumDimensions, vector<bool> adjustables, vector<int> aTargetValues){
+        identifyingLetter = letter;
+        timeGiven = aTimeGiven;
+        panelType= pType;
+        numDimensions = aNumDimensions;
+        
+        generateListOfControls();
+        targetParams = aTargetValues;
+        whichCandidateParamAdjustable = adjustables;
+        randomiseTargetAdjustable();
+        setCandidateToTarget();
+        
+        randomiseCandidate();
+        scored = true;
+    }
+    
+        // the one that is called from generate some tests!!
+    Test(char letter, int aTimeGiven, controlPanelType pType, int aNumDimensions, vector<bool> adjustables, vector<int> aTargetValues, bool ascored = true, bool ahint = false, bool aMemoryTest = false){
+        identifyingLetter = letter;
+        timeGiven = aTimeGiven;
+        panelType= pType;
+        numDimensions = aNumDimensions;
+        
+        generateListOfControls();
+        targetParams = aTargetValues;
+        whichCandidateParamAdjustable = adjustables;
+        randomiseTargetAdjustable();
+        setCandidateToTarget();
+        
+        randomiseCandidate();
+        scored = ascored;
+        withHint = ahint;
+        memoryTest = aMemoryTest;
+        targetPlaysLeft = 1; // only decr if memory test
+    };
+    bool isWithHint(){
+        return withHint;
+    };
+    bool isMemoryTest(){
+        return memoryTest;
+    };
+    bool isPractice(){
+        return !scored;
+    };
+    float euclideanDistance(vector<int> v1, vector<int> v2) const{
+        if (v1.size() != v2.size()){
+            cout << "ERROR ERROR: vectors must be same length for Mr Euclid";
+            return 0.;
+        }
+        vector<float> diff;
+        
+        std::transform(v1.begin(), v1.end(), v2.begin(), v1.begin(), difference<float>());
+        // sqr diff
+        
+        std::transform(v1.begin(), v1.end(), v1.begin(),squared<float>());
+        float ans = std::accumulate(v1.begin(),v1.end(),0.0);
+        
+        return sqrt(ans);
+        
+    };
+    
+    TestResult getScoreForAnswer(vector<int> answer, TimerMillisec timeTaken) const {
+        TestResult result;
+        stringstream msg;
+        int score = 0;
+        // work out euc distance from actual point
+        //for_each(answer.begin(),answer.end(),printThing<int>());
+        //for_each(targetParams.begin(),targetParams.end(),printThing<int>());
+        float dist = euclideanDistance(targetParams, answer);
+        auto dimComp = sqrt(numDimensions);
+        int band = -1;
+        if (dist < TARGET_SCORE_CC_BAND*dimComp){
+            score = 50;
+            band = 1;
+
+            msg << "DOUBLE BULLSEYE!" << endl;
+
+
+        }else if (dist < TARGET_SCORE_CC_BAND*2*dimComp){
+
+            score = 25;
+            band = 2;
+            
+            msg << "SINGLE BULLSEYE!" << endl;
+
+        }else if (dist < TARGET_SCORE_CC_BAND*3*dimComp){
+
+            score = 15;
+            band = 3;
+            msg << "CLOSE..." << endl;
+
+        }else if (dist < TARGET_SCORE_CC_BAND*4*dimComp){
+            score = 5;
+            band = 4;
+            msg << "OK...ISH" << endl;
+
+        }else if (dist < TARGET_SCORE_CC_BAND*6*dimComp){ // 30
+            score = 2;
+            band = 5;
+            msg << "MEDIOCRE" << endl;
+
+        }else if (dist < TARGET_SCORE_CC_BAND*9*dimComp){ // 45
+            score = 1;
+            band = 6;
+            msg << "POOR..." << endl;
+
+        }else{
+            score = 0;
+            band = 7;
+            msg << "MISSED COMPLETELY!" << endl;
+
+            
+        }
+        msg << "Distance from target: " << dist << endl;
+        msg << "Basic Score: " << score << endl;
+        msg << "-----" << endl;
+        msg << "Time taken: " << timeTaken/1000.0 << endl;
+        int window = -1;
+        if (timeTaken < 5000){
+            msg << "Under 5 seconds: 10x bonus" << endl;
+            score *= 10;
+            window = 1;
+        }else if (timeTaken < 10000){
+            msg << "Under 10 seconds: 5x bonus" << endl;
+            score *= 5;
+            window = 2;
+        }else if (timeTaken < 15000){
+            msg << "Under 15 seconds: 3x bonus" << endl;
+            score *=3;
+            window = 3;
+        }else if (timeTaken < 20000){
+            msg << "Under 20 seconds: 2x bonus" << endl;
+            score *=2;
+            window = 4;
+        }else if (timeTaken < 20000){
+            msg << "Under 25 seconds: no bonus" << endl;
+            score *=2;
+            window = 4;
+        }else if (timeTaken > 30000){
+            msg << "Over 30 seconds: TOO LATE!" << endl;
+            score *=0;
+            window = 4;
+        }
+        score *= numDimensions; // dimension bonus
+        msg << numDimensions*numDimensions << "X Dimension bonus" << endl;
+        msg << "-----" << endl;
+        if (!scored){
+            
+            msg << "PRACTICE RUN, scored: " << score << endl;
+            score = 0;
+        }else{
+            msg << "Total: " << score << endl;
+        }
+        result.realDistanceToTarget = dist;
+        result.targetBandHit = band; // eg bullseye = 0 edge = 7
+        result.timeTaken = timeTaken;
+        result.timeWindowHit = window; // eg < 5s  = 0
+        result.dimensionMult = numDimensions*numDimensions;
+        result.score = score;
+        result.displayText = msg.str();
+        
+
+        return result;
+    }
+    
+    vector<int> getChangeableIndices() {
+        vector<int> ids;
+        for(int i=0; i< whichCandidateParamAdjustable.size();i++){
+            if (whichCandidateParamAdjustable[i]){
+                ids.push_back(i);
+            }
+        }
+        return ids;
+    };
+    vector<int> getTargetValues() const{
+        return targetParams;
+        
+    };
+    vector<int> getStartingCandidateValues() const{
+        return candidateParams;
+        
+    };
+
+    int getNumDimensions() const{
+        return numDimensions;
+    }
+    char getTestLetter() const{
+        return identifyingLetter;
+    }
+    vector<bool> getAdjustableMask() const{
+        return whichCandidateParamAdjustable;
+    }
+    vector<controllerType> getListOfControlTypes() const{
+        return listOfControls;
+    }
+    controlPanelType getControlPanelType() const{
+        return panelType;
+    };
+    string getTestTypeAdvanceWarning(){
+        stringstream msg;
+        
+        msg << "Next test: " << endl << endl;
+        if (panelType == REVISITABLE){
+            msg << "Sliders " << " x " << numDimensions << endl;
+        }
+        if (panelType == SIMULTANEOUS && numDimensions == 2){
+            msg << "XY pad " << endl;
+        }
+        if (panelType == SIMULTANEOUS && numDimensions == 3){
+            msg << "LEAP MOTION 3D -> -> -> -> ->" << endl;
+        }
+        if (isMemoryTest()){
+            msg << endl << "MEMORY TEST - ONE TARGET LISTEN ONLY!!" << endl;
+        }
+        if (!scored){
+            msg << "PRACTICE RUN" << endl;
+        }
+        
+        return msg.str();
+    };
+    bool checkTargetPlaysRemaining(){
+        if (isMemoryTest()){
+            targetPlaysLeft--;
+            if (targetPlaysLeft <= 0){
+                return false;
+            }
+        
+        }
+        return true;
+    }
+    int getTargetPlaysLeft(){
+        return targetPlaysLeft;
+    }
+    
+private:
+    char identifyingLetter;
+    int timeGiven;
+    int timeElapsed;
+    controlPanelType panelType;
+    vector<controllerType> listOfControls;
+    int numDimensions;
+    vector<int> targetParams;
+    vector<int> candidateParams;
+    vector<bool> whichCandidateParamAdjustable;
+    int targetPlaysLeft;
+    bool scored, withHint, memoryTest;
+    
+    
+    void setCandidateToTarget(){
+        
+        for(vector<int>::iterator ii = targetParams.begin(); ii < targetParams.end(); ii++){
+            candidateParams.push_back(*ii);
+        }
+    };
+    
+    void generateListOfControls(){
+        // need ?
+        if(panelType == SEQUENTIAL || panelType == REVISITABLE){
+            for(int i=0;i<numDimensions;i++){
+                listOfControls.push_back(SLIDER);
+            }
+        }else{ // panelType == SIMULTANEOUS
+            if(numDimensions == 2){
+                listOfControls.push_back(XYPAD);
+            }
+            if(numDimensions == 3){
+                listOfControls.push_back(LEAP3D);
+
+            }
+            if(numDimensions == 4){
+                listOfControls.push_back(XYPAD);
+                listOfControls.push_back(XYPAD);
+            }
+            
+        }
+    };
+
+    void makeRandomTarget(){
+        targetParams.clear();
+        for(int i=0;i<TOTAL_NUM_PARAMS; i++){
+            int n = ofRandom(0,127); // why does this consistently come out the same??
+            targetParams.push_back(n);
+            
+        }
+  
+    };
+
+    
+    void randomiseCandidate(){
+        // randomises only those who are adjustable
+        // needs to be more than a certain distance (1/3 of total = 40?)
+        float reasonableDistance = 40.0*sqrt(numDimensions);
+        bool notFarEnough = true;
+        while (notFarEnough){
+            transform(candidateParams.begin(),
+                      candidateParams.end(),
+                      whichCandidateParamAdjustable.begin(),
+                      candidateParams.begin(),
+                      randomiseCCValIf<int>());
+            notFarEnough = ( euclideanDistance(candidateParams,targetParams) < reasonableDistance );
+        }
+        cout << "Test distance = " << euclideanDistance(candidateParams,targetParams) << endl;
+        
+    };
+    
+    void randomiseTargetAdjustable(){
+        // randomises only those who are adjustable, in the case where we want exactly the same space
+        // i.e. original target was fed in
+            transform(targetParams.begin(),
+                      targetParams.end(),
+                      whichCandidateParamAdjustable.begin(),
+                      targetParams.begin(),
+                      randomiseCCValIf<int>());
+
+        
+    };
+    
+    // select a few params that will be the ones to chng
+    void randomParamSelection(int numToChange){
+
+        for(int i= 0; i<numToChange; i++){
+            whichCandidateParamAdjustable.push_back(true);
+        }
+        for(int i= 0; i<(TOTAL_NUM_PARAMS - numToChange); i++){
+            whichCandidateParamAdjustable.push_back(false);
+        }
+        random_shuffle(whichCandidateParamAdjustable.begin(), whichCandidateParamAdjustable.end());
+        //for_each(whichCandidateParamAdjustable.begin(), whichCandidateParamAdjustable.end(), printThing<int>());
+        
+    };
+
+    
+};
+//-------------------------------------------------------------
+//-------------------------------------------------------------
+
+
+class TestController {
+public:
+    static const int numDifferentTests;
+    static const int totalNumTests;
+    vector<Test>::iterator currentTest;
+    TestController(){
+        
+       generateSomeTests(1, 3, SIMULTANEOUS,1, false, true, true);
+        
+        generateSomeTests(4, 2, SIMULTANEOUS, 1, true);
+        
+        // 3 demo runs
+        generateSomeTests(1, 1, REVISITABLE, 1, false, false, false);
+        generateSomeTests(1, 2, SIMULTANEOUS, 1, false, false, true);
+        generateSomeTests(1, 3, SIMULTANEOUS,1, false, true, false);
+        // 3 practice runs
+
+        //----------------------
+        // 1D set 1
+        // 7 to 12
+        generateSomeTests(2, 1, REVISITABLE, 1, true);
+        generateSomeTests(2, 1, REVISITABLE, 2, true);
+        generateSomeTests(2, 1, REVISITABLE, 4, true);
+
+        
+        // 2D tests 1
+        // 13 to 28
+        generateSomeTests(4, 2, REVISITABLE, 1, true);
+        generateSomeTests(4, 2, SIMULTANEOUS, 1, true);
+        generateSomeTests(4, 2, SIMULTANEOUS, 2, true);
+        generateSomeTests(4, 2, REVISITABLE, 2, true);
+        
+        // 3D Tests 1
+        // 29 to 44
+        generateSomeTests(8,3,SIMULTANEOUS,1, true);
+        generateSomeTests(8,3,REVISITABLE,1, true);
+        
+        //----------------------
+        
+        // 1D set 2 (WITH HINTS)
+        // 45 to 47
+        generateSomeTests(1, 1, REVISITABLE, 1, true, true);
+        generateSomeTests(1, 1, REVISITABLE, 2, true, true);
+        generateSomeTests(1, 1, REVISITABLE, 4, true, true);
+        
+        // 2D set 2 (WITH HINTS)
+        // 48 to 51
+        generateSomeTests(2, 2, SIMULTANEOUS, 1, true, true);
+        generateSomeTests(2, 2, REVISITABLE, 1, true, true);
+        
+        // 3D set 2 (WITH HINTS)
+        // 52 to 59
+        generateSomeTests(4,3,REVISITABLE ,1, true, true);
+        generateSomeTests(4,3,SIMULTANEOUS,1, true, true);
+        
+        //----------------------
+        
+        // NOW MEMORY TESTS!
+        // 1D memory test
+        generateSomeTests(2, 1, REVISITABLE, 1, true,false, true);
+        generateSomeTests(2, 1, REVISITABLE, 2, true,false, true);
+        generateSomeTests(2, 1, REVISITABLE, 4, true,false, true);
+        
+        // 2D set 3
+        generateSomeTests(4, 2, SIMULTANEOUS, 1, true,false, true);
+        generateSomeTests(4, 2, REVISITABLE, 1, true,false, true);
+        generateSomeTests(4, 2, REVISITABLE, 2, true,false, true);
+        generateSomeTests(4, 2, SIMULTANEOUS, 2, true,false, true);
+        
+       
+        // 3D set 3
+        // MEMORY
+        generateSomeTests(8,3,REVISITABLE ,1, true,false, true);
+        generateSomeTests(8,3,SIMULTANEOUS,1, true,false, true);
+        // 72
+        //----------------------
+
+        
+        
+        //generate1DSpecificTests();
+        
+        //generate2DSpecificTests();
+        //generate3DSpecificTests();
+        //generate2DRandomTests();
+        //generate4DTests();
+        currentTest = testList.begin()-1; // dont do this
+        scoreRunningTotal = 0;
+    };
+
+    
+    bool isLastTest(){
+        if (currentTest == testList.end()-1){
+            return true;
+        }
+        return false;
+    }
+    Test goToNextTest(){
+        
+        if (currentTest < testList.end()-1){
+            currentTest++;
+            cout << "***************** NEXT TEST : " << (*currentTest).getTestLetter() <<  endl;
+            // return a copy
+            // (*currentTest).logThisTestSetup();
+            
+            return *currentTest;
+            
+        }else{
+            cout << "Sequence finished!!!!!!" << endl;
+            Test dummyTest;
+            return dummyTest;
+        }
+    };
+
+    Test getCurrentTest(){
+        // check for valid current test?
+        return *currentTest;
+    }
+    
+    Test* getCurrentTestPtr(){
+        // check for valid current test?
+        if (currentTest < testList.end() && currentTest >= testList.begin()){
+            return &(*currentTest);
+        }else{
+            return NULL;
+        }
+    }
+    //
+    TestResult submitAnswer(vector<int> answer, TimerMillisec timeTaken){
+        TestResult result;
+        result = (*currentTest).getScoreForAnswer(answer, timeTaken);
+        scoreRunningTotal += result.score;
+        return result;
+    }
+    vector<controllerType> getCurrentListOfControls(){
+        return (*currentTest).getListOfControlTypes();
+    }
+    int getScoreRunningTotal(){
+        return scoreRunningTotal;
+    };
+    char getCurrentTestLetter(){
+        return (*currentTest).getTestLetter();
+    };
+    controlPanelType getCurrentPanelType(){
+        return (*currentTest).getControlPanelType();
+    };
+    vector<int> getCurrentChangeableParams(){
+        return (*currentTest).getChangeableIndices();
+    }
+private:
+    void generate2DRandomTests();
+    void generate1DSpecificTests();
+    void generate2DSpecificTests();
+    void generate3DSpecificTests();
+    void generate4DTests();
+    void generateSomeTests(int howManyTests, int dimensions, controlPanelType panelType, int whichSpace, bool scored = true,bool aHint = false, bool aMemoryTest = false);
+    
+    void generateTestSequence(){
+
+    };
+    //aregaergeargera
+    vector<Test> testList;
+
+    long scoreRunningTotal;
+   
+    
+};
+
+
+
+
+#endif /* defined(__tweakathlon__TestController__) */