Mercurial > hg > tweakathon2ios
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__) */