/* Assignment 3 - Rock Paper Scissors by Steven Lehrburger and Armanda Lewis NYU ITP Sociable Objects 13 June 2008 This is the code for the two player Arduino/Xbee setups. There is separate code for the Scorekeeper. Be sure to change the player number constant when uploading to each Arduino! */ #define txLED 3 // LED to indicate outgoing data #define rxLED 4 // LED to indicate incoming data #define rockRxLED 5 // LED to indicate opponent's choice of Rock #define paperRxLED 6 // LED to indicate opponent's choice of Paper #define scissorsRxLED 7 // LED to indicate opponent's choice of Scissors #define rockInPin 8 // pin to determine if player has chosen Rock #define paperInPin 9 // pin to determine if player has chosen Paper #define scissorsInPin 10 // pin to determine if player has chosen Scissors #define winlossLEDPin 13 // LED to indicate the winner; between the two boards on-off indicates win, on-on indicates tie #define restartPin 12 // pin to determine if the player wants to begin a new game #define inputReceived 2 // LED to indicate that the opponent has made a choice and is waiting for the player to make a choise #define playerNum '1' // change this, depending on which board you are uploading to char scoreCount = 0; // keep track of the score, nothing happens in case of tie boolean opponentReady; // true if input has been received from the opponent, otherwise false boolean selfReady; // true if input has been received from the switches, otherwise false char opponentChoice; // input received from opponent char selfChoice; // input received from switches // we need the booleans AND the chars for this (as opposed to just checking to make sure the chars are valid values // in place of the booleans) because we only want to store the first valid input // actually, I am not sure this is still true, but having them makes it much easier to understand what is going on in the code. // function prototypes void setDestination(boolean forOpponent); void sendByte(char byteToSend, boolean useRorelseN); char getChoice(); void handleSerial(); boolean receivedDesiredChar(char desiredChar); int determineWinner(); void newGame(); void setup() { Serial.begin(9600); // configure serial communications: pinMode(txLED, OUTPUT); // configure input/output pins as described above pinMode(rxLED, OUTPUT); pinMode(rockRxLED, OUTPUT); pinMode(paperRxLED, OUTPUT); pinMode(scissorsRxLED, OUTPUT); pinMode(rockInPin, INPUT); pinMode(paperInPin, INPUT); pinMode(scissorsInPin, INPUT); pinMode(winlossLEDPin, OUTPUT); pinMode(restartPin, INPUT); pinMode(inputReceived, OUTPUT); blink(winlossLEDPin, 3); // blink the winloss LED indicating that the main program's about to start: newGame(); // initialize variables for a new game } void setDestination(boolean forOpponent) { Serial.flush(); // put the radio in command mode: delay(1010); Serial.print("+++"); delay(1010); char thisByte = 0; // wait for the radio to respond with "OK\r" while (thisByte != '\r') { if (Serial.available() > 0) { thisByte = Serial.read(); } } if (forOpponent) { if (playerNum == '1') { Serial.print("ATDH0, DL1234\r"); // addresses are different based on which player the code is being uploaded to } else { Serial.print("ATDH0, DL5678\r"); } } else { Serial.print("ATDH0, DL0202\r"); } if (playerNum == '1') { Serial.print("ATMY5678\r"); } else { Serial.print("ATMY1234\r"); } Serial.print("ATID1492\r"); Serial.print("ATCN\r"); } // Blink the specified LED the specified number of times void blink(int pinNum, int howManyTimes) { for (int i=0; i< howManyTimes; i++) { digitalWrite(pinNum, HIGH); delay(200); digitalWrite(pinNum, LOW); delay(200); } } void loop() { // first, check for input from the opponent if ((!opponentReady) && (Serial.available() >= 2)) { // listen for incoming serial data digitalWrite(rxLED, HIGH); // turn on the rx LED whenever you're reading data: handleSerial(); // determine what to do with the serial data digitalWrite(rxLED, LOW); // turn off the rx LED delay(500); // wait before continuing so players understand what is going on } // second, check for input from the switches selfChoice = getChoice(); if (selfReady) { // if the user is ready, send the value - note this is only ever true when selfChoice has a valid value sendByte(selfChoice, true); } // if input has been received from both, finish the game if (opponentReady && selfReady) { scoreCount += determineWinner(); // determine winner will return a 1 or a 0, depending on the inputs Serial.flush(); // flush the buffer - the game is over so this is now safe to do boolean bothRestarted = false; // now both players will wait until one hits the restart button, and then both need to restart boolean delayScoreSignal = false; while ((digitalRead(restartPin) == LOW) && !bothRestarted) {// so wait until one hits the button //if you receive an N, newGame(); if (receivedDesiredChar('N')) { // if the other player hits a button and sends you an N for (int i = 0; i < 10; i++) { // send back a D saying you got it (only 10 times, so that the 'goodbye-goodbye-goodbye problem' is avoided sendByte('D', true); } bothRestarted = true; // and this will break us out of this loop delayScoreSignal = true; // this is necessary so that the players do not both send their scores to the scorekeeper at the same time } // and give it a jumbled mess of values in the buffer } //if mine is pressed while (!receivedDesiredChar('D') && !bothRestarted) { // enter into this while loop if your button was pressed first sendByte('N', true); // and send Ns until you receive confirmation in the form of a D that the opponent received an N } if (delayScoreSignal) { delay(2500); } newGame(); // and restart the game. now we are ready to go back into the loop! } } void sendByte(char byteToSend, boolean useRorelseN) { // sends the byte to the opponent, the optional parameter allows the user to specify the protocol ending character digitalWrite(txLED, HIGH); // light the tx LED to say you're sending: Serial.print(byteToSend, BYTE); // send the choice delay(20); // wait a bit before sending another byte if (useRorelseN) { Serial.print('\r', BYTE); // send a carriage return to meet protocol } else { Serial.print('\n', BYTE); // or send a linefeed } digitalWrite(txLED, LOW); // turn off the tx LED delay(100); // wait before this is called again - mostly so we can see the light blink } char getChoice() { // this gets a choice from the player by checking the three switches if (selfReady) { // if we already have already indicated a choice and are ready, we cannot change our choice return selfChoice; } selfReady = true; // will be set back to false if none of the switches have been pressed if (digitalRead(rockInPin) == HIGH) { // if the rock switch is pressed, return R return 'R'; } else if (digitalRead(paperInPin) == HIGH) { // if the paper switch is pressed, return P return 'P'; } else if (digitalRead(scissorsInPin) == HIGH) { // if the scissors switch is pressed, return S return 'S'; } selfReady = false; // if a switch was pressed we would have returned by now, so set the boolean back to false return '0'; } void handleSerial() { // this reads the opponent input from the buffer (this only gets called when the buffer is not empty) char firstByte = Serial.read(); // read the input if ((firstByte == 'R') || (firstByte == 'P') || (firstByte == 'S')) { // if it is a valid input byte characte char secondByte = Serial.read(); // read the next byte if (secondByte == '\r') { // if it is a valid input byte character opponentChoice = firstByte; // store it opponentReady = true; // the opponent is ready and we have his value digitalWrite(inputReceived, HIGH); // turn on the waiting light } } } boolean receivedDesiredChar(char desiredChar) { // reads from the bufer for a desired character and a carriage return - super useful in the newgame while loops if (Serial.available() >= 2) { // we can't use this in getChoice though because there we have multiple different desiredChars char firstByte = Serial.read(); // read the input if ((firstByte == desiredChar)) { // is it what we want? char secondByte = Serial.read(); if (secondByte == '\r') { // if it is a valid input byte character return true; // return true } } } return false; // otherwise return false } int determineWinner() { // returns 0 for tie or a loss, 1 for win (from perspective of self) if (opponentChoice == 'R') { // first, indicate what the opponent chose with the appropriate light digitalWrite(rockRxLED, HIGH); } else if (opponentChoice == 'P') { digitalWrite(paperRxLED, HIGH); } else if (opponentChoice == 'S') { digitalWrite(scissorsRxLED, HIGH); } // then figure out who won: if (opponentChoice == selfChoice) { // if there was a tie digitalWrite(winlossLEDPin, HIGH); return(0); // neither score changes } if (((opponentChoice == 'R') && (selfChoice == 'P')) || // if you won ((opponentChoice == 'P') && (selfChoice == 'S')) || ((opponentChoice == 'S') && (selfChoice == 'R'))) { digitalWrite(winlossLEDPin, HIGH); if (scoreCount < 16) { // 16 is the max score, because of the scorekeeper only has 4 lights return 1; } else { blink(winLossLEDPin, 20); // blink a lot to indicate max score has been reached return 0; } } else { // otherwise, you lost digitalWrite(winlossLEDPin, LOW); return(0); } } void newGame() { // resets variables, sends the score to the scorekeeper, and starts a new game Serial.flush(); // this flush might be redundant, but it is safe to do anyways opponentReady = false; // reset the variables selfReady = false; opponentChoice = '0'; selfChoice = '0'; digitalWrite(winlossLEDPin, LOW); // turn off the lights digitalWrite(inputReceived, LOW); digitalWrite(rockRxLED, LOW); digitalWrite(paperRxLED, LOW); digitalWrite(scissorsRxLED, LOW); blink(txLED, 1); // do a blink pattern so the user knows what is going on blink(rxLED, 1); blink(rockRxLED, 1); blink(paperRxLED, 1); blink(scissorsRxLED, 1); setDestination(false); // and update the scores for (int i = 0; i < 5; i++) { // set the Xbee destination to the scorekeeper sendByte(playerNum, false); // send the player number, followed by a line feed sendByte(scoreCount, true); // and then the score for that player, followed by a carriage return } setDestination(true); // and set the destination back to the opponent }