4 way timer game

Hey guys,

I'm just beginning a new project. Its a game where 4 players are given a time, for example 10 seconds, and once the timer starts they have to press a button when they think 10 seconds has passed. The arduino has to display there times and then say who the closest was by lighting up an LED.

I know that coding a simple stopwatch isnt too challenging, so im told, just not sure how to have 4 stopwatches running at the same time and then have arduino interpret the data and pick a winner. Could any of you guys point me in the right direction? Im still trying to learn millis so maybe this is the time to really knuckle down.

Cheers guys

See if you can make heads or tails of this. This code relies on pin interrupts; not all Arduino models have 4 available interrupts (my 2560 does). If yours doesn't, just change it to poll the switches:

//define a limit for waiting for all players, 20-seconds in this case
#define GAME_TIME_LIMIT     20000L

//these are the player button pins
//these were used on a Mega2560
//make sure the pins you use have pin interrupts available
const int PLYR_1_BTN = 2;
const int PLYR_2_BTN = 3;
const int PLYR_3_BTN = 18;
const int PLYR_4_BTN = 19;

unsigned long
    MasterTimer,
    timePlayer[4];
byte
    bPressFlags;
        
void setup()
{
    //we need the serial window for interaction so wait for it
    Serial.begin(9600);
    while(!Serial);

    //set up the buttons as input-pullups. Pushing a button should ground it
    pinMode( PLYR_1_BTN, INPUT_PULLUP );
    pinMode( PLYR_2_BTN, INPUT_PULLUP );
    pinMode( PLYR_3_BTN, INPUT_PULLUP );
    pinMode( PLYR_4_BTN, INPUT_PULLUP );

    //each button will cause an interrupt on a falling edge (e.g. a press)
    //map each button to its own handler
    attachInterrupt(digitalPinToInterrupt(PLYR_1_BTN), ISR_PLYR_1, FALLING);
    attachInterrupt(digitalPinToInterrupt(PLYR_2_BTN), ISR_PLYR_2, FALLING);
    attachInterrupt(digitalPinToInterrupt(PLYR_3_BTN), ISR_PLYR_3, FALLING);
    attachInterrupt(digitalPinToInterrupt(PLYR_4_BTN), ISR_PLYR_4, FALLING);

    //reset the game and start
    ResetGame();
    
}//setup

void ResetGame( void )
{    
    Serial.println( "\n\n*** Begin timing!!! ***" );
    //we start the master timer here
    MasterTimer = millis();

    //this byte carries the user button press info
    //in bits 0..3
    //bit 0 = 1 means player 1 hit his button
    //bit 1 = 1 means player 2 hit his button
    //bit 2 = 1 means player 3 hit his button
    //bit 3 = 1 means player 4 hit his button
    //sero it at the start; each ISR will set its own bit
    bPressFlags = 0x00;
    
}//ResetGame

void loop()
{
    unsigned long
        timeNow;
        
    //let the game run until all four have pressed a button or some arbitrarily long timeout has elapsed
    timeNow = millis();
    if( ((timeNow - MasterTimer) > GAME_TIME_LIMIT) || (bPressFlags == 0x0F) )
    {
        ShowResults();
        ResetGame();
        
    }//if
        
}//loop

void ShowResults( void )
{
    byte
        plyr,
        ClosestPlayer;
    unsigned long
        diff,
        ClosestTime;
    unsigned long
        timeDelta[4];
    float
        dispTime;

    //if no one pressed a button, say so
    if( bPressFlags == 0x00 )
    {
        Serial.println( "No one played the game!" );
        
    }//if
    else
    {
        //ClosestTime will contain the smallest difference between user times and 10-seconds from the MasterTimer
        //init with a huge value for comparison reasons
        ClosestTime = 10000000L;

        //check each player's results
        for( plyr=0; plyr<4; plyr++ )
        {
            Serial.print( "Player " ); Serial.print(plyr+1); Serial.print( " time: " );

            //if the player hit his button, a bit will be set in flags
            if( bPressFlags & (1<<plyr) )
            {
                //plyr hit the button...
                //find his time difference and make a value we can display to 3-digits (i.e. a float)
                timeDelta[plyr] = timePlayer[plyr] - MasterTimer;
                dispTime = (float)timeDelta[plyr] / 1000.0;
                Serial.println( dispTime, 3 );  
            }
            else
            {
                //bit wasn't set player hadn't hit his button
                Serial.println( "No time recorded. " );
            }//else
             
        }//for

        //go through and see who won
        for( plyr=0; plyr<4; plyr++ )
        {
            //only players that pressed their buttons are checked
            if( bPressFlags & (1<<plyr) )
            {
                //how close to 10-seconds?
                //could be over or under; this is crude but makes it easy
                if( timeDelta[plyr] >= 10000 )
                    diff = timeDelta[plyr] - 10000;
                else
                    diff = 10000 - timeDelta[plyr];

                //if the current players time is smaller than the current "closest" time, make that
                //the new closest and record the player number
                if( diff < ClosestTime )
                {
                    ClosestPlayer = plyr;
                    ClosestTime = diff;
                     
                }//if
                
            }//if
            
        }//for

        //print the results
        Serial.print( "The winner is player " ); Serial.print( ClosestPlayer + 1 ); Serial.print( " with an error of " );
        Serial.println( (float)(ClosestTime / 1000.0), 3 );
    }//else

    //use the serial console to re-start. Could also use any of the player buttons
    Serial.println( "\n\nPress any key to play again." );
    //wait for a character, then flush
    while( Serial.available() <= 0 );
    while( Serial.available() > 0 )
        Serial.read();

}//ShowResults

void ISR_PLYR_1( void )
{
    //if a button press has already been recorded, just leave (bounce etc)
    if( bPressFlags & 0x01 )
        return;

    //record the player's button press time
    timePlayer[0] = millis();
    //and set his flag indicating he played
    bPressFlags |= 0x01;
    
}//ISR_PLYR_1

void ISR_PLYR_2( void )
{
    if( bPressFlags & 0x02 )
        return;
        
    timePlayer[1] = millis();
    bPressFlags |= 0x02;
    
}//ISR_PLYR_2

void ISR_PLYR_3( void )
{
    if( bPressFlags & 0x04 )
        return;
        
    timePlayer[2] = millis();
    bPressFlags |= 0x04;
    
}//ISR_PLYR_3

void ISR_PLYR_4( void )
{
    if( bPressFlags & 0x08 )
        return;
        
    timePlayer[3] = millis();
    bPressFlags |= 0x08;
    
}//ISR_PLYR_4

Wow thank you so much blackfin, that is so much more than I was expecting.

Ive scanned through a couple of times and I might check in with a few questions about areas I struggle to understand but I get the principle I think. Im just reading into flags now.

If you made this circuit yourself, do you have any reference images of how you did it? Also Im able to do this with most kinds of LCD/LED screens arnt I, If I adjust the libraries accordingly