view TestController.h @ 44:d810aa9ca03a

times. cosmetic stuff
author Robert Tubb <rt300@eecs.qmul.ac.uk>
date Mon, 15 Dec 2014 17:33:41 +0000
parents 2bd658b44c2d
children
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 "algorithms.h"
#include <functional>
#include <algorithm>    // std::transform

//-------------------------------------------------------------

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;
    };

    
    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(2, 6, REVISITABLE,1, true, false, false);
        generateSomeTests(2, 6, SIMULTANEOUS,1, true, 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__) */