rt300@0: // rt300@0: // TestController.h rt300@0: // tweakathlon rt300@0: // rt300@0: // Created by Robert Tubb on 16/01/2014. rt300@0: // rt300@5: rt300@5: rt300@5: // controller for as-long-as-it-takes, arbitrary target tests rt300@0: rt300@0: #ifndef __tweakathlon__TestController__ rt300@0: #define __tweakathlon__TestController__ rt300@0: rt300@0: #include rt300@0: #include "ofMain.h" rt300@0: #include "globalVariables.h" rt300@0: #include "boost/foreach.hpp" rt300@0: #include "boost/bind.hpp" rt300@0: #include "boost/function.hpp" rt300@0: #include "timeController.h" rt300@22: #include "algorithms.h" rt300@0: #include rt300@0: #include // std::transform rt300@0: rt300@0: //------------------------------------------------------------- rt300@0: rt300@0: struct TestResult{ rt300@0: float realDistanceToTarget; rt300@0: int targetBandHit; // eg bullseye = 0 edge = 7 rt300@0: TimerMillisec timeTaken; rt300@0: int timeWindowHit; // eg < 5s = 0 rt300@0: int dimensionMult; rt300@0: int score; rt300@0: string displayText; rt300@0: }; rt300@0: rt300@0: //------------------------------------------------------------- rt300@0: // tests, once genrated by the testcontroller get passed around and used to set synths and so on rt300@0: //------------------------------------------------------------- rt300@0: class Test { rt300@0: public: rt300@0: Test(){ rt300@0: //? rt300@0: identifyingLetter = '?'; rt300@0: timeGiven = 0; rt300@0: panelType= SEQUENTIAL; rt300@0: numDimensions = 0; rt300@0: rt300@0: generateListOfControls(); rt300@0: makeRandomTarget(); rt300@0: setCandidateToTarget(); rt300@0: randomParamSelection(numDimensions); rt300@0: randomiseCandidate(); rt300@0: scored = true; rt300@0: withHint = false; rt300@0: memoryTest = false; rt300@0: targetPlaysLeft = 2; // only decr if memory test rt300@0: }; rt300@0: // test with random params rt300@0: Test(char letter, int atimeGiven, controlPanelType apanelType, int anumDimensions){ rt300@0: // init the stuff rt300@0: //cout << "Making test " << letter << endl; rt300@0: rt300@0: identifyingLetter = letter; rt300@0: timeGiven = atimeGiven; rt300@0: panelType= apanelType; rt300@0: numDimensions = anumDimensions; rt300@0: rt300@0: generateListOfControls(); rt300@0: makeRandomTarget(); rt300@0: setCandidateToTarget(); rt300@0: randomParamSelection(numDimensions); rt300@0: randomiseCandidate(); rt300@0: scored = true; rt300@0: }; rt300@0: // constructor that copies params from other test, pass em in rt300@0: // also serves as a constructor that passes in specific settings rt300@0: Test(char letter, int atimeGiven, controlPanelType apanelType, int anumDimensions, vector aTargetValues, vector aCandidateValues, vector adjustables){ rt300@0: identifyingLetter = letter; rt300@0: timeGiven = atimeGiven; rt300@0: panelType= apanelType; rt300@0: numDimensions = anumDimensions; rt300@0: rt300@0: whichCandidateParamAdjustable = adjustables; rt300@0: generateListOfControls(); rt300@0: targetParams = aTargetValues; rt300@0: candidateParams = aCandidateValues; rt300@0: scored = true; rt300@0: rt300@0: } rt300@0: // constructot that enables you to set the space the test is carried out in but not start rt300@0: Test(char letter, int aTimeGiven, controlPanelType pType, int aNumDimensions, vector adjustables){ rt300@0: identifyingLetter = letter; rt300@0: timeGiven = aTimeGiven; rt300@0: panelType= pType; rt300@0: numDimensions = aNumDimensions; rt300@0: rt300@0: generateListOfControls(); rt300@0: makeRandomTarget(); rt300@0: setCandidateToTarget(); rt300@0: whichCandidateParamAdjustable = adjustables; rt300@0: randomiseCandidate(); rt300@0: scored = true; rt300@0: } rt300@0: rt300@0: rt300@0: rt300@0: // constructor that takes target values, but leaves those not adjustable rt300@0: Test(char letter, int aTimeGiven, controlPanelType pType, int aNumDimensions, vector adjustables, vector aTargetValues){ rt300@0: identifyingLetter = letter; rt300@0: timeGiven = aTimeGiven; rt300@0: panelType= pType; rt300@0: numDimensions = aNumDimensions; rt300@0: rt300@0: generateListOfControls(); rt300@0: targetParams = aTargetValues; rt300@0: whichCandidateParamAdjustable = adjustables; rt300@0: randomiseTargetAdjustable(); rt300@0: setCandidateToTarget(); rt300@0: rt300@0: randomiseCandidate(); rt300@0: scored = true; rt300@0: } rt300@0: rt300@0: // the one that is called from generate some tests!! rt300@0: Test(char letter, int aTimeGiven, controlPanelType pType, int aNumDimensions, vector adjustables, vector aTargetValues, bool ascored = true, bool ahint = false, bool aMemoryTest = false){ rt300@0: identifyingLetter = letter; rt300@0: timeGiven = aTimeGiven; rt300@0: panelType= pType; rt300@0: numDimensions = aNumDimensions; rt300@0: rt300@0: generateListOfControls(); rt300@0: targetParams = aTargetValues; rt300@0: whichCandidateParamAdjustable = adjustables; rt300@0: randomiseTargetAdjustable(); rt300@0: setCandidateToTarget(); rt300@0: rt300@0: randomiseCandidate(); rt300@0: scored = ascored; rt300@0: withHint = ahint; rt300@0: memoryTest = aMemoryTest; rt300@0: targetPlaysLeft = 1; // only decr if memory test rt300@0: }; rt300@0: bool isWithHint(){ rt300@0: return withHint; rt300@0: }; rt300@0: bool isMemoryTest(){ rt300@0: return memoryTest; rt300@0: }; rt300@0: bool isPractice(){ rt300@0: return !scored; rt300@0: }; rt300@34: rt300@0: rt300@0: TestResult getScoreForAnswer(vector answer, TimerMillisec timeTaken) const { rt300@0: TestResult result; rt300@0: stringstream msg; rt300@0: int score = 0; rt300@0: // work out euc distance from actual point rt300@0: //for_each(answer.begin(),answer.end(),printThing()); rt300@0: //for_each(targetParams.begin(),targetParams.end(),printThing()); rt300@0: float dist = euclideanDistance(targetParams, answer); rt300@0: auto dimComp = sqrt(numDimensions); rt300@0: int band = -1; rt300@0: if (dist < TARGET_SCORE_CC_BAND*dimComp){ rt300@0: score = 50; rt300@0: band = 1; rt300@0: rt300@0: msg << "DOUBLE BULLSEYE!" << endl; rt300@0: rt300@0: rt300@0: }else if (dist < TARGET_SCORE_CC_BAND*2*dimComp){ rt300@0: rt300@0: score = 25; rt300@0: band = 2; rt300@0: rt300@0: msg << "SINGLE BULLSEYE!" << endl; rt300@0: rt300@0: }else if (dist < TARGET_SCORE_CC_BAND*3*dimComp){ rt300@0: rt300@0: score = 15; rt300@0: band = 3; rt300@0: msg << "CLOSE..." << endl; rt300@0: rt300@0: }else if (dist < TARGET_SCORE_CC_BAND*4*dimComp){ rt300@0: score = 5; rt300@0: band = 4; rt300@0: msg << "OK...ISH" << endl; rt300@0: rt300@0: }else if (dist < TARGET_SCORE_CC_BAND*6*dimComp){ // 30 rt300@0: score = 2; rt300@0: band = 5; rt300@0: msg << "MEDIOCRE" << endl; rt300@0: rt300@0: }else if (dist < TARGET_SCORE_CC_BAND*9*dimComp){ // 45 rt300@0: score = 1; rt300@0: band = 6; rt300@0: msg << "POOR..." << endl; rt300@0: rt300@0: }else{ rt300@0: score = 0; rt300@0: band = 7; rt300@0: msg << "MISSED COMPLETELY!" << endl; rt300@0: rt300@0: rt300@0: } rt300@0: msg << "Distance from target: " << dist << endl; rt300@0: msg << "Basic Score: " << score << endl; rt300@0: msg << "-----" << endl; rt300@0: msg << "Time taken: " << timeTaken/1000.0 << endl; rt300@0: int window = -1; rt300@0: if (timeTaken < 5000){ rt300@0: msg << "Under 5 seconds: 10x bonus" << endl; rt300@0: score *= 10; rt300@0: window = 1; rt300@0: }else if (timeTaken < 10000){ rt300@0: msg << "Under 10 seconds: 5x bonus" << endl; rt300@0: score *= 5; rt300@0: window = 2; rt300@0: }else if (timeTaken < 15000){ rt300@0: msg << "Under 15 seconds: 3x bonus" << endl; rt300@0: score *=3; rt300@0: window = 3; rt300@0: }else if (timeTaken < 20000){ rt300@0: msg << "Under 20 seconds: 2x bonus" << endl; rt300@0: score *=2; rt300@0: window = 4; rt300@0: }else if (timeTaken < 20000){ rt300@0: msg << "Under 25 seconds: no bonus" << endl; rt300@0: score *=2; rt300@0: window = 4; rt300@0: }else if (timeTaken > 30000){ rt300@0: msg << "Over 30 seconds: TOO LATE!" << endl; rt300@0: score *=0; rt300@0: window = 4; rt300@0: } rt300@0: score *= numDimensions; // dimension bonus rt300@0: msg << numDimensions*numDimensions << "X Dimension bonus" << endl; rt300@0: msg << "-----" << endl; rt300@0: if (!scored){ rt300@0: rt300@0: msg << "PRACTICE RUN, scored: " << score << endl; rt300@0: score = 0; rt300@0: }else{ rt300@0: msg << "Total: " << score << endl; rt300@0: } rt300@0: result.realDistanceToTarget = dist; rt300@0: result.targetBandHit = band; // eg bullseye = 0 edge = 7 rt300@0: result.timeTaken = timeTaken; rt300@0: result.timeWindowHit = window; // eg < 5s = 0 rt300@0: result.dimensionMult = numDimensions*numDimensions; rt300@0: result.score = score; rt300@0: result.displayText = msg.str(); rt300@0: rt300@0: rt300@0: return result; rt300@0: } rt300@0: rt300@0: vector getChangeableIndices() { rt300@0: vector ids; rt300@0: for(int i=0; i< whichCandidateParamAdjustable.size();i++){ rt300@0: if (whichCandidateParamAdjustable[i]){ rt300@0: ids.push_back(i); rt300@0: } rt300@0: } rt300@0: return ids; rt300@0: }; rt300@0: vector getTargetValues() const{ rt300@0: return targetParams; rt300@0: rt300@0: }; rt300@0: vector getStartingCandidateValues() const{ rt300@0: return candidateParams; rt300@0: rt300@0: }; rt300@0: rt300@0: int getNumDimensions() const{ rt300@0: return numDimensions; rt300@0: } rt300@0: char getTestLetter() const{ rt300@0: return identifyingLetter; rt300@0: } rt300@0: vector getAdjustableMask() const{ rt300@0: return whichCandidateParamAdjustable; rt300@0: } rt300@0: vector getListOfControlTypes() const{ rt300@0: return listOfControls; rt300@0: } rt300@0: controlPanelType getControlPanelType() const{ rt300@0: return panelType; rt300@0: }; rt300@0: string getTestTypeAdvanceWarning(){ rt300@2: rt300@0: stringstream msg; rt300@0: rt300@0: msg << "Next test: " << endl << endl; rt300@0: if (panelType == REVISITABLE){ rt300@0: msg << "Sliders " << " x " << numDimensions << endl; rt300@0: } rt300@0: if (panelType == SIMULTANEOUS && numDimensions == 2){ rt300@0: msg << "XY pad " << endl; rt300@0: } rt300@0: if (panelType == SIMULTANEOUS && numDimensions == 3){ rt300@0: msg << "LEAP MOTION 3D -> -> -> -> ->" << endl; rt300@0: } rt300@2: if (panelType == REVISITABLE && numDimensions > 3){ rt300@2: msg << numDimensions << " DOF. The new frontier." << endl; rt300@2: } rt300@0: if (isMemoryTest()){ rt300@0: msg << endl << "MEMORY TEST - ONE TARGET LISTEN ONLY!!" << endl; rt300@0: } rt300@0: if (!scored){ rt300@0: msg << "PRACTICE RUN" << endl; rt300@0: } rt300@0: rt300@0: return msg.str(); rt300@0: }; rt300@0: bool checkTargetPlaysRemaining(){ rt300@0: if (isMemoryTest()){ rt300@0: targetPlaysLeft--; rt300@0: if (targetPlaysLeft <= 0){ rt300@0: return false; rt300@0: } rt300@0: rt300@0: } rt300@0: return true; rt300@0: } rt300@0: int getTargetPlaysLeft(){ rt300@0: return targetPlaysLeft; rt300@0: } rt300@0: rt300@0: private: rt300@0: char identifyingLetter; rt300@0: int timeGiven; rt300@0: int timeElapsed; rt300@0: controlPanelType panelType; rt300@0: vector listOfControls; rt300@0: int numDimensions; rt300@0: vector targetParams; rt300@0: vector candidateParams; rt300@0: vector whichCandidateParamAdjustable; rt300@0: int targetPlaysLeft; rt300@0: bool scored, withHint, memoryTest; rt300@0: rt300@0: rt300@0: void setCandidateToTarget(){ rt300@0: rt300@0: for(vector::iterator ii = targetParams.begin(); ii < targetParams.end(); ii++){ rt300@0: candidateParams.push_back(*ii); rt300@0: } rt300@0: }; rt300@0: rt300@0: void generateListOfControls(){ rt300@0: // need ? rt300@0: if(panelType == SEQUENTIAL || panelType == REVISITABLE){ rt300@0: for(int i=0;i()); rt300@0: notFarEnough = ( euclideanDistance(candidateParams,targetParams) < reasonableDistance ); rt300@0: } rt300@0: cout << "Test distance = " << euclideanDistance(candidateParams,targetParams) << endl; rt300@0: rt300@0: }; rt300@0: rt300@0: void randomiseTargetAdjustable(){ rt300@0: // randomises only those who are adjustable, in the case where we want exactly the same space rt300@0: // i.e. original target was fed in rt300@0: transform(targetParams.begin(), rt300@0: targetParams.end(), rt300@0: whichCandidateParamAdjustable.begin(), rt300@0: targetParams.begin(), rt300@0: randomiseCCValIf()); rt300@0: rt300@0: rt300@0: }; rt300@0: rt300@0: // select a few params that will be the ones to chng rt300@0: void randomParamSelection(int numToChange){ rt300@0: rt300@0: for(int i= 0; i()); rt300@0: rt300@0: }; rt300@0: rt300@0: rt300@0: }; rt300@0: //------------------------------------------------------------- rt300@0: //------------------------------------------------------------- rt300@0: rt300@0: rt300@0: class TestController { rt300@0: public: rt300@0: static const int numDifferentTests; rt300@0: static const int totalNumTests; rt300@0: vector::iterator currentTest; rt300@0: TestController(){ rt300@0: rt300@2: // int howManyTests, rt300@2: // int numDimensions, rt300@2: // controlPanelType panelType, rt300@2: // int whichSpace, rt300@2: // bool scored, rt300@2: // bool aHint, rt300@2: // bool aMemoryTest){ rt300@0: rt300@42: generateSomeTests(2, 6, REVISITABLE,1, true, false, false); rt300@42: generateSomeTests(2, 6, SIMULTANEOUS,1, true, false, false); rt300@0: rt300@0: currentTest = testList.begin()-1; // dont do this rt300@0: scoreRunningTotal = 0; rt300@0: }; rt300@0: rt300@0: rt300@0: bool isLastTest(){ rt300@0: if (currentTest == testList.end()-1){ rt300@0: return true; rt300@0: } rt300@0: return false; rt300@0: } rt300@0: Test goToNextTest(){ rt300@0: rt300@0: if (currentTest < testList.end()-1){ rt300@0: currentTest++; rt300@0: cout << "***************** NEXT TEST : " << (*currentTest).getTestLetter() << endl; rt300@0: // return a copy rt300@0: // (*currentTest).logThisTestSetup(); rt300@0: rt300@0: return *currentTest; rt300@0: rt300@0: }else{ rt300@0: cout << "Sequence finished!!!!!!" << endl; rt300@0: Test dummyTest; rt300@0: return dummyTest; rt300@0: } rt300@0: }; rt300@0: rt300@0: Test getCurrentTest(){ rt300@0: // check for valid current test? rt300@0: return *currentTest; rt300@0: } rt300@0: rt300@0: Test* getCurrentTestPtr(){ rt300@0: // check for valid current test? rt300@0: if (currentTest < testList.end() && currentTest >= testList.begin()){ rt300@0: return &(*currentTest); rt300@0: }else{ rt300@0: return NULL; rt300@0: } rt300@0: } rt300@0: // rt300@0: TestResult submitAnswer(vector answer, TimerMillisec timeTaken){ rt300@0: TestResult result; rt300@0: result = (*currentTest).getScoreForAnswer(answer, timeTaken); rt300@0: scoreRunningTotal += result.score; rt300@0: return result; rt300@0: } rt300@0: vector getCurrentListOfControls(){ rt300@0: return (*currentTest).getListOfControlTypes(); rt300@0: } rt300@0: int getScoreRunningTotal(){ rt300@0: return scoreRunningTotal; rt300@0: }; rt300@0: char getCurrentTestLetter(){ rt300@0: return (*currentTest).getTestLetter(); rt300@0: }; rt300@0: controlPanelType getCurrentPanelType(){ rt300@0: return (*currentTest).getControlPanelType(); rt300@0: }; rt300@0: vector getCurrentChangeableParams(){ rt300@0: return (*currentTest).getChangeableIndices(); rt300@0: } rt300@0: private: rt300@0: void generate2DRandomTests(); rt300@0: void generate1DSpecificTests(); rt300@0: void generate2DSpecificTests(); rt300@0: void generate3DSpecificTests(); rt300@0: void generate4DTests(); rt300@42: rt300@0: void generateSomeTests(int howManyTests, int dimensions, controlPanelType panelType, int whichSpace, bool scored = true,bool aHint = false, bool aMemoryTest = false); rt300@0: rt300@0: void generateTestSequence(){ rt300@0: rt300@0: }; rt300@0: //aregaergeargera rt300@0: vector testList; rt300@0: rt300@0: long scoreRunningTotal; rt300@0: rt300@0: rt300@0: }; rt300@0: rt300@0: rt300@0: rt300@0: rt300@0: #endif /* defined(__tweakathlon__TestController__) */