Location

We've moved!
213 E Ermina Ave, Spokane WA, 99207

Hours

5:30pm - 8:30pm Wednesday
New? Stop by for a tour at 6:30pm on Wednesdays

Thanks for supporting your
community 501(c)(3) makerspace


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
OBEY SIMON!
02-19-2016, 09:31 PM, (This post was last modified: 04-29-2016, 07:30 PM by Brendan.)
#1
OBEY SIMON!
So, it turns out the name "Simon Says" is already taken! Can you believe that?! So, I've named my musical LED follow-along game, "Obey Simon!" It has a much more imperative tone I feel.

I'm going to work on documenting the hardware build...so stay tuned for a post about that.

There were a couple of requests for the code...so here it is, in all it's ugliness.

I'm going to be sneaking back in here and ninja-editing the code to clean it up, add spacing to make it look better, etc...but if you wanted to see how Simon Says ... er ... Obey Simon! works, here you go!

Code:
/*
* Simon Says game for Arduino
* Built to run on the Spokane Create proto boards
* January, 2016 - B. Powell
*
*/

#include <EEPROM.h>


#define DEBOUNCE 10  // button debouncer, how many ms to debounce, 5+ ms is usually plenty

// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {4, 5, 6, 7}; // the analog 0-5 pins are also known as 14-19

// This handy macro lets us determine how big the array up above is, by checking the size
#define NUMBUTTONS sizeof(buttons)

// we will track if a button is just pressed
volatile byte pressed[NUMBUTTONS], scoreCounter, blinkDuration;

// Some timeout counters if the player walks away from the game
byte timeout = 0;
unsigned long timeoutTimer;

// High score and EEPROM variables
int address;
byte highScore, highTens, highOnes;

volatile byte score, scoreTens, scoreOnes, blinkCount, blinkTimes, tenCount, oneCount;  // All of these are for the high-score blinking routine
boolean ledOn = 0, tensDone, onesDone;


#define LED 13
#define RED A0
#define YELLOW A1
#define GREEN A2
#define BLUE A3

#define blueNote 440   // E4 440
#define yellowNote 277 // C-Sharp 277
#define redNote 165  // A 165
#define greenNote 330  // E3, octive lower? (higher?) 330

#define loseNote 41 // Buzzer type sound
#define gameLength 300 // Max length of moves before player wins "THIS! IS! SPARTA!" betweek 1,100 and 1,200 is the limit before getting a memory warning
#define gameSpeed 600 // ms of duration of tones...lower = faster
#define delayBetweenColors 150 // pause between colors / steps...lower = faster


byte theGame[gameLength];   // Initialize an array



void setup() {
  
  /*
   *
   * Okay, the array is built with values 0 - 3
   * 0 = Red
   * 1 = Yellow
   * 2 = Green
   * 3 = Blue
   *
   * Game Flow:
   * 1: Play start-up song
   * 2: Read high-score byte from eeprom
   *  Yes, a byte only...that's 256 iterations! Or, n*(n+1)/2 = 32,896 button presses! That's well over 8 hours of straight game-play.
   *    If you have 8 hours to play Simon on this PCB and beat it, you have enough time to convert the byte to a word in the code...
   * 3: create the game array - Takes about 20ms for a 300-move array at 20Mhz
   * 4: Start the game
   * 5: Play until loss...
   * 6: Log new high-score if necessary
   * 7: Start from 1
   * 8: Maybe add a "Minutes played" counter to eeprom for battery testing (Still to-do)
   *
   *
   */

   // This does an analog read of the LEDs...for random number seed.
   // So: ambient light, shadows, and how you're holding the game will help "seed" the random number generator.
   // No two games should be the same!
  
   long seed = (analogRead(RED) * analogRead(YELLOW) * analogRead(GREEN) * analogRead(BLUE));
   randomSeed(seed);
      
    byte i;
    
    Serial.begin(57600);
    
    // pin13 LED
    pinMode(13, OUTPUT);
  
    for (i=0; i < NUMBUTTONS; i++){
      pinMode(buttons[i], INPUT_PULLUP);    // Set switches as input, and activate the internal pull-up resistor
     }



    
   // LEDs
    pinMode (LED, OUTPUT);
    pinMode (RED, OUTPUT);
    pinMode (YELLOW, OUTPUT);
    pinMode (GREEN, OUTPUT);
    pinMode (BLUE, OUTPUT);
    

    // TIMER 1 for interrupt frequency 66.66666666666667 Hz: (every 15ms)

      cli(); // stop interrupts
      TCCR1A = 0; // set entire TCCR1A register to 0
      TCCR1B = 0; // same for TCCR1B
      TCNT1  = 0; // initialize counter value to 0
      // set compare match register for 66.66666666666667 Hz increments
      OCR1A = 37499; // = 20000000 / (8 * 66.66666666666667) - 1 (must be <65536)
      // turn on CTC mode
      TCCR1B |= (1 << WGM12);
      // Set CS12, CS11 and CS10 bits for 8 prescaler
      TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
      // enable timer compare interrupt
      TIMSK1 |= (1 << OCIE1A);
      sei(); // allow interrupts
  

   // Check if we're going to reset high-score...
    if (digitalRead(buttons[3]) == 0){
      EEPROM.write (0, 0);    // reset the high-score
    }
  
  
  }
  
  
  SIGNAL(TIMER1_COMPA_vect) {   // What to do when Timer1 Overflows (15ms) This is our interrupt Handler
  
    check_switches();

   // High score routine...

  
       if (tenCount > 0 & tensDone == 0) {    // Do we have 10s to blink?
      
          if (ledOn == 0 & blinkCount <= 26) {  // We're in an "LED Off" phase of the blink
            digitalWrite (LED, 0);
            blinkCount ++;
          }
          else if (ledOn == 1 & blinkCount <= 26) {   // We're in an "LED ON" phase of the blink
            digitalWrite (LED, 1);
            blinkCount ++;
          }
         if (ledOn == 0 & blinkCount == 26) {
          ledOn = 1;
          blinkCount = 0;
         }
          
          if (ledOn == 1 & blinkCount == 27) {    // Done with this blink
            digitalWrite (LED, 0);
            blinkCount = 0;
            ledOn = 0;
            tenCount --;
          }    
        }     // End of tenCount routine
      
        if (tenCount == 0 & blinkCount < 100 & tensDone == 0) {   // Delay between 10s and 1s
          blinkCount ++;
        }
      
        if (blinkCount >= 100) {    // All Tens have been blinked...move onto the ones
          tenCount = highTens;
          blinkCount = 0;
          tensDone = 1;
          onesDone = 0;
        }
      
        if (tensDone == 1) {
      
            if (oneCount > 0 & onesDone == 0) {   // Do we have ones to blink?
            
             if (ledOn == 0 & blinkCount <= 13) {   // Off part of the blink
                  digitalWrite (LED, 0);
                  blinkCount ++;
                }
                else if (ledOn == 1 & blinkCount <= 13) {   // On part of the blink
                  digitalWrite (LED, 1);
                  blinkCount ++;
                }
               if (ledOn == 0 & blinkCount == 13) {
                ledOn = 1;
                blinkCount = 0;
               }
                
                if (ledOn == 1 & blinkCount == 14) {
                  digitalWrite (LED, 0);
                  blinkCount = 0;
                  ledOn = 0;
                  oneCount --;
                }  
            }   // End of Ones section
      
        if (oneCount == 0 & blinkCount < 255 & onesDone == 0) {   // Delay
          blinkCount ++;
        }
        
        if (blinkCount >= 100) {
          oneCount = highOnes;
          blinkCount = 0;
          tensDone = 0;
          onesDone = 1;
        }
        
        } // end of delay and one-count

    
  }

// ***************************************************************

void loop() {

  
  highScore = readHighScore();
  highTens = highScore / 10;
  highOnes = highScore % 10;
  tenCount = highTens;
  oneCount = highOnes;
  tensDone = 0;
  onesDone = 0;
  blinkCount = 0;
  
  
    
    int reds = 0, yellows = 0, greens = 0, blues = 0;   // Zero out the summing counters
    long timing;
    
    Serial.println("Starting to build the array...");
      timing = millis();
      buildArray(theGame, gameLength);
      timing = millis() - timing;
    Serial.print("The Array is built; Time: ");
    Serial.print (timing);
    Serial.println(" milliseconds.");
    Serial.println("Here is the game...");    // So...if you are reading serial from the game, here is the whole game...
    
    for (int i = 0; i <= gameLength; i++){
    
    switch (theGame[i]) {
      case 0:
        Serial.print("Blue");
        blues++;
        break;
      case 1:
        Serial.print("Green");
        greens++;
        break;
      case 2:
        Serial.print("Yellow");
        yellows++;
        break;
      case 3:
        Serial.print("Red");
        reds++;
        break;
        
    }
    
    // Serial.print(theGame[i], DEC);
    Serial.print(", ");
    
      
    }
    // Here I'm just displaying the sum of REDs, Yellows, etc. I wanted to make sure the random arrays were balanced.
    // If I were pressed for space, this would go...but I've got room to spare!
    
    Serial.println ("  ");
    Serial.print ("Reds: ");
    Serial.println (reds, DEC);
    Serial.print ("Yellows: ");
    Serial.println (yellows, DEC);
    Serial.print ("Greens: ");
    Serial.println (greens, DEC);
    Serial.print ("Blues: ");
    Serial.println (blues, DEC);
    
    Serial.println ("Let's start the game!");
      
    playStartSong();
    playTheGame();
    
    if (timeout == 1) {
      blinky();
     }
    
    
  }
  
  // end


void buildArray(byte* ptrToArray, int sizeOfArray) {     // fill the array with random numbers...0 - 3
    
      for (int i = 0; i <= sizeOfArray; i++) {
      
        ptrToArray[i] = (byte)random(0,4);
      }
  }


byte readHighScore(){      // Read high score and return it
  
  address = 0;      // Where the high score is stored...
  byte score = EEPROM.read (address);
  return score;
  
  }

void writeHighScore(byte score) {          // Write high score to eeprom
  // Add writing code here...

  address = 0;      // Where the high score is stored
  EEPROM.write (address, score);


    
  }

void playStartSong(){
  
    // RED, Yellow, Green, Blue, Green, BlueEEEEEEEE
  
      digitalWrite (RED, HIGH);
      tone (11, redNote, 100);
      delay (100);
      digitalWrite (RED, LOW);
      digitalWrite (YELLOW, HIGH);
      tone (11, yellowNote, 100);
      delay (100);
      digitalWrite (YELLOW, LOW);
      digitalWrite (GREEN, HIGH);
      tone (11, greenNote, 100);
      delay (100);
      digitalWrite (GREEN, LOW);
      digitalWrite (BLUE, HIGH);
      tone (11, blueNote, 100);
      delay (100);
      digitalWrite (BLUE, LOW);
      digitalWrite (GREEN, HIGH);
      tone (11, greenNote, 100);
      delay (100);
      digitalWrite (GREEN, LOW);
      digitalWrite (BLUE, HIGH);
      tone (11, blueNote, 800);
      delay (800);
      digitalWrite (BLUE, LOW);
      delay (800);
    
    
  }


void playLosingSong(){
  
    for (int i = 0; i < 8; i++) {
      digitalWrite (RED, HIGH);
      digitalWrite (YELLOW, HIGH);
      digitalWrite (GREEN, HIGH);
      digitalWrite (BLUE, HIGH);
      
      tone (11, 41, 100);
      delay (100);
      digitalWrite (RED, LOW);
      digitalWrite (YELLOW, LOW);
      digitalWrite (GREEN, LOW);
      digitalWrite (BLUE, LOW);
      delay (100);
  
      
        }
    
  }

void playTheGame(){
  // Game code here...
  
    int marker = 0;
  
    for (int i = 0; i < gameLength; i++){       // The main playing loop...
      
      while (marker <= i) {               // Play the sequence
        switch (theGame[marker]) {
          case 0:
            digitalWrite (BLUE, HIGH);
            tone (11, blueNote, gameSpeed);
            delay (gameSpeed);
            digitalWrite (BLUE, LOW);
            break;
          
          case 1:
            digitalWrite (GREEN, HIGH);
            tone (11, greenNote, gameSpeed);
            delay (gameSpeed);
            digitalWrite (GREEN, LOW);
            break;
          
          case 2:
            digitalWrite (YELLOW, HIGH);
            tone (11, yellowNote, gameSpeed);
            delay (gameSpeed);
            digitalWrite (YELLOW, LOW);
            break;
          
          case 3:
            digitalWrite (RED, HIGH);
            tone (11, redNote, gameSpeed);
            delay (gameSpeed);
            digitalWrite (RED, LOW);
            break;
          
          default:
            break;
          
        }
        marker ++;
        delay (delayBetweenColors);
        
      }
  
      marker = 0;
  
      while (marker <= i) {         // Read user inputs...
  
      int testCondition = theGame[marker];
      timeout = 0;
      timeoutTimer = millis() + 10000;
      
      while (pressed[0] == 0 && pressed[1] == 0 && pressed[2] == 0 && pressed[3] == 0) {
        if (millis() == timeoutTimer) {
          timeout = 1;
          playLosingSong();
          if (i > highScore) {
          highScoreSong();
          writeHighScore ((byte)i);
          delay (100);
          // Add code here ... maybe play cool LED display or something to indicate hitting high score
        }
          return;
        }
        
       }
      
      if (!pressed[testCondition]) {       // Not the right button!
        playLosingSong();
        if (i > highScore) {
          highScoreSong();
          writeHighScore ((byte)i);
          delay (100);
          // Add code here ... maybe play cool LED display or something to indicate hitting high score
          // BLUE, GREEN, YELLOW, RED Ta Da!
        }
        return;           // Game over...return to loop
      }
      else {
        marker ++;    // Right button, onto the next...
      }
  
  
      if (pressed[0]) {
        while (pressed[0]){
          digitalWrite (BLUE, HIGH);
          tone (11, blueNote, 50);
          
        }
        digitalWrite (BLUE, LOW);
      }
      if (pressed[1]) {
        while (pressed[1]){
          digitalWrite (GREEN, HIGH);
          tone (11, greenNote, 50);
        }
        digitalWrite (GREEN, LOW);
  
      }
      if (pressed[2]) {
        while (pressed[2]) {
          digitalWrite (YELLOW, HIGH);
          tone (11, yellowNote, 50);
        }
        digitalWrite (YELLOW, LOW);
  
      }
    
      if (pressed[3]) {
        while (pressed[3]) {
          digitalWrite (RED, HIGH);
          tone (11, redNote, 50);
          
        }
        digitalWrite (RED, LOW);
  
      }
  
      }
  
  marker = 0;
  delay (500);
  
    }
    // If you've made it here...you won the game...you've been correct to the max length of the array.
    // I Don't know what to do here...you're a super-genious (Or the array is set TOO SMALL!)
    // Here, let me sing you the song of my people...
    for (int i = 0; i < 3; i++) {
      playStartSong();
    }
  
    }

  
void check_switches()     // Called by Timer overflow every 15ms
  {
    static byte previousstate[NUMBUTTONS];
    static byte currentstate[NUMBUTTONS];
    static long lasttime;
    byte index;
  
    if (millis() < lasttime) { // we wrapped around, lets just try again
       lasttime = millis();
    }
    
    if ((lasttime + DEBOUNCE) > millis()) {
      // not enough time has passed to debounce
      return;
    }
    // ok we have waited DEBOUNCE milliseconds, lets reset the timer
    lasttime = millis();
    
    for (index = 0; index < NUMBUTTONS; index++){
    
     currentstate[index] = digitalRead(buttons[index]);   // read the button
  
      
      if (currentstate[index] == previousstate[index]) {
  
        pressed[index] = !currentstate[index];  // remember, digital HIGH means NOT pressed
      }
      previousstate[index] = currentstate[index];   // keep a running tally of the buttons
    }
  }

void blinky() {

  for (int i = 0; i < gameLength; i++){       // The main playing loop...
      
        switch (theGame[i]) {
          case 0:
            digitalWrite (BLUE, HIGH);
            delay (1000);
            digitalWrite (BLUE, LOW);
            break;
          
          case 1:
            digitalWrite (GREEN, HIGH);
            delay (1000);
            digitalWrite (GREEN, LOW);
            break;
          
          case 2:
            digitalWrite (YELLOW, HIGH);
            delay (1000);
            digitalWrite (YELLOW, LOW);
            break;
          
          case 3:
            digitalWrite (RED, HIGH);
            delay (1000);
            digitalWrite (RED, LOW);
            break;
          
          default:
            break;
          
        }
    if (pressed[0] == 1 || pressed[1] == 1 || pressed[2] == 1 || pressed[3] == 1) {
      return;
    }
  }
  while (pressed[0] == 0 && pressed[1] == 0 && pressed[2] == 0 && pressed[3] == 0);       // Sit here until somebody pushes a button...
}


void highScoreSong(){

   // Blue Green Yellow Red!

  for (int i = 0; i < 3; i++) {
  
      digitalWrite (BLUE, HIGH);
      tone (11, redNote, 100);
      delay (100);
      digitalWrite (GREEN, HIGH);
      tone (11, yellowNote, 100);
      delay (100);
      digitalWrite (YELLOW, HIGH);
      tone (11, greenNote, 100);
      delay (100);
      digitalWrite (RED, HIGH);
      tone (11, blueNote, 600);
      delay (700);
      digitalWrite (BLUE, LOW);
      delay (100);
      digitalWrite (GREEN, LOW);
      delay (100);
      digitalWrite (YELLOW, LOW);
      delay (100);
      digitalWrite (RED, LOW);
      delay (100);
    
  }
  
}
Reply
02-20-2016, 07:19 AM,
#2
RE: OBEY SIMON!
Place holder.
Reply
02-22-2016, 09:05 PM,
#3
RE: OBEY SIMON!
(02-19-2016, 09:31 PM)Brendan Wrote: If you have 8 hours to play Simon on this PCB and beat it, you have enough time to convert the byte to a word in the code...

LOL. Nice. This is great! Not only would this make a great class, it could also make a great project for new visitors to work through if they don't have a project to work on. I'm thinking of a station with a step-by-step guide as an intro to Arduino project. Very nice.
-N8

"I built it because I didn't know I couldn't"
Reply
04-27-2016, 07:05 PM,
#4
RE: OBEY SIMON!
Whew! I've been away for a few weeks; sorry about that!

However, while I've been idle, I did make some changes to the Obey Simon code...I worked in the high score saving / storing routines...so it does record high-scores...You can also reset the high score by pressing the red-switch during power-up.

Last, I implemented a crazy (and rather ugly) interrupt-based state machine to allow the Pin 13 LED (the "standard" arduino LED) to blink the high-score code even during game-play...it's almost as if I have the Atmega multi-tasking! It blinks the "Tens" out slowly, and then the "ones" bit quicker.

That being said, I've been unable to break a high-score of 13.

I'll upload updated code soon!

Brendan
Reply
04-27-2016, 09:13 PM,
#5
RE: OBEY SIMON!
Awesome! This would have been a great topic to add to the class/workshop discussion tonight. I guess we did have a general Arduino topic, and this probably could fall under there. Would make a great soldering/Arduino combination workshop. When you are ready, let me know and we can fund ordering some boards, processors, and components for a workshop.
-N8

"I built it because I didn't know I couldn't"
Reply
04-29-2016, 07:32 PM,
#6
RE: OBEY SIMON!
I've updated the code in Post #1 to include the high-score EEPROM read/write...and this will also blink the Pin 13 LED with the current high score the moment power is applied until it is removed.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)