Mercurial > hg > tweakathon2ios
view TestController.h @ 5:213df0baed47
presets file is updated when one is saved.
author | Robert Tubb <rt300@eecs.qmul.ac.uk> |
---|---|
date | Thu, 16 Oct 2014 15:52:53 +0100 |
parents | 851833072cf1 |
children | 8124f46eda65 |
line wrap: on
line source
// // TestController.h // tweakathlon // // Created by Robert Tubb on 16/01/2014. // // controller for as-long-as-it-takes, arbitrary target tests #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 (panelType == REVISITABLE && numDimensions > 3){ msg << numDimensions << " DOF. The new frontier." << 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(){ // int howManyTests, // int numDimensions, // controlPanelType panelType, // int whichSpace, // bool scored, // bool aHint, // bool aMemoryTest){ generateSomeTests(8, 8, REVISITABLE,1, false, false, false); 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__) */