#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
int buttonPin[] = { 12, 11, 10, 9};
int ledPin[] = { 7, 6, 5, 4};
#define SPEAKER_PIN 2
const int gameTones[] = { 262, 296, 330, 349 };


byte urutan[225] = {0};
byte indexgame = 0;
byte score = 0;

uint8_t lampIcon[8] = {
  0b00000, //
  0b00000, //
  0b01110, //  000
  0b11011, // 00 00
  0b11101, // 000 0
  0b11101, // 00000
  0b11111, // 00000
  0b01010, //  0 0
};

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.createChar(3, lampIcon);
  lcd.begin(16, 2);
  pinMode(SPEAKER_PIN, OUTPUT);
  for (byte i = 0; i < 4; i++) {
    pinMode(buttonPin[i], INPUT_PULLUP);
    pinMode(ledPin[i], OUTPUT);
  }
  start();
}

void start() {
  tone(SPEAKER_PIN, 262, 50);
  delay(50);
  tone(SPEAKER_PIN, 330, 100);

  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("MEMORY GAMES");
  lcd.setCursor(1, 1);
  lcd.print("ORDER THE LAMP");
  delay(1500);
  tone(SPEAKER_PIN, 330, 100);

  lcd.clear();
  lcd.setCursor(4, 0);
  lcd.print("MADE BY");
  lcd.setCursor(5, 1);
  lcd.print("REAZON");
  delay(1500);
  tone(SPEAKER_PIN, 330, 100);

  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("FIND CORRECT");
  lcd.setCursor(1, 1);
  lcd.print("ORDER OF LAMP!");
  delay(1500);

  tone(SPEAKER_PIN, 330, 100);
  lcd.clear();
  lcd.setCursor(3, 0);
  lcd.print("LETS BEGIN!");
  delay(1500);

  lcd.clear();
  delay(700);

}


void playOrder() {

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("SCORE :");
  lcd.setCursor(7, 0);
  lcd.print(score);

  lcd.setCursor(5, 1);
  lcd.print("[____]");
  for (byte i = 0; i < indexgame; i++) {

    lcd.setCursor(6 + urutan[i], 1);
    lcd.print("\x03");
    digitalWrite(ledPin[urutan[i]], HIGH);
    tone(SPEAKER_PIN, gameTones[urutan[i]], 300);
    delay(300);
    digitalWrite(ledPin[urutan[i]], LOW);
    noTone(SPEAKER_PIN);
    lcd.setCursor(6 + urutan[i], 1);
    lcd.print("_");
    delay(500);
  }

}

byte readButtons() {
  while (true) {
    for (byte i = 0; i < 4; i++) {
      byte button = buttonPin[i];
      if (digitalRead(button) == LOW) {
        return i;
      }
    }
    delay(1);
  }
}

bool isCorrect() {
  for (byte u = 0; u < indexgame; u++) {

    lcd.setCursor(7, 0);
    lcd.print(score);

    byte pressed = readButtons();
    lcd.setCursor(6 + pressed, 1);
    lcd.print("\x03");
    tone(SPEAKER_PIN, gameTones[pressed], 300);
    digitalWrite(ledPin[pressed], HIGH);
    delay(300);
    lcd.setCursor(6 + pressed, 1);
    lcd.print("_");
    digitalWrite(ledPin[pressed], LOW);
    noTone(SPEAKER_PIN);
    if (pressed != urutan[u]) return false;
    score++;
  }

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("CORRECT!! LETS");
  lcd.setCursor(0, 1);
  lcd.print("TO NEXT LEVEL!");
  tone(SPEAKER_PIN, 349, 50);
  delay(50);
  tone(SPEAKER_PIN, 330, 100);
  for (byte u = 4; u > 0; u--) {
    digitalWrite(ledPin[u - 1], HIGH);
    delay(200);
    digitalWrite(ledPin[u - 1], LOW);
  }
  return true;
}

void gameOver() {
  lcd.setCursor(0, 0);
  lcd.print("NOO.. YOU FAIL!");

  tone(SPEAKER_PIN, 462, 50);
  delay(50);
  tone(SPEAKER_PIN, 100, 500);

  for (byte i = 0; i < 2; i++) {
    for (byte i = 0; i < 4; i++) {
      digitalWrite(ledPin[i], HIGH);
      lcd.setCursor(6 + i, 1);
      lcd.print("\x03");
    }
    delay(200);
    for (byte i = 0; i < 4; i++) {
      digitalWrite(ledPin[i], LOW);
      lcd.setCursor(6 + i, 1);
      lcd.print("_");
    }
    delay(200);

  }
  delay(500);
  indexgame = 0;
  score = 0;
}

void loop() {
  // put your main code here, to run repeatedly:
  for (byte i = 0; i < indexgame; i++) {
    byte rand = random(0, 4);
    urutan[i] = rand;
  }

  indexgame++;
  playOrder();

  if (!isCorrect()) {
    gameOver();
  }
  delay(400);
}