#include <LiquidCrystal.h>
#include <Keypad.h>
// LCD: RS, E, D4, D5, D6, D7 (D7 = A3)
LiquidCrystal lcd(7, 8, 9, 10, 11, A3);
// Shift register pins
const int dataPin = 6;
const int latchPin = 13;
const int clockPin = 12;
// Keypad setup (4x3 including A, B, 9)
const byte ROWS = 3;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', ' '} // last column = A4 (connected to A, B, 9)
};
byte rowPins[ROWS] = {2, 3, 4};
byte colPins[COLS] = {A0, A1, A2, A4}; // A4 is column for A/B/9
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Game state
String pattern = "";
String userInput = "";
int level = 1;
bool gameRunning = true;
void setup() {
lcd.begin(16, 2);
pinMode(dataPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
randomSeed(analogRead(A5));
showStartupInstructions();
}
void loop() {
if (!gameRunning) return;
generatePattern(level);
showPattern();
if (!getUserInput()) {
return;
}
lcd.clear();
lcd.print("Correct!");
level++;
delayWithExit(1500);
lcd.clear();
}
void showStartupInstructions() {
lcd.print("Memory Game!");
delayWithExit(1500);
lcd.clear();
lcd.print("Press keys:");
delayWithExit(1000);
lcd.clear();
lcd.print("9=Exit");
delayWithExit(1200);
lcd.clear();
lcd.print("A=Delete");
delayWithExit(1200);
lcd.clear();
lcd.print("B=Reset");
delayWithExit(1200);
lcd.clear();
lcd.print("Repeat Pattern!");
delayWithExit(1500);
lcd.clear();
}
void generatePattern(int len) {
pattern = "";
for (int i = 0; i < len; i++) {
char c = '1' + random(0, 8); // only 1–8
pattern += c;
}
}
void showPattern() {
for (char c : pattern) {
checkForImmediateExit();
int index = c - '1';
lcd.clear();
lcd.print("Watch: ");
lcd.print(c);
lightLED(index);
delayWithExit(700);
lightLED(-1);
delayWithExit(200);
}
lcd.clear();
lcd.print("Your turn:");
}
bool getUserInput() {
userInput = "";
lcd.setCursor(0, 1); // bottom line
while (userInput.length() < pattern.length()) {
char key = keypad.getKey();
if (key) {
if (key == '9') {
endGame();
return false;
} else if (key == 'A') {
// Delete last character
if (userInput.length() > 0) {
userInput.remove(userInput.length() - 1);
lcd.setCursor(userInput.length(), 1);
lcd.print(" ");
lcd.setCursor(userInput.length(), 1);
}
} else if (key == 'B') {
// Reset game
level = 1;
gameRunning = true;
lcd.clear();
lcd.print("Resetting...");
delay(1000);
lcd.clear();
return true;
} else if (key >= '1' && key <= '8') {
userInput += key;
lcd.print("*");
int index = key - '1';
lightLED(index);
delay(200);
lightLED(-1);
}
}
}
return userInput == pattern;
}
void endGame() {
lcd.clear();
lcd.print("Game Over!");
lcd.setCursor(0, 1);
lcd.print("Score: ");
lcd.print(level - 1);
delay(2000);
lcd.clear();
lcd.print(getFeedback(level - 1));
gameRunning = false;
while (true); // freeze
}
String getFeedback(int score) {
if (score >= 10) return "Amazing Memory!";
if (score >= 5) return "Good Job!";
return "Try Again!";
}
void lightLED(int index) {
byte leds = 0;
if (index >= 0 && index < 8) {
leds = 1 << index;
}
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, leds);
digitalWrite(latchPin, HIGH);
}
// Delay but allow exit check
void delayWithExit(unsigned long ms) {
unsigned long start = millis();
while (millis() - start < ms) {
checkForImmediateExit();
}
}
void checkForImmediateExit() {
char key = keypad.getKey();
if (key == '9') {
endGame();
}
}