// https://forum.arduino.cc/t/how-to-use-the-start-button/1427244/
// Original, single player project
// https://projecthub.arduino.cc/mircemk/diy-simple-arduino-whack-a-mole-game-4c2f0e
/*
Arduino Oyuna Başla
Ateşbey, June 2025
*/
//////////////////////////////////////////////////
// DEFINITIONS
//////////////////////////////////////////////////
#include <LiquidCrystal.h>
LiquidCrystal lcd(52, 50, 48, 46, 44, 42);
// Pin Definitions
const int buttonStart = A0;
int buttonPins[][8] = { // buttonPin has [player rows/arrays] of [8 elements/pins]
{ 5, 6, 7, 8, 9, 10, 11, 12 }, // player 1
{ 24, 26, 28, 30, 32, 34, 36, 38 }, // player 2
};
int ledPins[][8] = { // ledPin has [player rows/arrays] of [8 elements/pins]
{ 14, 15, 16, 17, 18, 19, 22, 23 }, // player 1
{ 25, 27, 29, 31, 33, 35, 37, 39 }, // player 2
};
const int buzzerPin = 13; // Buzzer pin (digital 13)
#define SOUND_ON 0 // 1 = sound on, 0 = sound off
// Game variables
int numMoles = 8; // Number of moles/buttons/LEDs
int inactiveMole = -1;
int currentMole[2] = {inactiveMole, inactiveMole}; // Current mole (LED) to be lit
int score[2] = {0, 0}; // ARRAY Player's score
// timing
unsigned long reactionTime = 1000; // Initial reaction time (milliseconds)
const unsigned long reactionTimeDecrement = 0; // Time to reduce reaction by (milliseconds) ZERO DOES NOTHING
const unsigned long minReactionTime = 0; // Minimum reaction time (milliseconds)
unsigned long lastMoleTime[2] = {0, 0}; // Time when the last mole was lit
unsigned long gameStartTime = 0; // Start time of the game
unsigned long gameDuration = 30000; // Total game duration (30 seconds)
unsigned long currentMillis, elapsedTime;
bool ENDGAME = false; // game over when 'true'
bool START = true; // signal game start
//////////////////////////////////////////////////
// SETUP
//////////////////////////////////////////////////
void setup()
{
lcd.begin(16, 2);
randomSeed(analogRead(A0)); // Initialize random seed from an unused analog pin
configureSerialMonitor();
configurePins();
// testLEDsButtonsBuzzerLCD(); // UNCOMMENT for testing LEDs, BUTTONS, BUZZER
configureLCD();
readStartButton();
displayScoreboard();
gameStartTime = millis(); // Record game start time
}
//////////////////////////////////////////////////
// LOOP
//////////////////////////////////////////////////
void loop()
{
updateTimer(); // get current time
updateProgressBar(); //
for (int player = 0; player < 2; player++) { // number of players
updateMoleLEDs(player);
readMoleButtons(player); // good hit, bad hit, no hit
updateScore(player);
updateMole(player);
}
}
//////////////////////////////////////////////////
// CONFIGURATION
//////////////////////////////////////////////////
void configureSerialMonitor()
{
Serial.begin(9600); // For debugging and displaying the score
}
void updateTimer()
{
currentMillis = millis(); // update time to compare to elapsed and duration
}
void configureLCD()
{
lcd.begin(16, 2);
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Butona bas");
lcd.setCursor(3, 1);
lcd.print("Oyuna Basla");
// delay(2000);
lcd.clear();
}
void configurePins()
{
// Initialize button pins and LED pins
pinMode(buttonStart, INPUT_PULLUP);
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < numMoles; i++)
{
pinMode(buttonPins[j][i], INPUT_PULLUP); // Set button pins as input with pull-up resistors
pinMode(ledPins[j][i], OUTPUT); // Set LED pins as output
}
}
pinMode(buzzerPin, OUTPUT); // Set buzzer pin as output
}
//////////////////////////////////////////////////
// UPDATE
//////////////////////////////////////////////////
void displayScoreboard()
{
lcd.setCursor(0, 0);
lcd.print("P1: P2:");
}
void updateProgressBar()
{
elapsedTime = currentMillis - gameStartTime; // time since start
if (elapsedTime <= gameDuration) // game not over
{
int barLength = map(gameDuration - elapsedTime, 0, gameDuration, 0, 16);
lcd.setCursor(0, 1);
for (int i = 0; i < 17; i++) // 0 to 16, inclusive, (before 17)
{
lcd.write(i < barLength ? 255 : 32); // write "█" for remaining time, ' ' for elapsed
// lcd.print(i < barLength ? '-' : ' '); // Print '-' for remaining time, ' ' for elapsed
}
}
}
void updateMole(int player)
{
digitalWrite(ledPins[player][currentMole[player]], LOW); // Turn off the current mole
currentMole[player] = inactiveMole; // Reset the mole to indicate no active mole
}
void updateMoleLEDs(int player)
{
// Light up a mole after a certain amount of time (based on reactionTime)
if (currentMillis - lastMoleTime[player] >= reactionTime)
{
if (currentMole[player] != inactiveMole) // = -1
{
digitalWrite(ledPins[player][currentMole[player]], LOW); // Turn off the previous mole
}
currentMole[player] = random(numMoles); // Randomly pick a mole (LED)
digitalWrite(ledPins[player][currentMole[player]], HIGH); // Light up the chosen LED
lastMoleTime[player] = currentMillis; // Update the time when the mole was lit
}
}
void updateScore(int player)
{
int scorePosition[] = {6, 15}; // players' score location on LCD
lcd.setCursor(scorePosition[player], 0); // move cursor to player score
lcd.print(score[player]); // Update the score
// score hit
if (SOUND_ON)
tone(buzzerPin, 1000, 200); // High-pitched sound (1000Hz) for 200ms
if (reactionTime > minReactionTime) // minReactionTime = zero, any time is greater
{
reactionTime -= reactionTimeDecrement; // does nothing, reactionTimeDecrement = zero
}
else
{
// wrong hit
score[player]--; // Wrong mole hit
lcd.setCursor(2, 0);
lcd.print("Score: "); // Clear the score field with extra spaces
lcd.setCursor(12, 0);
lcd.print(score[player]); // Update the score
if (SOUND_ON)
tone(buzzerPin, 400, 200); // Low-pitched sound (400Hz) for 200ms
}
}
//////////////////////////////////////////////////
// READ BUTTON PINS
//////////////////////////////////////////////////
void readMoleButtons(int player)
{
// Check if player pressed the correct mole button
for (int i = 0; i < numMoles; i++)
{
if (digitalRead(buttonPins[player][i]) == LOW) // Button pressed (LOW with INPUT_PULLUP)
{
if (i == currentMole)
{
score[player]++; // Correct mole hit
}
}
}
}
void readStartButton()
{
Serial.print("Oyun Başladı! ");
Serial.println("Press START button."); // BAŞLAT düğmesine basın = Press START button
lcd.setCursor(5, 0);
lcd.print("Press");
lcd.setCursor(5, 1);
lcd.print("START");
// wait for Button (A0) press to create LOW when pressed
while (digitalRead(buttonStart) == HIGH) {};
// wait for Button (A0) press to create LOW when pressed
START = true;
flashLEDsandBuzzer(); // warning before start
lcd.clear();
}
//////////////////////////////////////////////////
// ENDGAME
//////////////////////////////////////////////////
void endGame(int player)
{
// End the game after the set duration
if (elapsedTime >= gameDuration)
{
lcd.setCursor(3, 0);
// GAME OVER = OYUN BİTTİ
lcd.print(" GAME OVER! ");
flashLEDsandBuzzer();
}
ENDGAME = true; // if restarting, change to false
}
void flashLEDsandBuzzer()
{
if (START)
{
lcd.clear();
lcd.setCursor(5, 0);
lcd.print("READY");
delay(500);
lcd.clear();
}
for (int i = 0; i < 3; i++) // three flashes
{
for (int h = 0; h < 2; h++) // two players
{
for (int j = 0; j < numMoles; j++) // eight LEDs
{
digitalWrite(ledPins[h][j], HIGH); // LEDs ON
}
}
if (START)
{
lcd.setCursor(7, 0);
lcd.print(3 - i);
}
if (SOUND_ON)
tone(buzzerPin, 1000, 300); // Play sound
delay(500);
for (int h = 0; h < 2; h++) // two players
{
for (int j = 0; j < numMoles; j++) // eight LEDs
{
digitalWrite(ledPins[h][j], LOW); // LEDs ON
}
}
if (START)
{
lcd.clear();
}
noTone(buzzerPin); // buzzer off
delay(500);
}
START = false;
}
void startNewGame()
{
// unused
}
//////////////////////////////////////////////////
// TESTING
//////////////////////////////////////////////////
void testLEDsButtonsBuzzerLCD()
{
Serial.println("Press the button where the LED is lit.");
for (int test = 0; test < 2; test++)
{
for (int i = 0; i < numMoles; i++)
{
digitalWrite(ledPins[test][i], HIGH);
while (digitalRead(buttonPins[test][i])) {}; // stop here, wait for button
digitalWrite(ledPins[test][i], LOW);
Serial.print("Player: ");
Serial.print(test);
Serial.print(" LED: ");
Serial.print(ledPins[test][i]);
Serial.print(" BUTTON: ");
Serial.println(buttonPins[test][i]);
}
}
flashLEDsandBuzzer(); // flash buzzer and led test
while (1) {}; // stop here. do not return to program
}START