/* 1D Pong 2.0! * 1 Dimensional Pong is now much more intelligent!! * Compatible with: NICTA ed1 board, or Arduino Duemilanove ATmega328 board * System requirements (if not NICTA ed1)(configure program according to your board design): * Potentiometer * LCD screen * Piezo buzzer * Two pushbuttons * LED shift register * * A PiAMP Production * * Instructions: * A menu will appear on the LCD screen. Navigate it using the potentiometer to * navigate to your choice and press D15 to select your choice. * * The Game: * Simply type in the name of each player in the Serial monitor and press D15 * to confirm that player, starting with Player 1 (D14), then Player 2 (D15). * Press D14 to re - enter the name. Then the game begins! * Once the light reaches your button, press it to reflect it back. But beware! * The more the light is reflected, the faster it will get! If you press the * button before the light reaches you, or after it reaches you, or not at all, * you will lose! (NOTE: If the light is travelling away from you, pressing the button * will not affect the game in any way whatsoever). Once the game is over, the accumulated * score will be counted and the program will determine the winner and display the winner's * name on the LCD screen. * * More instructions can be accessed through the menu. * Do you have what it takes to become a Pong champion??? * * The demo video can be accessed at this web address: http://www.archive.org/download/Pieman1DPong2/Creative_Challenge.m4v * NOTE: iTunes and/or QuickTime are required to view this video * This program is open - source under Creative Commons Attribution 2.5 Australia licensing. * Source code is available at: http://piemanvideocast.game-host.org/~andrew/The_Pieman_Videocast_Show/ * Programs/Entries/2009/10/8_pong2.html */ // ******************BEGIN GLOBAL DECLARATIONS********************** #include <LiquidCrystal.h> // LED Shift Register constants #define CLOCK (10) #define LATCH (11) #define DATA (12) // Piezo and pushbutton pin constants #define PIEZO (9) #define D14 (14) #define D15 (15) #define POT (3) // maximum serial input size constant #define MAXSIZE (17) // tone definitions in microseconds #define HalfPer250Hz 2000 // half period of tone #define HalfPer1000Hz 500 #define toneLength 500000 // play tone for 500ms // Defintions to make program easier to read #define D14_PRESS 0x01 // bit 1 #define D15_PRESS 0x02 // bit 2 #define BOTH_PRESS 0x03 // note both bots set #define M_LEFT -1 #define M_RIGHT 1 // GLOBAL VARIABLES LiquidCrystal lcd(6, 7, 8, 2, 3, 4, 5); // initialise LiquidCrystal object char name1[MAXSIZE]; // player 1 char name2[MAXSIZE]; // player 2 int n1; // size of name1 string int n2; // size of mane2 string int score1 = 0; // cumulative score for Player 1 int score2 = 0; // cumulative score for Player 2 // ******************END GLOBAL DECLARATIONS********************** // ******************BEGIN MAIN********************** // BEGIN EXECUTION void setup() { // initialise the piezo buzzer pinMode(PIEZO, OUTPUT); // initialise the LED shift register pinMode(DATA, OUTPUT); pinMode(LATCH, OUTPUT); pinMode(CLOCK, OUTPUT); // configure the push buttons and their pullup resistors pinMode(D14, INPUT); digitalWrite(D14, HIGH); pinMode(D15, INPUT); digitalWrite(D15, HIGH); // initialise the serial port and illuminate all LEDs ledWrite(255); Serial.begin(9600); // welcome screen displays only on initial start up lcd.clear(); lcd.print("Welcome to Pong!"); lcd.setCursor(0, 1); lcd.print("Now loading..."); delay(5000); } void loop() { menu(); // delegate execution to the menu function // display the initial score (ie. 0 for both players) score(score1, score2, name1, name2); // delegate execution to the score function // begin the game delegate(); // delegate execution to the main delegate function lcd.clear(); // display the scoreboard tally(); // delegate execution to the tally function } // ******************END MAIN********************** // ******************BEGIN DELEGATE FUNCTION********************** void delegate() // the gameplay function { int tone; int num_games = 0; for(;;) // loop infinitely until 9 rounds have passed { int dir; // M_RIGHT or M_LEFT int pos; // LED1 == 0, LED8 == 7 int play; // set to zero to halt game int period; // delay in ms for each LED light int b; // return from button function int count; // keep track of game progress // starting conditions for new game pos = 4; // LED5 dir = M_LEFT; play = 1; period = 1000; // play single game in while loop // exit at game end while(play) { // light up LED ledWrite(1 << pos); // pause and detect any button press b = detectButtonPress(period); // assign b to the return value of detectButtonPress // check for game state if ((pos == 0) && (!(b & D14_PRESS))) { // D14 lose as button not pressed play = 0; tone = HalfPer250Hz; score2++; } if ((pos == 7) && (!(b & D15_PRESS))) { // D15 lose as button not pressed play = 0 ; tone = HalfPer1000Hz; score1++; } // ignore D14 if motion is right if ((pos > 0) && (pos < 7) && (dir == M_RIGHT) && (b & D15_PRESS)) { // D15 lose play = 0; tone = HalfPer1000Hz; score1++; } // ignore D15 if motion is left if ((pos > 0) && (pos < 7) && (dir == M_LEFT) && (b & D14_PRESS)) { // D14 lose play = 0; tone = HalfPer250Hz; score2++; } // else play on pos += dir; // change direction if necessary if (pos == 0) { count++; // increment game progress dir = M_RIGHT; } if (pos == 7) dir = M_LEFT; // speed up by 5% period *= 0.95; } // while loop // only get here when someone loses ledWrite(0); playSound(tone); score(score1, score2, name1, name2); num_games++; if (num_games == 9) { break; // terminate loop and delay before returning // to calling function (ie. loop function) } } // delay before return delay(5000); } // ******************END DELEGATE FUNCTION********************** // ******************BEGIN EXCEPTION HANDLING********************** void assert(const char errorName[]) { char message[] = "Error: See Serial Monitor"; lcd.clear(); scroll(message, 25, 0); Serial.println(errorName); } // ******************END EXCEPTION HANDLING********************** // ******************BEGIN GENERIC FUNCTIONS********************** int getLine(char buffer[], int size) // for retrieval of player names { int n = 0; // loop variable int waittime = 50; // maximum time in ms to wait between characters while( Serial.available() && (n < (size-1))) { while( Serial.available() && (n < (size-1))) { buffer[n] = (char)Serial.read(); n = n + 1; } if (n < (size-1)) delay(waittime); } buffer[n] = NULL ; if ( (n == (size-1)) && Serial.available() ) n = size; // n contains number of characters placed into the buffer return(n); } void ledWrite(byte a) // LED Shift Register manipulation function { digitalWrite(LATCH, LOW); shiftOut(DATA, CLOCK, MSBFIRST, a); digitalWrite(LATCH, HIGH); } void tally() // scoreboard function { char separator[] = ": "; // closing message, constant, but must have periods inserted by scroller function char message[22] = "A PiAMP Production"; const char genericError[25] = "An error has occurred"; if (score1 < score2) { lcd.print(name2); lcd.print(" wins!"); delay(5000); lcd.clear(); } else if (score2 < score1) { lcd.print(name1); lcd.print(" wins!"); delay(5000); lcd.clear(); } else { lcd.clear(); assert(genericError); delay(500); } // print credits lcd.print("Thanks 4 playing"); // no need to scroll, 16 characters exactly lcd.setCursor(0, 1); lcd.print(message); delay(500); scroll(message, 18, 1); // delegate execution to the scroller function delay(1000); lcd.clear(); // program ends here, restarts with name input } void scroll(char news[], int n, int cursor) // the sentence scrolling function, can be interrupted with the press of a button. { int i; int j; int start; if (n <= 16) { news[n] = NULL; // terminate string lcd.print(news); } else { // add period characters for (i = n; i < (n + 3); i++) { news[i] = '.'; } n = i; // 'start' points to first character in 16 character // sub-string that will be display on the LCD start = 0; for(int k = 0; k <= n; k++) { // jump out if new string available lcd.setCursor(0, cursor); // print 16 characters starting at index 'start' j = start; for (i = 0; i < 16; i++) { lcd.print(news[j]); j++; // wrap if need be if (j >= n ) { j = 0; } } // move sub-string one character along start++; // if necessary, reset to start if (start >= n) { start = 0; } if (digitalRead(D14) == LOW || digitalRead(D15) == LOW) return; // delay between characters delay(250); } } } void score(int player1, int player2, char p1[], char p2[]) // prints the current score { lcd.clear(); lcd.print(p1); lcd.print(": "); lcd.print(player1); lcd.setCursor(0, 1); lcd.print(p2); lcd.print(": "); lcd.print(player2); } void menu() // displays the menu items according to mapped potentiometer input { int last_region = -1; int raw; int mapped; for (;;) { lcd.clear(); raw = analogRead(POT); mapped = map(raw, 0, 1023, 2, 0); switch (mapped) // allows region 0 to operate as region 1 { case (0): // FALL THROUGH case (1): lcd.clear(); lcd.print("1. Play Game"); if (digitalRead(D15) == LOW) { lcd.setCursor(0, 1); lcd.print("Selected: "); if (mapped == 0) lcd.print("1"); else lcd.print(mapped); delay(100); lcd.clear(); gameSetup(); return; } break; case (2): lcd.print("2. Instructions"); if (digitalRead(D15) == LOW) { lcd.setCursor(0, 1); lcd.print("Selected: "); lcd.print(mapped); delay(100); lcd.clear(); instructions(); break; } break; default: assert("Invalid region"); break; } delay(100); } } void instructions() // displays the instructions for 1D Pong 2.0 { char notice[] = "See Serial Monitor"; scroll(notice, 18, 0); Serial.println("Welcome to Pong Reinvented!"); Serial.println("A game of skill and hand - eye coordination. Do you have what it takes to win?"); Serial.println(); Serial.println("Controls:"); Serial.println("In - Game:"); Serial.println("D14: Player 1, D15: Player 2"); Serial.println("Menus:"); Serial.println("Turn potentiometer to navigate menus, press D15 to select an item"); Serial.println(); Serial.println("Playing the game:"); Serial.println("To reflect the ball, the player must press the button when the LED directly"); Serial.println("beside their button is lit up. If it is pressed otherwise, that player will lose."); Serial.println("If a player wins 5 rounds, they win the game. Both scores will be saved to the scoreboard."); Serial.println(); Serial.print("Press any button to return to the menu..."); while (digitalRead(D15) == HIGH && digitalRead(D14) == HIGH) ; if (digitalRead(D14) == LOW || digitalRead(D15) == LOW) return; } // ******************END GENERIC FUNCTIONS********************** // ******************BEGIN GAME FUNCTIONS********************** void playSound(int halfTone) // play the loss tone { int iterations; int i; iterations = toneLength / (halfTone * 2); for(i = 0; i < iterations; i++) { digitalWrite(PIEZO, HIGH); delayMicroseconds(halfTone); digitalWrite(PIEZO, LOW); delayMicroseconds(halfTone); } lcd.clear(); } /* * Routine to detect press of a button within * passed time period * * Returns pin number of pressed button or: * 0 for no buttons pressed * D14_PRESS (1) for d14 button pressed * D15_PRESS (2) for d15 button pressed * D14_PRESS & D15_PRESS (3) for both buttons pressed */ int detectButtonPress(int waitFor) { unsigned long waitTill; int buttons = 0; waitTill = millis() + (unsigned long)waitFor; // wait for button to be pressed while(millis() < waitTill) { buttons |= (digitalRead(D14) == LOW) ? D14_PRESS : 0; buttons |= (digitalRead(D15) == LOW) ? D15_PRESS : 0; } // note, have not checked for de-press return (buttons); } void gameSetup() // sets up the player names for the game { // Button prompt constant, not declared const char[] or #define as periods // must be inserted into string by scroller function char buttons[29] = "D15: Confirm, D14: Cancel"; // loop indefinitely for player 1 name input (terminated if confirmed). for (;;) { lcd.clear(); lcd.print("Player 1:"); while (!Serial.available()) ; n1 = getLine(name1, MAXSIZE); // get player 1 name from serial input lcd.clear(); lcd.print(name1); lcd.setCursor(0, 1); lcd.print(buttons); delay(500); scroll(buttons, 25, 1); while (digitalRead(D15) == HIGH && digitalRead(D14) == HIGH) ; if (digitalRead(D15) == LOW) { break; // terminate loop and move to player 2 input } else if(digitalRead(D14) == LOW) // this may be redundant { continue; // restart loop } } // loop indefinitely for player 2 name input (terminated if confirmed) for(;;) { lcd.clear(); lcd.print("Player 2:"); while (!Serial.available()) ; n2 = getLine(name2, MAXSIZE); // get player 2 name from serial input lcd.clear(); lcd.print(name2); lcd.setCursor(0, 1); lcd.print(buttons); delay(500); scroll(buttons, 25, 1); while (digitalRead(D15) == HIGH && digitalRead(D14) == HIGH) ; if (digitalRead(D15) == LOW) { break; // terminate loop and begin game } else if (digitalRead(D14) == LOW) // this may be redundant { continue; // restart loop } } return; } // ******************END GAME FUNCTIONS********************** |
NICTA ed1 >