Maker.io main logo

DIY Simple Arduino Whack-a-Mole Game

2025-08-18 | By Mirko Pavleski

License: General Public License Displays Arduino

A "Whack-a-Mole" game is a classic arcade-style game where moles pop up randomly from holes, and the player uses a mallet or similar tool to "whack" the moles before they disappear.

cfvbh

The goal is to hit the moles as they appear, to score points while avoiding hitting any other targets. It’s typically timed, and the game ends after a certain period, with the player's score displayed. This time, I will present you with a simple way to make a Whack-a-Mole portable game using an Arduino microcontroller.

 

In this case, the "moles" will be small buttons with built-in LEDs in different colors. A randomly selected button lights up for a certain time when we have to press it to get a point.

This project is sponsored by PCBWay. They have all the services you need to create your project, whether it's a school project or a complex professional project. On PCBWay you can share your experiences or get inspiration for your next project. They also provide completed surface mount SMT PCB assembly service and ISO9001 quality control.

The device is very simple to make and consists of a few components:

hjkl

- Arduino Nano microcontroller module

- 5 Buttons with built-in LEDs with different colours

- LCD display with 16x2 characters and I2C communication protocol

- 5 resistors 470 Ohms

- and Buzzer

jl

The start of the game is indicated on the LCD display, and then the buttons start to light up in random order. With each successful press of a button while it is lit, we get a point. If we press the wrong button, we get a negative point, and the score is reduced by 1.

mn

Successful or unsuccessful activation of the button (actually catching the mole), as well as other states of the game, are signaled by an appropriate sound generated by the buzzer. When the game starts, the first line shows the current score, while the second line shows a bar graph that gradually decreases over time and disappears after exactly thirty seconds, the same time the game lasts. This way, we have a visual representation of the remaining time in the game.

v

To make the game more fun and addictive, over time, the duration of the lighting of the corresponding button is reduced; in fact, the game is accelerated. At the end of the game, all the buttons flash several times, and this is accompanied by appropriate sounds. Then the final score appears on the screen.

jio

After a 5-second break, a new game starts.

gh

I updated the code so that the player has a better overview of the game duration, and now the bar, instead of 16 horizontal bars, consists of 80 vertical bars.

dsgfd

And finally, a quick conclusion. This is a really simple Arduino version of the Whack-a-Mole arcade game made with just a few components, but endlessly fun and addictive, great for testing and practicing reflexes. I installed the device in a suitable box made of PVC board with a thickness of 3 to 5mm and covered with self-adhesive colored wallpaper. Also, for compactness and mobility, the game is powered by 2 lithium batteries (7.4V).

hj

Copy Code
/*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;
  }
}
製造商零件編號 A000005
ARDUINO NANO ATMEGA328 EVAL BRD
Arduino
Add all DigiKey Parts to Cart
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.