/*** Fast & Easy version of Simon Sings, by Ron Miller (RonM9) July 2015
 with special hardware consideration to except direct connection of:
 LED & Switch modules from: 
 - 1st pattern is now all 1-4 in sequence, to facilitate testing of hookup & pin assignments
 - uses onboard LED as 'Fail' indicator
 - along with some other minor code changes
 Refer http://Instructables.com/
***/
   
/* Simon Sings - a memory game for 4 leds, 4 switches and a buzzer.
 "Sings" is the sound-enhanced version of "Fairly Simple Simon".
 No relation to the Singh family :)
 Original Breadboard diagram, video, etc. at http://bit.ly/simonsings
 
 After reset, Simon plays a bar from "Simple Simon says", and starts playing a game at level 4.
 
 Playing a game at level N:
 1) Simon picks a random sequence of N LED flashes.
 2) Simon waits for you to press any button when ready.
 3) Simon "says" the sequence. Memorize it.
 4) You should then repeat the sequence on the buttons.
    * If you're right, Simon plays the song, and you start a level N+1 game at step 1.
    * If you push a wrong button, Simon plays a sad tune, and you go back to step 2.
 
 cc-by-sa by @TheRealDod, Nov 23, 2010
 RRM Oct 2015: allowed for faster button inputs
 */
 
//#include "pitches.h"   the following 'NOTE's were in a separate .h file   ...
/*************************************************
 * Public Constants
 *************************************************/

#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978


const int NLEDS = 4;

// these pin assignments are for Fast & Easy Instructables configuration using modules noted above
const int SPEAKERPIN = 8; //was orig 8 then 9;
const int LEDPINS[NLEDS] = {2,3,4,5}; // Some Fade effect if PWM pins are used
const int SWITCHPINS[NLEDS] = {15,16,17,18}; // A4-A1
const int PSUDOGND = 19;  // needed for button module in this Fast&Easy config
const int PSUDOGND2 = 11; //7;  // needed for LED module
const int PSUDOGND3 = 7; //11;  // used as -neg for buzzer
const int ONBRD_LED = 13;

const int SWITCHPRESSED = 0; // 1 or 0, for normally-open/closed switches
const int NOTES[NLEDS] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4};

const int FADESTEPS = 8;
const int FADEINDURATION = 200;
const int FADEOUTDURATION = 150;
const int SEQDELAY = 50; // Millis between led flashes.
const int PAUSEB4SEQ = 500; // Millis before starting the sequence.
bool watchBtn=false;  // used to keep LED illumination activity from blocking Button activity

const int MINLEVEL = 4;
const int MAXLEVEL = 16;
int gameLevel;
int simonSez[MAXLEVEL]; // sequence of 0..NLEDS-1

// -- song-array note fields --
// Tone
const int NOTETONE = 0;
const int SILENCE = 0;
const int ENDOFSONG = -1;
// Duration
const int NOTEDURATION = 1;
const int SINGLEBEAT = 125; // Note duration (millis) is multiplied by this
const float PAUSEFACTOR=0.2; // relative length of silence after playing a note
// LED
const int NOTELED = 2;
const int ALLLEDS = -1;

int WINSONG[][3] = {
  {SILENCE,2,ALLLEDS}
  ,{NOTE_E4,1,2} ,{NOTE_E4,1,2} ,{NOTE_E4,1,2}
  ,{NOTE_F4,1,3} ,{NOTE_E4,1,2} ,{NOTE_D4,3,1}
  ,{NOTE_G4,1,ALLLEDS} ,{NOTE_G4,1,ALLLEDS} ,{NOTE_G4,1,ALLLEDS}
  ,{NOTE_A4,2,ALLLEDS} ,{NOTE_G4,5,ALLLEDS}
  ,{ENDOFSONG,ENDOFSONG,ENDOFSONG}
};

int LOSESONG[][3] = {
  {NOTE_B5,2,3},{NOTE_A5,2,2},{NOTE_GS5,2,1},{NOTE_G5,8,ALLLEDS}, {ENDOFSONG,ENDOFSONG,ENDOFSONG}
};

// =============================================================
void setup() {
  Serial.begin(9600);    // use serial
  Serial.println("\r\n  ---- Simon Sings ----");

  // Analog in 0 should *not* be connected.
  // It's mama's little PRNG :)
  randomSeed(analogRead(0));

  pinMode(ONBRD_LED,OUTPUT);
  pinMode(SPEAKERPIN,OUTPUT);
  noTone(SPEAKERPIN);
  gameLevel=MINLEVEL;
  for (byte l=0; l<NLEDS; l++) {
    pinMode(LEDPINS[l], OUTPUT);
    pinMode(SWITCHPINS[l], INPUT);
    digitalWrite(SWITCHPINS[l],1 - SWITCHPRESSED);
  }
  // setup psuedo ground(s)
  pinMode(PSUDOGND, OUTPUT);
  digitalWrite(PSUDOGND,LOW);
  pinMode(PSUDOGND2, OUTPUT);
  digitalWrite(PSUDOGND2,LOW);
  pinMode(PSUDOGND3, OUTPUT);
  digitalWrite(PSUDOGND3,LOW);
  
//  beep(50); beep(50); beep(50);
//  delay(200);
  
  // Visual feedback after reset. Also good as a cable check :)
  playWinSequence();
  
  // init Game Sequence as 1,2,3,4    suitable for configuration verification
   for (int i=0; i<gameLevel; i++) {
    simonSez[i]=i;
  }
  Serial.println("\r\n  ---- Simon Sings ----");
}

// ===========================================================
void loop() {
  int done=0;

  while (!done) {
    Serial.println("\r\n  Press any button for next pattern.");
    getSwitchStroke();
    digitalWrite(ONBRD_LED,LOW);  // clears the red 'FAILURE indicator' LED
    delay(PAUSEB4SEQ);
    playGameSequence(gameLevel);
    Serial.println("\r\n  Repeat the pattern.");    
    if (playerGuess(gameLevel)) {
      playWinSequence();
      done = 1;
      if (gameLevel<MAXLEVEL) {
        gameLevel++;
      }
      extendSequence();
    } else {
      playLoseSequence();
      gameLevel = MINLEVEL;
      initGameSequence(gameLevel);
    }
  }
}

// ===========================================================
//             Local Utilities

void initGameSequence(int gameLevel) {
  // assertion: gameLevel<=MAXLEVEL
  for (int i=0; i<gameLevel; i++) {
    simonSez[i]=random(NLEDS);
  }
}

void extendSequence() {
    simonSez[gameLevel-1]=random(NLEDS);
}

void playGameSequence(int gameLevel) {
  for (int i=0; i<gameLevel; i++) {
    playLed(simonSez[i]); // Fade LED and play its note
  }
}

void fadeLed(int theLed,int val,int duration) {
  int fadeStep=256/FADESTEPS;
  int fadeDelay=duration/FADESTEPS;
  for (int i=0; i<256; i+=fadeStep) {
    if (theLed>=0) {
      analogWrite(LEDPINS[theLed],val?i:255-i);
    } 
    else { // ALLLEDS
      for (int j=0; j<NLEDS; j++) {
        analogWrite(LEDPINS[j],val?i:255-i);
      }
    }
    delay(fadeDelay);
    if (watchBtn && btnReased()) break; // to allow fast button input sequences
  }
  
  // force val (in case fadeStep doesn't divide 256)
  if (theLed>=0) {
    digitalWrite(LEDPINS[theLed],val); 
  }
  else {
    for (int j=0; j<NLEDS; j++) {
      digitalWrite(LEDPINS[j],val); 
    }
  }
}

void playLed(int theLed) { // Fade LED and play its note
//  pinMode(SPEAKERPIN,OUTPUT);
  tone(SPEAKERPIN,NOTES[theLed]);
  fadeLed(theLed,HIGH,FADEINDURATION); // Fade in
  noTone(SPEAKERPIN);
  fadeLed(theLed,LOW,FADEOUTDURATION); // Fade out
}

int playerGuess(int gameLevel) {
  for (int i=0 ; i<gameLevel ; i++) {
    int guess=getSwitchStroke();
    //Serial.print(guess,DEC);
    //Serial.print(",");
    //Serial.println(simonSez[i]);
    if (guess!=simonSez[i]) {
      return 0;
    } 
    else {
      watchBtn = true;
      playLed(guess); // Fade LED and play its note
      watchBtn = false;
    }
  }
  return 1;
}

void playSong(int song[][3]) {
//  pinMode(SPEAKERPIN,OUTPUT);
  for (int note=0; song[note][NOTETONE]!=ENDOFSONG; note++) {
    int theDuration=SINGLEBEAT*song[note][NOTEDURATION];
    int theTone=song[note][NOTETONE];
    if (theTone) {
      tone(SPEAKERPIN,theTone);
    }
    int theLed=song[note][NOTELED];
    fadeLed(theLed,HIGH,theDuration); // Fade in
    noTone(SPEAKERPIN);
    fadeLed(theLed,LOW,theDuration*PAUSEFACTOR); // Fade out + silence between note
  }
}

int playWinSequence() {
  Serial.println("\r\n You are a Winner !");
  playSong(WINSONG);
}

int playLoseSequence() {
  playSong(LOSESONG);
  Serial.println("\r\nRasberrys!! You lost it.");
//  digitalWrite(ONBRD_LED,HIGH);   // turns on the 'FAILURE indicator' LED
}

int getSwitchStroke() {
  while (get1stPressedSwitch()>=0) {
    // flush everything until no switch is pressed
    delay(10);
  }
  while (get1stPressedSwitch()<0) {
    // wait for next press
    delay(10);
  }
  return get1stPressedSwitch();
}

int get1stPressedSwitch() {
  for (int i=0; i<NLEDS; i++) {
    if (digitalRead(SWITCHPINS[i])==SWITCHPRESSED) {
      return i;
    }
  }
  return -1;
}

bool btnReased() {
  bool released=true;
  for (int i=0; i<NLEDS; i++) {
    if (digitalRead(SWITCHPINS[i])==SWITCHPRESSED) {
      released=false;
    }
  }
  return(released);
}

void beep(unsigned char delayms){  // works with both buzzers and speakers
  byte pinState = 0;       // 0 turns it off
  for (int i=0; i<delayms; i++) {         // wait for a delayms ms
    pinState = 1 - pinState;
    digitalWrite ( SPEAKERPIN, pinState); 
    delay(1);    
  }
  delay(delayms);          // wait for a delayms ms   
}  
nano:12
nano:11
nano:10
nano:9
nano:8
nano:7
nano:6
nano:5
nano:4
nano:3
nano:2
nano:GND.2
nano:RESET.2
nano:0
nano:1
nano:13
nano:3.3V
nano:AREF
nano:A0
nano:A1
nano:A2
nano:A3
nano:A4
nano:A5
nano:A6
nano:A7
nano:5V
nano:RESET
nano:GND.1
nano:VIN
nano:12.2
nano:5V.2
nano:13.2
nano:11.2
nano:RESET.3
nano:GND.3
r1:1
r1:2
led1:A
led1:C
r2:1
r2:2
led2:A
led2:C
led3:A
led3:C
led4:A
led4:C
r3:1
r3:2
r4:1
r4:2
r5:1
r5:2
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
btn2:1.l
btn2:2.l
btn2:1.r
btn2:2.r
btn3:1.l
btn3:2.l
btn3:1.r
btn3:2.r
btn4:1.l
btn4:2.l
btn4:1.r
btn4:2.r
bz1:1
bz1:2