/*******************************************************************************
 * 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