//##########################################################################################################################################################################################################################
//### Initialize
//##########################################################################################################################################################################################################################
//MODULE 3 - SIMON SAYS
//#############################################################################################################
#include "pitches.h"
/* Constants - define pin numbers for LEDs,
buttons and speaker, and also the game tones: */
const byte ledPins[] = {22, 24, 26, 28};
const byte buttonPins[] = {23, 25, 27, 29};
#define SPEAKER_PIN 51
#define MAX_GAME_LENGTH 100
const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5};
/* Global variables - store the game state */
byte gameSequence[MAX_GAME_LENGTH] = {0};
byte gameIndex = 0;
byte userIndex = 0;
unsigned long lastActionTime = 0;
const unsigned long actionDelay = 300;
enum GameState { START_SEQUENCE, PLAY_SEQUENCE, USER_INPUT, GAME_OVER, LEVEL_UP };
GameState gameState = START_SEQUENCE;
//##########################################################################################################################################################################################################################
//### SETUP
//##########################################################################################################################################################################################################################
void setup() {
Serial.begin(9600);
//MODULE 3 - SIMON SAYS
for (byte i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(SPEAKER_PIN, OUTPUT);
// The following line primes the random number generator.
// It assumes pin A0 is floating (disconnected):
randomSeed(analogRead(A0));
}
//##########################################################################################################################################################################################################################
//### FUNCTIONS
//##########################################################################################################################################################################################################################
//#############################################################################################################
//### MODULE 3 - SIMON SAYS
//#############################################################################################################
// 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 of notes that the user has to repeat
void playSequence() {
static byte seqIndex = 0;
if (seqIndex < gameIndex) {
byte currentLed = gameSequence[seqIndex];
lightLedAndPlayTone(currentLed);
seqIndex++;
lastActionTime = millis();
} else {
seqIndex = 0;
gameState = USER_INPUT;
}
}
// Waits until the user pressed one of the buttons, and returns the index of that button
byte readButtons() {
for (byte i = 0; i < 4; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
return i;
}
}
return 255;
}
// Play the game over sequence, and report the game score
void gameOver() {
Serial.print("Game over! your score: ");
Serial.println(gameIndex - 1);
gameIndex = 0;
userIndex = 0;
// 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);
delay(500);
gameState = START_SEQUENCE;
}
// 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);
}
//##########################################################################################################################################################################################################################
//### Loop
//##########################################################################################################################################################################################################################
void loop() {
//MODULE 3 - SIMON SAYS
//#############################################################################################################
// Add a random color to the end of the sequence
switch (gameState) {
case START_SEQUENCE:
// Add a random color to the end of the sequence
gameSequence[gameIndex] = random(0, 4);
gameIndex++;
if (gameIndex >= MAX_GAME_LENGTH) {
gameIndex = MAX_GAME_LENGTH - 1;
}
gameState = PLAY_SEQUENCE;
break;
case PLAY_SEQUENCE:
if (millis() - lastActionTime >= actionDelay) {
playSequence();
}
break;
case USER_INPUT:
if (userIndex < gameIndex) {
byte button = readButtons();
if (button != 255) {
lightLedAndPlayTone(button);
if (gameSequence[userIndex] != button) {
gameState = GAME_OVER;
} else {
userIndex++;
}
}
} else {
gameState = LEVEL_UP;
}
break;
case GAME_OVER:
gameOver();
break;
case LEVEL_UP:
playLevelUpSound();
delay(300);
userIndex = 0;
gameState = START_SEQUENCE;
break;
}
}