Introduction: Simon Says Memory Game

If you're seeing this message then you've stumbled across the Simon says memory game. Hi there, I'm Esam, a grade 11 computer engineering student and this is my final project. The reason I chose the Simon says memory game isn't because of its simplicity but actually because I wanted to make the game a bit more interesting. Instead of a simple memory game, I wanted to add sounds, digital displays etc. I wanted to add my spin off on this once very popular game!

How to play the game:

  1. Once the game starts, click the button corresponding to the led/note played
  2. If the correct button is pressed, a score will be added to the 7-segment displays and the LCD. Once a score has been added, a new note will play again on top of the previous note. this sequence will continue to stack until the player has gotten a note wrong.
  3. If a button is pressed that doesn't correspond to the right led/note then the game will play a losing sound and end.

The Objective of the game is to achieve the highest score by getting the sequence right every time.

Supplies

Step 1: Wiring and Setup

For wiring help, look at the images above.


  • Connect power and ground from the Arduino to the power and ground rails on the breadboard. This will give easy access to all the parts on the breadboard.
  • For this part, you will need to attach your pushbuttons in the middle of the breadboard along with the LEDs. The sequence should be pushbutton then LED. This must be repeated 4 times.
  • Once the previous step is completed, connect the LED's and pushbuttons to ground and their assigned pins.


Assigned pins (left to right):

  1. Terminal 1a to pin 5 (pb #1)
  2. LED anode to pin 12 (red)
  3. Terminal 1a to pin 4 (pb #2)
  4. LED anode to pin 11 (green)
  5. Terminal 1a to pin 3 (pb #3)
  6. LED anode to pin 10 (blue)
  7. Terminal 1a to pin 2 (pb #4)
  8. LED anode to pin 9 (yellow)


  • Next, take a buzzer (types listed above) and connect its negative leg to a ground rail and connect its power leg to pin # 8.
  • After that, take two shift registers and place them upside down in the middle of the breadboard with 7-segment displays in between.

Shift register guide for those who don't know how to work with one.


Shift Registers' connections (left to right):

  1. Connect pin #9 to the second SR pin 14.
  2. Connect pin #10 to a power rail and the common anode pins of the 7-segment displays.
  3. Connect pin #11 to Arduino pin A2 with a 10k Ohm resistor and to the second SR pin #11.
  4. Connect pin #12 to Arduino pin A1 with a 10k Ohm resistor and to the second SR pin #12.
  5. Connect pin #13 to SR pin # 8 and to the second SR pin #13.
  6. Connect pin #8 to a ground rail with a 10k Ohm resistor and to the SR pin #8.
  7. Connect pin #14 to Arduino pin A0 with a 10k Ohm resistor.
  8. Connect pin #16 to power with a 10k Ohm resistor and to second SR pins #9 & #16


7-Segment Display connections (Alphabetically):

  1. Pin A to pin 15 on SR #1
  2. Pin B to pin 1 on SR #1
  3. Pin C to pin 2 on SR #1
  4. Pin D to pin 3 on SR #1
  5. Pin E to pin 4 on SR #1
  6. Pin F to pin 5 on SR #1
  7. Pin G to pin 6 on SR #1

Repeat the same 7-segment display connections for the second shift register.


  • Finally, take a 1602 LCD (I2C) and connect it to power and ground along with its other connections.



Step 2: Code Breakdown

The following is code for the libraries to be used and the notes to be played by the buzzer.


/**

  Simon Game for Arduino with Score display*/


#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <Adafruit_LiquidCrystal.h>

#include <LiquidCrystal.h>


int seconds = 1;


Adafruit_LiquidCrystal lcd(0);



#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


The following is code defining each pin connection and initialisation of the Arduino.

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


  lcd.begin(16, 2);

 lcd.print("Start game");

 delay(200);

 lcd.clear();


The following is code is for the 7-segment displays and for detecting and displaying the score.

/* 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]);

}


The following is code plays a certain sound assigned to certain LEDs.

 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);

 }

}


The following code is for detecting if the pushbuttons are being pressed or not.

byte readButtons() {

 while (true) {

  for (byte i = 0; i < 4; i++) {

   byte buttonPin = buttonPins[i];

   if (digitalRead(buttonPin) == LOW) {

    return i;

   }

  }

  delay(1);

 }

}


The following code is for the game being over.

void gameOver() {

 lcd.setCursor(1, 0);

 lcd.print("The End! Score:");

 Serial.println(gameIndex - 1);

 gameIndex = 0;

 delay(500);

 lcd.clear();


The following code is for playing sounds if the player levels up or loses.

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 following code is for displaying the score and lighting up LCD when new information is displayed.

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);

  

  lcd.setCursor(0, 1);

 lcd.print(seconds);

 lcd.setBacklight(1);

 delay(500); // Wait for 500 millisecond(s)

 lcd.setBacklight(0);

 delay(500); // Wait for 500 millisecond(s)

 seconds += 1;

  

 }

}

Step 3: Full Code

Here is the following code for this project with comments:


/**

  Simon Game for Arduino with Score display*/


#include <Wire.h>

#include <LiquidCrystal_I2C.h>

#include <Adafruit_LiquidCrystal.h>

#include <LiquidCrystal.h>


int seconds = 1;


Adafruit_LiquidCrystal lcd(0);



#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


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


  lcd.begin(16, 2);

 lcd.print("Start game");

 delay(200);

 lcd.clear();

 

 

 // 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);

 }

}


/**

 Restart the game and show the players' score

*/

void gameOver() {

 lcd.setCursor(1, 0);

 lcd.print("The End! Score:");

 Serial.println(gameIndex - 1);

 gameIndex = 0;

 delay(500);

 lcd.clear();

 


 // 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);

  

  lcd.setCursor(0, 1);

 lcd.print(seconds);

 lcd.setBacklight(1);

 delay(500); // Wait for 500 millisecond(s)

 lcd.setBacklight(0);

 delay(500); // Wait for 500 millisecond(s)

 seconds += 1;

  

 }

}

Step 4: Ending

At last, if you made it to this step, your Arduino should be working along with the code. If you need any help or come across any problems, just leave me an email at 735407@pdsb.net. Thank you for taking your time to look at and using my project.

Step 5: Inspirations

I used the following sources for code, Inspiration and help: