/* Version information
a1: delete code and constants for displays
a3: refactor code
a4: add in timer for replies
a5: add pushbutton to start game
a6: revise to correct bug when first starting
a7: set target for number of rounds to get gate to open
a8: gate opening and closing routines, refactor code
a9: LED chaser for initiation module halted by start button
a10: Add mode selection switch to read game mode and set game modes for 3 or 5 rounds
a11: routine to read game mode, rotarty switch can inhibit game start when in position 0
a12: routine for "straight in" game mode 3
*/
/* define nicknames or aliases for things and values for constants */
#include "pitches.h" // library of tones and tone functions
#define SPEAKER_PIN 8
#define GATE_PIN 13
#define MAX_GAME_LENGTH 100
/* declare pin numbers for LEDs, buttons etc. as constants */
const uint8_t ledPins[] = {9, 10, 11, 12}; // name the various pins
const int startButtonPin = 6;
const uint8_t buttonPins[] = {2, 3, 4, 5};
const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5}; // create array of tones for each LED
/* declare global variables - "celebs" */
uint8_t displaySequence[MAX_GAME_LENGTH] = {0}; // Set up array to hold sequence
uint8_t gameLevel = 0; // create index to identify which round we are on
unsigned long previousMillis = 0; // Store the last time we did something
unsigned long currentMillis =0;
long interval = 3000; // time allowance for choosing which button to press
byte goal = 3; // number of rounds to complete to open gate
byte gameMode = 0; // Records game mode as selected by rotary switch
// ******************************************** SET UP ARDUINO BOARD, initialize Serial communication, define functions
void setup() {//set up serial communiacations and set up hardware pins on Arduino
Serial.begin(9600);
for (byte i = 0; i < 4; i++) { // set up pins
pinMode(ledPins[i], OUTPUT);
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(SPEAKER_PIN, OUTPUT);
digitalWrite(GATE_PIN, LOW);
pinMode(GATE_PIN, OUTPUT);
pinMode(startButtonPin, INPUT_PULLUP);
pinMode(A0, INPUT_PULLUP); // Set A0 as a digital input
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
pinMode(A4, INPUT_PULLUP);
pinMode(A7, INPUT);
// The following line primes the random number generator.
// It assumes pin A7 is floating (disconnected):
randomSeed(analogRead(A7));
}
//----------------------------------------------------------------------------------------------------------
void lightLedAndPlayTone(byte ledIndex) {// Lights the given LED and plays a suitable tone
digitalWrite(ledPins[ledIndex], HIGH);
tone(SPEAKER_PIN, gameTones[ledIndex]);
delay(250);
digitalWrite(ledPins[ledIndex], LOW);
noTone(SPEAKER_PIN);
delay(250);
}
//-------------------------------------------------------------------------------------------------------------
void playSequence() {// Plays the current sequence that the user has to repeat
for (int i = 0; i < gameLevel; i++) { // cycle through sequence
byte currentLed = displaySequence[i]; // identify next LED to light
lightLedAndPlayTone(currentLed); // light LED and play tone
}
}
//-------------------------------------------------------------------------------------------------------
byte readButtons() {// Keeps checking for button press, return index
// set previous = current
currentMillis = millis(); // Get the current time
previousMillis = currentMillis; //reset starting time
// true test for next statement: (currentMillis-previousMillis) < interval
while ((currentMillis-previousMillis) < interval) { // Current - previous less than pause allowance
for (byte i = 0; i < 4; i++) { // poll pushbottons to check if one pushed
byte buttonPin = buttonPins[i];
if (digitalRead(buttonPin) == LOW) { // if pressed, exit function returning the button id
return i;
}
}
delay(1);
currentMillis = millis(); //update current time before repeatng loop
}
// return with "i" set to 4 i.e. illegal to indicate timeout and force a fail when compared to correct answer
return 5; //to cause fail
}
//-------------------------------------------------------------------------------------------------------
void gameOver() {// Play the "game over" tune, and report the game score
/*
Serial.print("Game over! your score: ");
Serial.println(gameLevel - 1);
Serial.print("currentMillis: ");
Serial.println(currentMillis);
Serial.print("previousMillis: ");
Serial.println(previousMillis);
*/
gameLevel = 0;
delay(200);
// Play a Wah-Wah-Wah-Wah sound
tone(SPEAKER_PIN, NOTE_DS5);
delay(300);
tone(SPEAKER_PIN, NOTE_D5);
delay(300);
tone(SPEAKER_PIN, NOTE_CS5);
delay(300);
for (byte i = 0; i < 10; i++) {
for (int pitch = -10; pitch <= 10; pitch++) {
tone(SPEAKER_PIN, NOTE_C5 + pitch);
delay(5);
}
}
noTone(SPEAKER_PIN);
}
//----------------------------------------------------------------------------------------------------------
bool checkUserSequence() { // Get the user's input and compare it with the expected sequence.
for (int i = 0; i < gameLevel; i++) {
byte expectedButton = displaySequence[i]; // get next in sequence
byte actualButton = readButtons(); // poll for pushbutton entry
Serial.println(actualButton);
if (actualButton == 5) {
return false;
}
lightLedAndPlayTone(actualButton); // light approprite LED
if (expectedButton != actualButton) { // test user entry
return false;
}
}
return true;
}
void playLevelUpSound() {// Plays a hooray sound whenever the user finishes a level
tone(SPEAKER_PIN, NOTE_E4);
delay(150);
tone(SPEAKER_PIN, NOTE_G4);
delay(150);
tone(SPEAKER_PIN, NOTE_E5);
delay(150);
tone(SPEAKER_PIN, NOTE_C5);
delay(150);
tone(SPEAKER_PIN, NOTE_D5);
delay(150);
tone(SPEAKER_PIN, NOTE_G5);
delay(150);
noTone(SPEAKER_PIN);
}
void openGate(){
digitalWrite(GATE_PIN, HIGH);//open gate
tone(SPEAKER_PIN, NOTE_C7);
delay(150);
noTone(SPEAKER_PIN);
delay(150);
tone(SPEAKER_PIN, NOTE_C7);
delay(150);
noTone(SPEAKER_PIN);
}
void closeGate(){
for (int i = 0; i < 3; i++) {
// Code to be repeated
tone(SPEAKER_PIN, NOTE_C5);
delay(150);
noTone(SPEAKER_PIN);
delay(150);
}
digitalWrite(GATE_PIN, LOW);//close gate
}
void ledChaser(){
//loop though each LED in turn then reset and start again
byte i = 0;
while(true) { // this is an endless loop until the break statement is run
digitalWrite(ledPins[i], HIGH);
delay(75);
digitalWrite(ledPins[i], LOW);
delay(75);
if (digitalRead(startButtonPin) == LOW) { // if pressed, exit functidling state to start game
readGameMode(); // and get game mode setting from rotary switch
if(gameMode!=0) {
delay (400); //to allow user to get ready to spot first led
break;
}
}
i++;
if(i>3){i=0;}
}
}
void readGameMode(){
if(digitalRead(A0)==0) {gameMode=0;} //when switch is activated, input goes low
else if(digitalRead(A1)==0) {gameMode=1;goal=3;}
else if(digitalRead(A2)==0) {gameMode=2;goal=5;}
else if(digitalRead(A3)==0) {gameMode=3;goal=5;} //straight into final sequence game
else if(digitalRead(A4)==0) {gameMode=4;goal=100;} //i.e. play conventioal multiplayer game
else gameMode=0; // no correct input from rotary switch
Serial.println(gameMode);
Serial.println("Test");
}
/* **************************************************************************** MAIN GAME LOOP *** */
void loop() { //this loops for each round of a game
/*** idle mode awaiting inputs ***/
// If game (re)starting i.e. gameLevel=0 run ledChaser whilst waiting user input(start and game mode)
if(gameLevel == 0) {
ledChaser();
}
/* Display new game sequence if in normal build up the sequence mode*/
if (gameMode!=3) { // i.e. its normal build up mode
displaySequence[gameLevel] = random(0, 4); // Add a random color to the end of the challenge sequence and play updated sequence
gameLevel++;
if (gameLevel >= MAX_GAME_LENGTH) {
gameLevel = MAX_GAME_LENGTH - 1;
}
playSequence(); // start new round with challenge
}
if (gameMode==3){ //this is the game version for the straight-in vs build up routine
for (int i = 0; i < 5; i++) {
displaySequence[i] = random(0, 4); //this creates a 5 digit random sequence in the form of an array
}
gameLevel=5;
for (int i=0; i<5;i++){
Serial.print(displaySequence[i]);
}
Serial.println("");
playSequence();
delay(500);
}
/* Read user input and check against target sequence */
if (!checkUserSequence()) {
gameOver();
}
delay(300);
if (gameLevel == goal) {
// do code for opening gate and reset game counter to zero ready for restart
openGate();
delay(3000);
closeGate();
gameLevel = 0;
}
else {
playLevelUpSound();
delay(300);
}
}