CODE

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

// Pin Definitions
const int buttonPins[] = {8, 9, 10, 11, 12};
const int ledPins[] = {2, 3, 4, 5, 6};
const int buzzerPin = 7;
const int numMoles = 5;

// Game variables
int currentMole = -1;
int score = 0;
int highScore = 0;
unsigned long reactionTime = 1000;
unsigned long lastMoleTime = 0;
unsigned long gameStartTime = 0;
unsigned long gameDuration = 30000;
bool gameActive = false;

// Reaction time adjustment
const unsigned long reactionTimeDecrement = 100;
const unsigned long minReactionTime = 300;

// ------------------ Setup ------------------
void setup() {
    lcd.init();
    lcd.backlight();
    lcd.begin(16, 2);
    lcd.clear();
    lcd.setCursor(2, 0);
    lcd.print("Whack-a-Mole");
    delay(2000);
    lcd.clear();

    for (int i = 0; i < numMoles; i++) {
        pinMode(buttonPins[i], INPUT_PULLUP);
        pinMode(ledPins[i], OUTPUT);
        digitalWrite(ledPins[i], LOW);
    }

    // Buzzer
    pinMode(buzzerPin, OUTPUT);
    digitalWrite(buzzerPin, HIGH); // ปิดเสียงตอนเริ่ม

    Serial.begin(9600);
    randomSeed(analogRead(0));

    lcd.setCursor(0, 0);
    lcd.print("Press Any Btn");
    lcd.setCursor(0, 1);
    lcd.print("to Start!");
}

// ------------------ Loop ------------------
void loop() {
    unsigned long currentMillis = millis();

    // ---------- WAITING STATE ----------
    if (!gameActive) {
        for (int i = 0; i < numMoles; i++) {
            if (digitalRead(buttonPins[i]) == LOW) {
                gameActive = true;
                score = 0;
                currentMole = -1;
                reactionTime = 1000;
                lcd.clear();
                lcd.setCursor(3, 0);
                lcd.print("GAME START!");
                delay(1000); // ไม่มีเสียงตอนเริ่ม
                lcd.clear();
                gameStartTime = millis();
                lastMoleTime = millis();
            }
        }
        return;
    }

    // ---------- ACTIVE GAME STATE ----------
    unsigned long elapsedTime = currentMillis - gameStartTime;

    if (elapsedTime <= gameDuration) {
        // Progress bar
        int barLength = map(gameDuration - elapsedTime, 0, gameDuration, 0, 16);
        lcd.setCursor(0, 1);
        for (int i = 0; i < 16; i++) {
            lcd.print(i < barLength ? '-' : ' ');
        }

        // Countdown Timer
        int timeLeft = (gameDuration - elapsedTime) / 1000;
        lcd.setCursor(10, 1);
        if(timeLeft < 10) lcd.print(" ");
        lcd.print(timeLeft);
        lcd.print("s");

        // สุ่ม LED (mole)
        if (currentMillis - lastMoleTime >= reactionTime) {
            if (currentMole != -1) digitalWrite(ledPins[currentMole], LOW);
            currentMole = random(0, numMoles);
            digitalWrite(ledPins[currentMole], HIGH);
            lastMoleTime = currentMillis;
        }

        // ตรวจปุ่ม
        for (int i = 0; i < numMoles; i++) {
            if (digitalRead(buttonPins[i]) == LOW) {
                if (i == currentMole) {
                    score++;
                    lcd.setCursor(2, 0);
                    lcd.print("Score: ");
                    lcd.setCursor(9, 0);
                    lcd.print(score);
                    beep(2, 80); // เสียงถูก
                    if (reactionTime > minReactionTime) reactionTime -= reactionTimeDecrement;
                } else {
                    score--;
                    lcd.setCursor(2, 0);
                    lcd.print("Score: ");
                    lcd.setCursor(9, 0);
                    lcd.print(score);
                    beep(1, 250); // เสียงผิด
                }

                if (currentMole != -1) digitalWrite(ledPins[currentMole], LOW);
                currentMole = -1;
                delay(300);
            }
        }
    }

    // ---------- END GAME STATE ----------
    if (elapsedTime >= gameDuration) {
        lcd.clear();
        lcd.setCursor(3, 0);
        lcd.print("Game Over!"); // แสดง Game Over
        lcd.setCursor(0, 1);
        lcd.print("Final Score: ");
        lcd.setCursor(13, 1);
        lcd.print(score);

        // เอฟเฟกต์จบเกม
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < numMoles; j++) digitalWrite(ledPins[j], HIGH);
            beep(2, 200);
            delay(300);
            for (int j = 0; j < numMoles; j++) digitalWrite(ledPins[j], LOW);
            delay(300);
        }

        delay(2000); // รอสักพักก่อนโชว์ High Score

        // อัปเดต High Score และแสดง
        if (score > highScore) highScore = score;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("High Score:");
        lcd.setCursor(13, 0);
        lcd.print(highScore);
        lcd.setCursor(0, 1);
        lcd.print("Score: ");
        lcd.print(score);

        delay(3000); // แสดง High Score 3 วินาที
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Press Any Btn");
        lcd.setCursor(0, 1);
        lcd.print("to Restart");
        gameActive = false;
    }
}

// ------------------ ฟังก์ชันเสียง ------------------
void beep(int times, int duration) {
    for (int i = 0; i < times; i++) {
        digitalWrite(buzzerPin, LOW); // เปิดเสียง
        delay(duration);
        digitalWrite(buzzerPin, HIGH); // ปิดเสียง
        delay(100);
    }
}