/*
a1: delete code and constants for displays
a3: refactor code
a4: add in timer for replies
*/
// ***************************************************************set up compiler constants etc.
#include "pitches.h" // library of tones and tone functions
/* Constants - define pin numbers for LEDs,
buttons and speaker, and also the game tones: */
const uint8_t ledPins[] = {9, 10, 11, 12}; // name the various pins
const uint8_t buttonPins[] = {2, 3, 4, 5};
#define SPEAKER_PIN 8
#define MAX_GAME_LENGTH 100
const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5}; // create array of tones for each LED
// Global varaibles below:
uint8_t gameSequence[MAX_GAME_LENGTH] = {0}; // Set up array to hold sequence
uint8_t gameIndex = 0; // create index to count where we have got to in building sequence
unsigned long previousMillis = 0; // Store the last time we did something
unsigned long currentMillis =0;
const long interval = 3000; // Interval at which to do something (in milliseconds)
// ******************************************** SET UP ARDUINO BOARD initialize Serial communication
void setup() {
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);
// The following line primes the random number generator.
// It assumes pin A3 is floating (disconnected):
randomSeed(analogRead(A3));
}
// ****************************************************************** DEFINE FUNCTIONS
// =========== Lights the given LED and plays a suitable tone
void lightLedAndPlayTone(byte ledIndex) {
digitalWrite(ledPins[ledIndex], HIGH);
tone(SPEAKER_PIN, gameTones[ledIndex]);
delay(300);
digitalWrite(ledPins[ledIndex], LOW);
noTone(SPEAKER_PIN);
}
// ==== Plays the current sequence that the user has to repeat
void playSequence() {
for (int i = 0; i < gameIndex; i++) { // cycle through sequence
byte currentLed = gameSequence[i]; // identify next LED to light
lightLedAndPlayTone(currentLed); // light LED and play tone
delay(50);
}
}
// ========= Keeps checking for button press, return index
byte readButtons() {
// 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
}
// ==== Play the "game over" sequence, and report the game score
void gameOver() {
Serial.print("Game over! your score: ");
Serial.println(gameIndex - 1);
Serial.print("currentMillis: ");
Serial.println(currentMillis);
Serial.print("previousMillis: ");
Serial.println(previousMillis);
gameIndex = 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);
}
// ======== Get the user's input and compare it with the expected sequence.
bool checkUserSequence() {
for (int i = 0; i < gameIndex; i++) {
byte expectedButton = gameSequence[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;
}
// ===== Plays a hooray sound whenever the user finishes a level
void playLevelUpSound() {
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);
}
/* **************************************************************************** MAIN GAME LOOP */
void loop() { // loops for each round
// Add a random color to the end of the challenge sequence and play updated sequence
gameSequence[gameIndex] = random(0, 4);
gameIndex++;
if (gameIndex >= MAX_GAME_LENGTH) {
gameIndex = MAX_GAME_LENGTH - 1;
}
playSequence(); // start new round with challenge
// check user attempt
if (!checkUserSequence()) {
gameOver();
}
delay(300);
if (gameIndex > 0) {
playLevelUpSound();
delay(300);
}
}