#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/*
* Read about EEPROM. Keep high score?
* Better random seed. https://forum.arduino.cc/t/the-reliable-but-not-very-sexy-way-to-seed-random/65872/52
*/
// Board stuff
const byte ledPins[4] = {9, 10, 11, 12};
const byte buttonPins[4] = {5, 6, 7, 8};
const byte soundPin = 3;
const byte WIDTH = 128;
const byte HEIGHT = 64;
const byte OLED_RESET = 4;
Adafruit_SSD1306 display(WIDTH, HEIGHT, &Wire, OLED_RESET);
// Game stuff
const byte maxGameLength = 250;
const int soundFreq[4] = {523, 659, 784, 1047};
byte Q[maxGameLength];
byte correctInput = 0;
byte buttonPressed = 0;
byte gameIndex = 0;
byte inputCount = 0;
bool buttonDown = false;
int nextButtonDelay = 800;
unsigned long currentTime = 0;
unsigned long prevTime = 0;
unsigned long lastBounceTime = 0;
const byte bounceDelay = 50;
void turnOnLed(byte ledIndex) {
digitalWrite(ledPins[ledIndex], HIGH);
tone(soundPin, soundFreq[ledIndex], 30);
}
void turnOnAllLeds(int duration) {
tone(soundPin, 300, duration);
for (byte i = 0; i < 4; i++) {
digitalWrite(ledPins[i], HIGH);
}
delay(duration);
for (byte i = 0; i < 4; i++) {
digitalWrite(ledPins[i], LOW);
}
}
void newGameSequence() {
const byte seq[4] = {0, 2, 1, 3};
int duration = 150;
for (byte i = 0; i < 4; i++) {
if (i == 3) { duration = 300; }
tone(soundPin, soundFreq[seq[i]], duration);
digitalWrite(ledPins[seq[i]], HIGH);
delay(duration);
digitalWrite(ledPins[seq[i]], LOW);
}
delay(2000);
}
void nextGameConfirm() {
while (digitalRead(buttonPins[0])) {
delay(50);
}
showScore();
newGameSequence();
}
void gameOver() {
//Serial.println(F("Game Over!"));
display.setTextSize(1);
display.setCursor(64, 4);
display.print(F("Game Over!"));
display.setCursor(64, 16);
display.print(F("R for next"));
display.display();
turnOnAllLeds(1000);
gameIndex = 0;
inputCount = 0;
nextButtonDelay = 800;
nextGameConfirm();
}
void showScore() {
display.clearDisplay();
display.setTextSize(3);
display.setCursor(2, 2);
if (inputCount < 10) { display.print('0'); }
if (inputCount < 100) { display.print('0'); }
display.print(inputCount);
display.display();
}
void setup() {
Serial.begin(9600);
pinMode(soundPin, OUTPUT);
for (byte i = 0; i < 4; i++) {
pinMode(ledPins[i], OUTPUT);
pinMode(buttonPins[i], INPUT_PULLUP);
}
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3d)) { // Address 0x3C for 128x32
Serial.println(F("SSD1306 alloc fail"));
for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(2, 2);
display.print(F("S-MASTER"));
display.setTextSize(1);
display.print(F("tm"));
display.setCursor(2, 20);
display.print(F("RED to start!"));
display.display();
nextGameConfirm();
showScore();
randomSeed(micros() + analogRead(A3) + analogRead(A4));
}
void loop() {
if (gameIndex == maxGameLength) {
// TODO: What to do here?
gameOver();
}
currentTime = millis();
if (currentTime - prevTime >= nextButtonDelay) {
if (gameIndex < 40) {
nextButtonDelay -= 8;
} else if (gameIndex >= 40 && gameIndex <= 90) {
nextButtonDelay -= 3;
} else {
nextButtonDelay -= 1;
}
digitalWrite(ledPins[Q[gameIndex - 1]], LOW);
byte newRandom = random(0, 4);
if (newRandom == Q[gameIndex - 1]) {
// don't repeat the same LED twice
newRandom = (newRandom + 1) % 4;
}
Q[gameIndex] = newRandom;
turnOnLed(newRandom);
gameIndex++;
prevTime = currentTime;
}
correctInput = Q[inputCount];
// Check if any button has been pressed down
if (!buttonDown) {
for (int i = 0; i < 4; i++) {
// digitalRead() returning LOW means the button is pressed down
// because the buttons are set as INPUT_PULLUP
if (digitalRead(buttonPins[i]) == LOW) {
buttonPressed = i;
//Serial.print(F("Expected: "));
//Serial.println(correctInput);
//Serial.print(F("Button pressed: "));
//Serial.println(buttonPressed);
buttonDown = true;
lastBounceTime = millis();
}
}
}
if (buttonDown) {
if (digitalRead(buttonPins[buttonPressed]) == LOW) {
lastBounceTime = millis();
}
if ((millis() - lastBounceTime) > bounceDelay) {
buttonDown = false;
if (buttonPressed == correctInput) {
inputCount++;
showScore();
} else {
gameOver();
}
}
}
}