#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);
}
}