Mirko Pavleski
Published © GPL3+

DIY Simple Arduino Whack-a-Mole Game

Imple to make Arduino version of the Whack-a-Mole arcade game wich consist a few components.

BeginnerFull instructions provided2 hours191
DIY Simple Arduino Whack-a-Mole Game

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
I2C 16x2 Arduino LCD Display Module
DFRobot I2C 16x2 Arduino LCD Display Module
×1
Buzzer
Buzzer
×1
Illuminated Pushbutton Switch, Momentary Spring Return
Illuminated Pushbutton Switch, Momentary Spring Return
×5
Through Hole Resistor, 470 ohm
Through Hole Resistor, 470 ohm
×5

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic

...

Code

Code

C/C++
...
/*Arduino Whack-A-Mole Game 
by mircemk, June 2025
*/

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

// Pin Definitions
const int buttonPins[] = {8, 9, 10, 11, 12}; // Button pins
const int ledPins[] = {2, 3, 4, 5, 6};      // LED pins
const int buzzerPin = 13;                   // Buzzer pin (digital 13)
const int numMoles = 5;                     // Number of moles/buttons/LEDs

// Game variables
int currentMole = -1;         // Current mole (LED) to be lit
int score = 0;                // Player's score
unsigned long reactionTime = 1000;  // Initial reaction time (milliseconds)
unsigned long lastMoleTime = 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)

// Reaction time adjustment
const unsigned long reactionTimeDecrement = 100; // Time to reduce reaction by (milliseconds)
const unsigned long minReactionTime = 300;       // Minimum reaction time (milliseconds)

void setup() {
  lcd.backlight();
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(2, 0);
  lcd.print("Whack-a-Mole");
  lcd.setCursor(3, 1);
  lcd.print("by mircemk");
  delay(2000);
  lcd.clear();

  // Initialize button pins and LED pins
  for (int i = 0; i < numMoles; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);  // Set button pins as input with pull-up resistors
    pinMode(ledPins[i], OUTPUT);           // Set LED pins as output
    digitalWrite(ledPins[i], LOW);         // Turn off all LEDs initially
  }

  pinMode(buzzerPin, OUTPUT); // Set buzzer pin as output

  Serial.begin(9600); // For debugging and displaying the score
  randomSeed(analogRead(0)); // Initialize random seed from an unused analog pin
  Serial.println("Whack-a-Mole Game Started!");

  lcd.setCursor(2, 0);
  lcd.print("GAME STARTED!");
  delay(500);
  lcd.clear();

  gameStartTime = millis(); // Record game start time
}

void loop() {
  unsigned long currentMillis = millis();  // Get the current time

  // Update the progress bar
  unsigned long elapsedTime = currentMillis - gameStartTime;
  if (elapsedTime <= gameDuration) {
    int barLength = map(gameDuration - elapsedTime, 0, gameDuration, 0, 16);
    lcd.setCursor(0, 1);
    for (int i = 0; i < 16; i++) {
      lcd.print(i < barLength ? '-' : ' '); // Print '-' for remaining time, ' ' for elapsed
    }
  }

  // Light up a mole after a certain amount of time (based on reactionTime)
  if (currentMillis - lastMoleTime >= reactionTime) {
    if (currentMole != -1) {
      digitalWrite(ledPins[currentMole], LOW);  // Turn off the previous mole
    }
    currentMole = random(0, numMoles);  // Randomly pick a mole (LED)
    digitalWrite(ledPins[currentMole], HIGH); // Light up the chosen LED

    lastMoleTime = currentMillis;  // Update the time when the mole was lit
  }

  // Check if the player pressed the correct button for the lit mole
  for (int i = 0; i < numMoles; i++) {
    if (digitalRead(buttonPins[i]) == LOW) {  // Button pressed (LOW due to INPUT_PULLUP)
      if (i == currentMole) {
        score++;  // Correct mole hit
 lcd.setCursor(2, 0);
lcd.print(" Score:      "); // Clear the score field with extra spaces
lcd.setCursor(12, 0);
lcd.print(score); // Update the score

        tone(buzzerPin, 1000, 200);  // High-pitched sound (1000Hz) for 200ms

        if (reactionTime > minReactionTime) {
          reactionTime -= reactionTimeDecrement;
        }
      } else {
        score--;  // Wrong mole hit
 lcd.setCursor(2, 0);
lcd.print(" Score:      "); // Clear the score field with extra spaces
lcd.setCursor(12, 0);
lcd.print(score); // Update the score

        tone(buzzerPin, 400, 200);  // Low-pitched sound (400Hz) for 200ms
      }

      digitalWrite(ledPins[currentMole], LOW);  // Turn off the current mole
      currentMole = -1;  // Reset the mole to indicate no active mole
      delay(500);  // Short delay to debounce button press
    }
  }

  // End the game after the set duration
  if (elapsedTime >= gameDuration) {
lcd.setCursor(3, 0);
lcd.print("Game Over!   ");
lcd.setCursor(0, 1);
lcd.print("Final Score: ");
lcd.setCursor(13, 1);
lcd.print("   ");           // Clear any leftover characters
lcd.setCursor(13, 1);
lcd.print(score);           // Print the final score

// Flash all LEDs and play a sound three times
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < numMoles; j++) {
    digitalWrite(ledPins[j], HIGH);
  }
  tone(buzzerPin, 1000, 300);  // Play sound
  delay(300);                  // Keep LEDs on
  for (int j = 0; j < numMoles; j++) {
    digitalWrite(ledPins[j], LOW);
  }
  delay(300);                  // Keep LEDs off
}

delay(2000);                   // Pause before resetting the game
lcd.clear();
score = 0;
currentMole = -1;
for (int i = 0; i < numMoles; i++) {
  digitalWrite(ledPins[i], LOW);
}
lcd.setCursor(0, 0);
lcd.print(" Start New Game");
delay(5000);
lcd.clear();
gameStartTime = millis();
reactionTime = 1000;
  }
}

Credits

Mirko Pavleski
188 projects • 1451 followers

Comments