/*******************************************************************************
* Optimized SparkFun Inventor's Kit - Simon Game
* Based on Example sketch 16 Simon Game
*******************************************************************************/
// If header file is missing, uncomment these definitions:
// Game modes
#define MODE_MEMORY 0
#define MODE_BATTLE 1
// Button pin definitions
#define BUTTON_RED 5 // Button pins with internal pullups
#define BUTTON_GREEN 4
#define BUTTON_BLUE 3
#define BUTTON_YELLOW 2
// LED pin definitions
#define LED_RED 12
#define LED_GREEN 11
#define LED_BLUE 10
#define LED_YELLOW 9
// Buzzer pins
#define BUZZER1 8
//#define BUZZER2 9
// Button/LED choice definitions
#define CHOICE_RED (1 << 0) // Using bit positions for more efficient checks
#define CHOICE_GREEN (1 << 1)
#define CHOICE_BLUE (1 << 2)
#define CHOICE_YELLOW (1 << 3)
#define CHOICE_NONE 0
#define CHOICE_OFF 0
// Game settings
#define ROUNDS_TO_WIN 13 // Game rounds before winning
#define ENTRY_TIME_LIMIT 3000 // Time limit for button entries (ms)
// Easter egg music notes
#define NOTE_G4 392
#define NOTE_A4 440
#define NOTE_C5 523
#define NOTE_D4 294
#define NOTE_E4 330
// Optional feature flag - comment out to save memory
#define INCLUDE_EASTER_EGGS
// Arrays for more efficient code
const byte BUTTONS[] = {BUTTON_RED, BUTTON_GREEN, BUTTON_BLUE, BUTTON_YELLOW};
const byte LEDS[] = {LED_RED, LED_GREEN, LED_BLUE, LED_YELLOW};
const unsigned int TONES[] = {1136, 568, 851, 638}; // Tone delays for each button
const byte CHOICES[] = {CHOICE_RED, CHOICE_GREEN, CHOICE_BLUE, CHOICE_YELLOW};
// Game state variables
byte gameMode = MODE_MEMORY;
byte gameBoard[ROUNDS_TO_WIN]; // Only allocate what's needed
byte gameRound = 0;
// Timer variables for non-blocking operation
unsigned long previousMillis = 0;
unsigned long buttonHeldTime = 0;
boolean buttonHeld = false;
boolean buttonPressedFlag = false;
// Function declarations
boolean play_memory(void);
boolean play_battle(void);
void playMoves(void);
void add_to_moves(void);
void setLEDs(byte leds);
byte wait_for_button(void);
byte checkButton(void);
void toner(byte which, int buzz_length_ms);
void buzz_sound(int buzz_length_ms, int buzz_delay_us);
void play_winner(void);
void play_loser(void);
void attractMode(void);
void play_beegees(void);
void setup() {
// Set up button inputs with internal pull-up resistors
for (byte i = 0; i < 4; i++) {
pinMode(BUTTONS[i], INPUT_PULLUP);
pinMode(LEDS[i], OUTPUT);
}
// Setup buzzer pins
pinMode(BUZZER1, OUTPUT);
// pinMode(BUZZER2, OUTPUT);
// Mode checking
gameMode = MODE_MEMORY; // Default game mode
// Check for special button presses during startup
byte startupButton = checkButton();
if (startupButton == CHOICE_YELLOW) {
#ifdef INCLUDE_EASTER_EGGS
play_beegees();
#else
play_winner(); // Simplified if easter eggs disabled
#endif
}
else if (startupButton == CHOICE_GREEN) {
gameMode = MODE_BATTLE; // Enter battle mode
setLEDs(CHOICE_GREEN);
toner(CHOICE_GREEN, 150);
setLEDs(CHOICE_RED | CHOICE_BLUE | CHOICE_YELLOW);
while(checkButton() != CHOICE_NONE); // Wait for button release
delay(10); // Debounce
}
play_winner(); // Startup greeting
}
void loop() {
attractMode(); // Blink lights waiting for player
// Indicate game start
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE | CHOICE_YELLOW);
delay(1000);
setLEDs(CHOICE_OFF);
delay(250);
if (gameMode == MODE_MEMORY) {
// Play memory game and handle result
if (play_memory())
play_winner();
else
play_loser();
}
else if (gameMode == MODE_BATTLE) {
play_battle();
play_loser();
}
}
// Optimized play_memory function
boolean play_memory(void) {
randomSeed(millis()); // Seed with current time
gameRound = 0;
while (gameRound < ROUNDS_TO_WIN) {
add_to_moves();
playMoves();
// Player's turn to repeat sequence
for (byte currentMove = 0; currentMove < gameRound; currentMove++) {
byte choice = wait_for_button();
if (choice == CHOICE_NONE) return false; // Timeout
if (choice != gameBoard[currentMove]) return false; // Wrong button
}
delay(1000); // Correct sequence delay
}
return true; // Player won all rounds
}
// Battle mode game play
boolean play_battle(void) {
gameRound = 0;
while (1) {
byte newButton = wait_for_button();
if (newButton == CHOICE_NONE) return false;
gameBoard[gameRound++] = newButton;
// Next player repeats sequence
for (byte currentMove = 0; currentMove < gameRound; currentMove++) {
byte choice = wait_for_button();
if (choice == CHOICE_NONE) return false;
if (choice != gameBoard[currentMove]) return false;
}
delay(100); // Time to pass to next player
}
return true; // Should never reach here
}
// Play back current game sequence
void playMoves(void) {
for (byte currentMove = 0; currentMove < gameRound; currentMove++) {
toner(gameBoard[currentMove], 150);
delay(150); // Adjust this to change difficulty
}
}
// Add a new random move to sequence
void add_to_moves(void) {
byte newButton = CHOICES[random(0, 4)]; // Choose directly from CHOICES array
gameBoard[gameRound++] = newButton;
}
// Optimized LED control function
void setLEDs(byte leds) {
for (byte i = 0; i < 4; i++) {
digitalWrite(LEDS[i], (leds & CHOICES[i]) ? HIGH : LOW);
}
}
// Wait for button press with timeout
byte wait_for_button(void) {
unsigned long startTime = millis();
while ((millis() - startTime) < ENTRY_TIME_LIMIT) {
byte button = checkButton();
if (button != CHOICE_NONE) {
toner(button, 150);
while (checkButton() != CHOICE_NONE); // Wait for release
delay(10); // Debounce
return button;
}
}
return CHOICE_NONE; // Timeout
}
// Optimized button checking
byte checkButton(void) {
for (byte i = 0; i < 4; i++) {
if (digitalRead(BUTTONS[i]) == LOW) {
return CHOICES[i];
}
}
return CHOICE_NONE;
}
// Light LED and play tone for a button
void toner(byte which, int buzz_length_ms) {
setLEDs(which);
// Find tone index from CHOICES array
for (byte i = 0; i < 4; i++) {
if (which == CHOICES[i]) {
buzz_sound(buzz_length_ms, TONES[i]);
break;
}
}
setLEDs(CHOICE_OFF);
}
// Optimized buzzer function
void buzz_sound(int buzz_length_ms, int buzz_delay_us) {
long buzz_length_us = buzz_length_ms * (long)1000;
while (buzz_length_us > (buzz_delay_us * 2)) {
buzz_length_us -= buzz_delay_us * 2;
digitalWrite(BUZZER1, LOW);
// digitalWrite(BUZZER2, HIGH);
delayMicroseconds(buzz_delay_us);
digitalWrite(BUZZER1, HIGH);
// digitalWrite(BUZZER2, LOW);
delayMicroseconds(buzz_delay_us);
}
}
// Winner animation
void play_winner(void) {
// Alternating LED patterns with winner sounds
byte patterns[2] = {CHOICE_GREEN | CHOICE_BLUE, CHOICE_RED | CHOICE_YELLOW};
for (byte i = 0; i < 4; i++) {
setLEDs(patterns[i % 2]);
winner_sound();
}
}
// Winner sound effect
void winner_sound(void) {
for (byte x = 250; x > 70; x--) {
for (byte y = 0; y < 3; y++) {
// digitalWrite(BUZZER2, HIGH);
digitalWrite(BUZZER1, LOW);
delayMicroseconds(x);
// digitalWrite(BUZZER2, LOW);
digitalWrite(BUZZER1, HIGH);
delayMicroseconds(x);
}
}
}
// Loser animation
void play_loser(void) {
byte patterns[2] = {CHOICE_RED | CHOICE_GREEN, CHOICE_BLUE | CHOICE_YELLOW};
for (byte i = 0; i < 4; i++) {
setLEDs(patterns[i % 2]);
buzz_sound(255, 1500);
}
}
// Non-blocking attract mode
void attractMode(void) {
const unsigned long interval = 100; // LED cycle interval
byte ledCycle = 0;
while (1) {
unsigned long currentMillis = millis();
// Time to change LED?
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Cycle through LEDs in sequence
setLEDs(CHOICES[ledCycle]);
ledCycle = (ledCycle + 1) % 4;
}
// Check for button press to exit
if (checkButton() != CHOICE_NONE) return;
}
}
#ifdef INCLUDE_EASTER_EGGS
// Bee Gees Easter Egg
void play_beegees(void) {
// Notes in the melody - each note is about 1/8th note, 0 = rest
int melody[] = {
NOTE_G4, NOTE_A4, 0, NOTE_C5, 0, 0, NOTE_G4, 0, 0, 0,
NOTE_E4, 0, NOTE_D4, NOTE_E4, NOTE_G4, 0,
NOTE_D4, NOTE_E4, 0, NOTE_G4, 0, 0,
NOTE_D4, 0, NOTE_E4, 0, NOTE_G4, 0, NOTE_A4, 0, NOTE_C5, 0
};
int noteDuration = 115; // Tempo setting
byte LEDnumber = 0;
// Startup animation
setLEDs(CHOICE_YELLOW);
toner(CHOICE_YELLOW, 150);
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE);
while(checkButton() != CHOICE_NONE); // Wait for button release
setLEDs(CHOICE_NONE);
delay(1000);
digitalWrite(BUZZER1, LOW);
// Play song until button press
while(checkButton() == CHOICE_NONE) {
for (int thisNote = 0; thisNote < 32; thisNote++) {
// Change LED for visual effect
setLEDs(1 << LEDnumber);
LEDnumber = (LEDnumber + 1) % 4;
// Play current note
if (melody[thisNote] != 0) {
// tone(BUZZER2, melody[thisNote], noteDuration);
}
// Pause between notes
int pauseBetweenNotes = noteDuration * 1.30;
delay(pauseBetweenNotes);
// noTone(BUZZER2);
// Exit if button pressed
if (checkButton() != CHOICE_NONE) return;
}
}
}
#else
// Simplified version if easter eggs disabled
void play_beegees(void) {
play_winner();
}
#endif