#include "pitches.h"
#include <Adafruit_SSD1306.h>
#define TOT_PIN 4
#define SPEAKER_PIN 7
#define ADDR 0x3C
// Width and Height of the screen
#define W 128
#define H 64
// Letter width in the screen
#define LETTER_W 6
#define CENTER_TXT_SIZE 2
#define MAX_GAME_LENGTH 50
// Oled screen declaration
Adafruit_SSD1306 oled(W, H);
// The following arrays corresponds to the pins of
// LEDs, buttons and tones
// The entry i of each array corresponds to the same color
// 0 -> RED, 1 -> GREEN, 2 -> BLUE, 3 -> YELLOW
const uint8_t led_pins[TOT_PIN] = { 3, 4, 5, 6 };
const uint8_t button_pins[TOT_PIN] = { 8, 9, 10, 11 };
const int tones[TOT_PIN] = { NOTE_G3, NOTE_C4, NOTE_E4, NOTE_G5 };
// 50 is the maximum
uint8_t previous_plays[MAX_GAME_LENGTH] = {0};
uint8_t last_play = 0;
// Oled to print strings
String header_string = "Welcome :)";
String center_string;
int min_x;
int center_str_coord = 0;
bool is_game_started = false;
void setup() {
if (!oled.begin(SSD1306_SWITCHCAPVCC, ADDR)) {
while (true);
}
oled.setTextWrap(false);
oled.clearDisplay();
pinMode(SPEAKER_PIN, OUTPUT);
for (byte i = 0; i < TOT_PIN; i++) {
// LED pins are OUTPUT
pinMode(led_pins[i], OUTPUT);
// Button pins are INPUT_PULLUP
// This way I can avoid a resistor
pinMode(button_pins[i], INPUT_PULLUP);
}
// A2 is free, so we use it as random seed
randomSeed(analogRead(A2));
}
void game_over() {
center_string = "You lost :(";
center_str_coord = 0;
// Displays You Lost on display
disp_all(false);
delay(200);
// Plays Game Over 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);
delay(500);
}
// Renders header_string in the upper part of the display
void render_header_string() {
oled.setFont();
oled.setTextSize(1);
oled.setTextColor(WHITE);
uint16_t w = 0;
oled.getTextBounds(header_string, 0, 0, 0, 0, &w, 0);
oled.setCursor((W - w) / 2, 0);
oled.print(header_string);
}
// Renders center_string in the center part of the display
// The variable set_center_string is used for positioning: if it's > W then the text gets centered
void set_center_string() {
oled.setFont();
oled.setTextSize(CENTER_TXT_SIZE);
oled.setTextColor(WHITE);
uint16_t w = 0, h = 0;
oled.getTextBounds(center_string, 0, 0, 0, 0, &w, &h);
if (center_str_coord > W) {
// center the text
center_str_coord = (W - w) / 2;
}
oled.setCursor(center_str_coord, (H - h) / 2);
oled.print(center_string);
}
// Turns off led and buzzer
void turn_off(byte i) {
noTone(SPEAKER_PIN);
digitalWrite(led_pins[i], LOW);
}
// Turns on led and buzzer
void turn_on(byte i) {
digitalWrite(led_pins[i], HIGH);
tone(SPEAKER_PIN, tones[i], 255); // Plays 262Hz tone for 0.250 seconds
}
// Turns on led and buzzer, after 255ms turns them off
void play(byte i) {
turn_on(i);
delay(255);
turn_off(i);
}
// Displays stuff on the oled screen
void disp_all (bool render_header) {
oled.clearDisplay();
if (render_header)
render_header_string();
set_center_string();
oled.display();
}
// Loops until a button is pressed, then it returns the index of that button (index = color)
byte read_buttons(bool disp) {
if (disp) {
center_string = "Press a button to play!";
center_str_coord = W;
min_x = center_string.length() * LETTER_W * CENTER_TXT_SIZE;
}
while (true) {
for (byte i = 0; i < 4; i++) {
if (digitalRead(button_pins[i]) == LOW) {
return i;
}
}
if (disp) {
// Not currently playing
disp_all(true);
center_str_coord -= LETTER_W;
if (center_str_coord < -min_x)
center_str_coord = W;
}
delay(1);
}
}
// Checks if the user combination is correct
bool check_user_play() {
for(uint8_t i=0; i<last_play; i++) {
byte expected_button = previous_plays[i];
byte read_button = read_buttons(false);
play(read_button);
if(read_button != expected_button) {
// Wrong button pressed
return false;
}
}
return true;
}
// Plays levelup sound
void level_up() {
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);
}
void reset() {
header_string = "Last score: ";
last_play--;
header_string = header_string+last_play;
last_play = 0;
is_game_started = false;
}
void loop() {
if (!is_game_started) {
// Game is not started yet, wait for random input
read_buttons(true);
is_game_started = true;
delay(200);
} else {
// Game is ON
if (last_play >= MAX_GAME_LENGTH) {
// Player won!
reset();
center_string = "You won :)";
center_str_coord = 0;
disp_all(false);
level_up();
delay(2000);
return;
}
center_string = "Score: ";
center_string = center_string + last_play;
center_str_coord = W+1;
disp_all(false);
previous_plays[last_play] = random(0, 4);
last_play++;
for(uint8_t i=0; i<last_play; i++) {
delay(100);
play(previous_plays[i]);
}
if (!check_user_play()) {
// Player lost
reset();
game_over();
} else {
// Levelup!
delay(200);
level_up();
}
}
}