/**
Simon Game for Arduino
The physical layout of the game setup is based on the following:
https://wokwi.com/arduino/libraries/demo/simon-game
*/
/* headers */
#include "pitches.h"
/* symbolic constants */
#define TRUE 1
#define FALSE 0
#define NULL 0
#define MAX_ROUND_NUMBER 5
#define SEC 1000
#define EXPECT_MORE_INPUT 0
#define EXPECT_NO_MORE_INPUT 1
#define WRONG 2
/* Color indexes */
#define GREEN 0
#define BLUE 1
#define YELLOW 2
#define RED 3
/* Define the pins for LEDs and buttons */
#define GREEN_BTN 4
#define BLUE_BTN 3
#define YELLOW_BTN 2
#define RED_BTN 5
#define STOP_BTN 7
#define GREEN_LED 11
#define BLUE_LED 10
#define YELLOW_LED 9
#define RED_LED 12
#define SPEAKER_PIN 8
#define QUIT 0
#define CORRECT 1
#define WRONG 2
#define NO_BUTTON_WAS_PRESSED 99
#define NUMBER_OF_BUTTONS 5
#define MAX_WAIT_TIME SEC*15
#define MAX_NO_MORE_INPUT_TIME SEC*2
#define FLASH_TIME_INTERVAL SEC*1
#define ROUND_INTERVAL SEC*0.5
#define GAME_START_FLASH_INTERVAL SEC*0.5
#define BUTTON_SOUND_LENGTH SEC*0.5
/*Result of shouldContinueToNextRound*/
#define CONTINUE 1
#define RESTART 2
/* The array that contains the pin number of all the buttons/leds */
/* Indexed by the color */
const int buttons[NUMBER_OF_BUTTONS] = {GREEN_BTN, BLUE_BTN, YELLOW_BTN, RED_BTN, STOP_BTN};
const int leds[] = {GREEN_LED, BLUE_LED, YELLOW_LED, RED_LED};
/* The array that contains the tones of all the colors */
/* Indexed by the color */
/* The 5th is an empty sound for the stop button*/
const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5, NOTE_B0};
/* sorry, unimplemented: non-trivial designated initializers not supported */
//const int btns2toneIndex[] = {[GREEN_BTN] = GREEN, [BLUE_BTN] = BLUE, [YELLOW_BTN] = YELLOW, [RED_BTN] = RED, [STOP_BTN] = 5};
const int btns2toneIndex[] = {0, 0, 2, 1, 0, 3, 0, 5};
/* work function */
void runSimon(void);
int start();
void checkTimer();
/* work function */
void runSimon() {
Serial.println("\nrun Simon...");
/* Initialize the random number generator*/
randomSeed(micros());
start();
}
/*
Generate a random number between lower and upper, inclusive
*/
int randInt(int lower, int upper) {
return random(upper - lower + 1) + lower;
}
/*
Fill data[] with random values between 0 and 3, inclusive
*/
void generateSequence(int length, int data[]) {
for (int i = 0; i < length; i++) {
data[i] = randInt(0, 3);
}
}
int isUserQuit() {
return digitalRead(STOP_BTN) == LOW;
}
int isTimeOver(unsigned long startTime, unsigned long milliseconds) {
unsigned long currentTime = millis();
return ((currentTime - startTime) > milliseconds);
}
int isTheButtonPressed(int buttonPin) {
if (digitalRead(buttonPin) == LOW) {
while (digitalRead(buttonPin) == LOW) {
/*Do nothing*/
}
/* Now the button is released */
return TRUE;
}
return FALSE;
}
/* Return the pin number of the button that was pressed or NO_BUTTON_WAS_PRESSED */
int detectThePressedButton() {
for (int i = 0; i < NUMBER_OF_BUTTONS; i++) {
if (isTheButtonPressed(buttons[i])) {
return buttons[i];
}
}
return NO_BUTTON_WAS_PRESSED;
}
/*
- Returns
- QUIT?
- CORRECT?
- WRONG?
*/
int checkUserAnswers(int correctAnswers[], int numberOfCorrectAnswers) {
unsigned long startTime = millis();
if (isUserQuit()) {
return QUIT;
}
return checkUserInput(correctAnswers, numberOfCorrectAnswers, startTime, 0);
}
/*
Returns
- EXPECT_NO_MORE_INPUT
- EXPECT_MORE_INPUT
- WRONG
*/
int checkAnswer(int correctAnswers[], int numberOfCorrectAnswers, int i, int currentButton) {
int correctAnswer = correctAnswers[i];
int correctAnswerBtn = buttons[correctAnswer];
if (correctAnswerBtn == currentButton) {
if (numberOfCorrectAnswers == i + 1) {
return EXPECT_NO_MORE_INPUT;
}
return EXPECT_MORE_INPUT;
}
return WRONG;
}
int expectNoMoreInput(unsigned long noMoreInputStartTime) {
int detectThePressedButtonResult;
detectThePressedButtonResult = detectThePressedButton();
if (detectThePressedButtonResult == STOP_BTN) {
return QUIT;
}
if (detectThePressedButtonResult == GREEN_BTN ||
detectThePressedButtonResult == RED_BTN ||
detectThePressedButtonResult == YELLOW_BTN ||
detectThePressedButtonResult == BLUE_BTN ) {
playBtnSound(detectThePressedButtonResult);
return WRONG;
}
if (isTimeOver(noMoreInputStartTime, MAX_NO_MORE_INPUT_TIME)) {
return CORRECT;
}
return expectNoMoreInput(noMoreInputStartTime);
}
void playBtnSound(int btnPin) {
tone(SPEAKER_PIN, gameTones[btns2toneIndex[btnPin]]);
delay(BUTTON_SOUND_LENGTH);
noTone(SPEAKER_PIN);
}
/*
@param i The index of the current answer to check
*/
int checkUserInput(int correctAnswers[], int numberOfCorrectAnswers, unsigned long startTime, int i) {
int detectThePressedButtonResult = NO_BUTTON_WAS_PRESSED;
int checkAnswerResult = 100;
unsigned long noMoreInputStartTime;
if (!isTimeOver(startTime, MAX_WAIT_TIME)) {
detectThePressedButtonResult = detectThePressedButton();
if (detectThePressedButtonResult == GREEN_BTN ||
detectThePressedButtonResult == RED_BTN ||
detectThePressedButtonResult == YELLOW_BTN ||
detectThePressedButtonResult == BLUE_BTN ) {
playBtnSound(detectThePressedButtonResult);
checkAnswerResult = checkAnswer(correctAnswers, numberOfCorrectAnswers, i, detectThePressedButtonResult);
}
}
if (detectThePressedButtonResult == STOP_BTN) {
return QUIT;
}
if (detectThePressedButtonResult == NO_BUTTON_WAS_PRESSED && !isTimeOver(startTime, MAX_WAIT_TIME)) {
return checkUserInput(correctAnswers, numberOfCorrectAnswers, startTime, i);
}
if (checkAnswerResult == EXPECT_MORE_INPUT) {
return checkUserInput(correctAnswers, numberOfCorrectAnswers, startTime, i + 1);
}
if (checkAnswerResult == EXPECT_NO_MORE_INPUT) {
noMoreInputStartTime = millis();
return expectNoMoreInput(noMoreInputStartTime);
}
if (isTimeOver(startTime, MAX_WAIT_TIME) || checkAnswerResult == WRONG) {
return WRONG;
}
}
void flashLEDs(int colorsArr[], int n, int time_interval) {
int i;
int color_i;
for (i = 0; i < n; i++) {
color_i = colorsArr[i];
digitalWrite(leds[color_i], HIGH);
tone(SPEAKER_PIN, gameTones[color_i]);
delay(time_interval / 2);
digitalWrite(leds[color_i], LOW);
noTone(SPEAKER_PIN);
if (i == n - 1) {
// Skip the final delay
return ;
}
delay(time_interval / 2);
}
}
void showStartSignals() {
int i;
int n = 3;
for (i = 0; i < n; i++) {
digitalWrite(GREEN_LED, HIGH);
digitalWrite(BLUE_LED, HIGH);
digitalWrite(YELLOW_LED, HIGH);
digitalWrite(RED_LED, HIGH);
delay(GAME_START_FLASH_INTERVAL);
digitalWrite(GREEN_LED, LOW);
digitalWrite(BLUE_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
digitalWrite(RED_LED, LOW);
delay(GAME_START_FLASH_INTERVAL);
}
}
void showWinSignals() {
// https://wokwi.com/arduino/libraries/demo/simon-game 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);
}
void showLoseSignals() {
// https://wokwi.com/arduino/libraries/demo/simon-game gameOver
// 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 (int 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);
}
int shouldContinueToNextRound(int nextRoundNumber) {
if (nextRoundNumber <= MAX_ROUND_NUMBER) {
return CONTINUE;
}
return RESTART;
}
int correct(int nextRoundNumber, int correctAnswers[]) {
int shouldContinueToNextRoundResult;
Serial.println("Correct\n");
showWinSignals();
shouldContinueToNextRoundResult = shouldContinueToNextRound(nextRoundNumber);
delay(ROUND_INTERVAL);
if (shouldContinueToNextRoundResult == CONTINUE) {
return newRound(correctAnswers, nextRoundNumber);
}
if (shouldContinueToNextRoundResult == RESTART) {
return start();
}
}
int wrong(){
Serial.println("Wrong\n");
showLoseSignals();
delay(ROUND_INTERVAL);
return start();
}
int newRound(int correctAnswers[], int currentRoundNumber) {
int checkUserAnswersResult;
flashLEDs(correctAnswers, currentRoundNumber, FLASH_TIME_INTERVAL);
checkUserAnswersResult = checkUserAnswers(correctAnswers, currentRoundNumber);
// Serial.println("\ncheckUserAnswersResult: ");
// Serial.println(checkUserAnswersResult);
if (checkUserAnswersResult == QUIT) {
return gameEnd();
}
if (checkUserAnswersResult == CORRECT) {
return correct(currentRoundNumber + 1, correctAnswers);
}
if (checkUserAnswersResult == WRONG) {
return wrong();
}
}
int start() {
int currentRoundNumber = 1;
int correctAnswers[MAX_ROUND_NUMBER];
generateSequence(MAX_ROUND_NUMBER, correctAnswers);
Serial.println("\nStart the game...");
// Serial.println("correctAnswers: ");
// for (int i = 0; i < MAX_ROUND_NUMBER; i++) {
// Serial.print(correctAnswers[i]);
// Serial.print(" ");
// }
showStartSignals();
return newRound(correctAnswers, currentRoundNumber);
}
int gameEnd() {
Serial.println("\nGame ended...");
return QUIT;
}
void setup()
{
Serial.begin(9600);
// Serial.println("setup");
/* Set all the LEDs andd button pins */
pinMode(GREEN_BTN, INPUT_PULLUP);
pinMode(BLUE_BTN, INPUT_PULLUP);
pinMode(YELLOW_BTN, INPUT_PULLUP);
pinMode(RED_BTN, INPUT_PULLUP);
pinMode(STOP_BTN, INPUT_PULLUP);
pinMode(GREEN_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(SPEAKER_PIN, OUTPUT);
runSimon();
}
void loop()
{
// runSimon();
}