203 lines
4.3 KiB
C++
203 lines
4.3 KiB
C++
/**
|
|
Simon Game for Arduino with Score display
|
|
|
|
Copyright (C) 2022, Uri Shaked
|
|
|
|
Released under the MIT License.
|
|
*/
|
|
|
|
#include "pitches.h"
|
|
|
|
/* Constants - define pin numbers for LEDs,
|
|
buttons and speaker, and also the game tones: */
|
|
const uint8_t ledPins[] = {9, 10, 11, 12};
|
|
const uint8_t buttonPins[] = {2, 3, 4, 5};
|
|
#define SPEAKER_PIN 8
|
|
|
|
// These are connected to 74HC595 shift register (used to show game score):
|
|
const int LATCH_PIN = A1; // 74HC595 pin 12
|
|
const int DATA_PIN = A0; // 74HC595pin 14
|
|
const int CLOCK_PIN = A2; // 74HC595 pin 11
|
|
|
|
#define MAX_GAME_LENGTH 100
|
|
|
|
const int gameTones[] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5};
|
|
|
|
/* Global variables - store the game state */
|
|
uint8_t gameSequence[MAX_GAME_LENGTH] = {0};
|
|
uint8_t gameIndex = 0;
|
|
|
|
/**
|
|
Set up the Arduino board and initialize Serial communication
|
|
*/
|
|
void setup() {
|
|
Serial.begin(9600);
|
|
for (byte i = 0; i < 4; i++) {
|
|
pinMode(ledPins[i], OUTPUT);
|
|
pinMode(buttonPins[i], INPUT_PULLUP);
|
|
}
|
|
pinMode(SPEAKER_PIN, OUTPUT);
|
|
pinMode(LATCH_PIN, OUTPUT);
|
|
pinMode(CLOCK_PIN, OUTPUT);
|
|
pinMode(DATA_PIN, OUTPUT);
|
|
|
|
// The following line primes the random number generator.
|
|
// It assumes pin A3 is floating (disconnected):
|
|
randomSeed(analogRead(A3));
|
|
}
|
|
|
|
/* Digit table for the 7-segment display */
|
|
const uint8_t digitTable[] = {
|
|
0b11000000,
|
|
0b11111001,
|
|
0b10100100,
|
|
0b10110000,
|
|
0b10011001,
|
|
0b10010010,
|
|
0b10000010,
|
|
0b11111000,
|
|
0b10000000,
|
|
0b10010000,
|
|
};
|
|
const uint8_t DASH = 0b10111111;
|
|
|
|
void sendScore(uint8_t high, uint8_t low) {
|
|
digitalWrite(LATCH_PIN, LOW);
|
|
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, low);
|
|
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, high);
|
|
digitalWrite(LATCH_PIN, HIGH);
|
|
}
|
|
|
|
void displayScore() {
|
|
int high = gameIndex % 100 / 10;
|
|
int low = gameIndex % 10;
|
|
sendScore(high ? digitTable[high] : 0xff, digitTable[low]);
|
|
}
|
|
|
|
/**
|
|
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() {
|
|
for (int i = 0; i < gameIndex; i++) {
|
|
byte currentLed = gameSequence[i];
|
|
lightLedAndPlayTone(currentLed);
|
|
delay(50);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Waits until the user pressed one of the buttons,
|
|
and returns the index of that button
|
|
*/
|
|
byte readButtons() {
|
|
while (true) {
|
|
for (byte i = 0; i < 4; i++) {
|
|
byte buttonPin = buttonPins[i];
|
|
if (digitalRead(buttonPin) == LOW) {
|
|
return i;
|
|
}
|
|
}
|
|
delay(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Play the game over sequence, and report the game score
|
|
*/
|
|
void gameOver() {
|
|
Serial.print("Game over! your score: ");
|
|
Serial.println(gameIndex - 1);
|
|
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);
|
|
|
|
sendScore(DASH, DASH);
|
|
delay(500);
|
|
}
|
|
|
|
/**
|
|
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];
|
|
byte actualButton = readButtons();
|
|
lightLedAndPlayTone(actualButton);
|
|
if (expectedButton != actualButton) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
The main game loop
|
|
*/
|
|
void loop() {
|
|
displayScore();
|
|
|
|
// 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;
|
|
}
|
|
|
|
playSequence();
|
|
if (!checkUserSequence()) {
|
|
gameOver();
|
|
}
|
|
|
|
delay(300);
|
|
|
|
if (gameIndex > 0) {
|
|
playLevelUpSound();
|
|
delay(300);
|
|
}
|
|
}
|