WT040
Published © LGPL

Whac-A-Mole (Button Edition)

Custom Whac-A-Mole game with light- and sound effects.

AdvancedFull instructions provided3 days12,223
Whac-A-Mole (Button Edition)

Things used in this project

Hardware components

Arduino Mega 2560
Arduino Mega 2560
×1
Adafruit 1.4" 4-digit 7-segment display with I2C backpack
Choose the color you want
×2
Adafruit Big arcade button with led (60mm)
Choose 5 different colors
×5
Adafruit Audio fx board 16mb
×1
Adafruit 2.5w amp
×2
Speaker: 3W, 4 ohms
Speaker: 3W, 4 ohms
×2
Adafruit on/off button with led
×1
Adafruit potentiometer 10 k
×2
Adafruit potentiometer knob
×2
wire
×1
Countersunk screws M3 + nuts
×1
Countersunk screws M4 + nuts
×1
All 3d printed parts
×1
All lasercut MDF parts
×1
Custom Arduino mega shield
×1
Arduino mega stackable headers (for spacing)
×1
Adafruit DC step-down converter 5v 3A
×1
12v PSU
×1
Female psu bus
×1
USB-B panelmount
×1

Software apps and online services

Arduino IDE
Arduino IDE
MDF lasercut service

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
screwdriver
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

Lasercut MDF parts 6mm

Lasercut MDF parts 6mm

Lasercut MDF parts 9mm

Lasercut MDF parts 9mm

Lasercut Plexiglass parts 3mm

Lasercut Plexiglass parts 3mm

Custom Arduino Mega Shield

Custom Arduino Mega Shield

3D printed amp mount

3D printed amp mount, print 2x

3D printed arduino mega mount

3D printed arduino mega mount

3D printed back connection panel mount

3D printed back connection panel mount

3D printed back connection panel

3D printed back connection panel

3D printed Cable Clip

3D printed Cable Clip, print as many as you like

3D printed led display mount

3D printed led display mount, print 2x

3D printed led display mount lock

3D printed led display mount lock, print 4x

3D printed led display top frame

3D printed led display top frame, print 2x

3D printed top cover mount back

3D printed top cover mount back

3D printed top cover mount front

3D printed top cover mount front, print 2x

3D printed top cover mount side

3D printed top cover mount side, print 2x

Schematics

Schematic

Code

Whac_A_Mole_v1.0.0

Arduino
Arduino Code, written within Visual Studio
/*
Whac_A_Mole_v1.0.0
By WT040
*/

#include "Wire.h"
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"

Adafruit_7segment matrix1 = Adafruit_7segment();
Adafruit_7segment matrix2 = Adafruit_7segment();

#define debounceTime 10  

//declare potmeter controls
#define potmeterVolume 0
#define potmeterGameLevel 1

//declare amp power pin
#define ampPower 32

//declare buttons
#define btn1Pin 22
#define btn2Pin 24
#define btn3Pin 26
#define btn4Pin 28
#define btn5Pin 30
byte btnArray[] = {
	btn1Pin,
	btn2Pin,
	btn3Pin,
	btn4Pin,
	btn5Pin
};

#define btn1 0	// for using the btn array
#define btn2 1	// for using the btn array
#define btn3 2	// for using the btn array
#define btn4 3	// for using the btn array
#define btn5 4	// for using the btn array

//declare button leds
#define btnLed1Pin 9
#define btnLed2Pin 10
#define btnLed3Pin 11
#define btnLed4Pin 12
#define btnLed5Pin 13 
byte btnLedArray[] = {
	btnLed1Pin,
	btnLed2Pin,
	btnLed3Pin,
	btnLed4Pin,
	btnLed5Pin
};

#define btnLed1 0	// for using the btn array
#define btnLed2 1	// for using the btn array
#define btnLed3 2	// for using the btn array
#define btnLed4 3	// for using the btn array
#define btnLed5 4	// for using the btn array

//declare audio fx digitals
#define fx0TriggerPin 29
#define fx1TriggerPin 31
#define fx2TriggerPin 33
#define fx3TriggerPin 35
#define fx4TriggerPin 37
#define fx5TriggerPin 39
#define fx6TriggerPin 41
#define fx7TriggerPin 43
#define fx8TriggerPin 45
#define fx9TriggerPin 47
#define fx10TriggerPin 49
#define fxVolUpPin 51
#define fxVolDnPin 53

byte fxTriggerArray[] = {
	fx0TriggerPin,
	fx1TriggerPin,
	fx2TriggerPin,
	fx3TriggerPin,
	fx4TriggerPin,
	fx5TriggerPin,
	fx6TriggerPin,
	fx7TriggerPin,
	fx8TriggerPin,
	fx9TriggerPin,
	fx10TriggerPin
};

byte fxVolumeArray[] = {
	fxVolUpPin,
	fxVolDnPin
};

#define fx0Trigger 0	// for using the btn array
#define fx1Trigger 1	// for using the btn array
#define fx2Trigger 2	// for using the btn array
#define fx3Trigger 3	// for using the btn array
#define fx4Trigger 4	// for using the btn array
#define fx5Trigger 5	// for using the btn array
#define fx6Trigger 6	// for using the btn array
#define fx7Trigger 7	// for using the btn array
#define fx8Trigger 8	// for using the btn array
#define fx9Trigger 9	// for using the btn array
#define fx10Trigger 10	// for using the btn array
#define fxVolUp 0	// for using the btn array
#define fxVolDn 1	// for using the btn array

signed int currentPlayingSound = -1;


byte btnPressed[5], ledState[5];

//blink led
unsigned long previousMillisLedBlink = 0; //for blinking led
#define intervalLedBlink 750 
//


//game start sequence
unsigned long previousMillisGameStarting = 0;
#define intervalCountDown 1000
byte gameStartCountDown;
//

//game steps
unsigned long previousMillisGameStep = 0;
int gameStepLength = 0; //length of an individual step
int gameStepBtn = 0; //the button that's need to be pushed 
//

int valPotmeterVolume = 0;
int volume = 0;
int actualVolume = 35; //volume of the adafruit fx board
int valPotmeterGameLevel = 0;
int gameLevel;
int currentScore;
int highScore;
int highScoreAddr = 1;

byte activeBtn;
bool debug; //enable debug or not

byte wackAMoleState; //state of the game
bool SystemInitialized = false;

#define stateUnknown 0
#define waitForStart 1
#define starting 2
#define running 3
#define gameOver 4
#define stopped 5


void setup()
{
	Serial.begin(9600);

	//init amp power pin
	pinMode(ampPower, OUTPUT);
	digitalWrite(ampPower, LOW);

	//init btn pins
	for (int Pin = 0; Pin < 5; Pin++) {
		pinMode(btnArray[Pin], INPUT);
	}

	//init btn led pins
	for (int Pin = 0; Pin < 5; Pin++) {
		pinMode(btnLedArray[Pin], OUTPUT);
	}


	//init btn fx pins
	for (int Pin = 0; Pin < 11; Pin++) {
		pinMode(fxTriggerArray[Pin], OUTPUT);
		digitalWrite(fxTriggerArray[Pin], HIGH);
	}

	//init btn fx volume pins
	for (int Pin = 0; Pin < 2; Pin++) {
		pinMode(fxVolumeArray[Pin], OUTPUT);
		digitalWrite(fxVolumeArray[Pin], HIGH);
	}

	matrix1.begin(0x70);
	matrix2.begin(0x71);



	while (!eeprom_is_ready()); // Wait for EEPROM to be ready
	cli();
	highScore = eeprom_read_word((uint16_t*)(gameLevel * 2));
	sei();

	matrix1.print(0);
	matrix1.writeDisplay();
	matrix2.print(0);
	matrix2.writeDisplay();

	initializeSystem();
}

void loop()
{
	checkButtonInputs();      // when we check the switches we'll get the current state
	readVolumePotmeter();


	if (wackAMoleState == waitForStart) {
		playSound(fx0Trigger);
		if (btnPressed[btn4]) {
			wackAMoleState = starting;
		}
		readGameLevelPotmeter();
		ledBlinkStart();
	}

	if (wackAMoleState == starting) {
		startGame();

	}

	if (wackAMoleState == running) {
		playSound(fx1Trigger);
		checkNextGameStep();
	}
}


void checkNextGameStep() {
	unsigned long currentMillisGameStep = millis();
	int amountOfButtonPresses = 0;

	for (int btn = 0; btn < 5; btn++) {
		if (btnPressed[btn]) {
			amountOfButtonPresses++;
		}
	}

	if (previousMillisGameStep == 0) {
		previousMillisGameStep = currentMillisGameStep;
		digitalWrite(btnLedArray[btnLed4], LOW);

		randomSeed(analogRead(2));
		gameStepBtn = random(0, 5);
		gameStepLength = random(3000 / gameLevel, 5000 / gameLevel);
		digitalWrite(btnLedArray[gameStepBtn], HIGH);	//light up new button light
	}

	if ((currentMillisGameStep - previousMillisGameStep >= gameStepLength) || (amountOfButtonPresses > 1) ||
		(amountOfButtonPresses == 1) && !btnPressed[gameStepBtn]) {

		//Game over...
		playSound(fx2Trigger);
		wackAMoleState = gameOver;
		previousMillisGameStep = 0;


		for (int counter = 0; counter < 5; counter++) {
			analogWrite(btnLedArray[counter], 255);
		}

		for (int brightness = 255; brightness > 0; brightness--) {
			analogWrite(btnLedArray[btnLed1], brightness);
			analogWrite(btnLedArray[btnLed2], brightness);
			analogWrite(btnLedArray[btnLed3], brightness);
			analogWrite(btnLedArray[btnLed4], brightness);
			analogWrite(btnLedArray[btnLed5], brightness);
			delay(10);
		}

		analogWrite(btnLedArray[btnLed1], 0);
		analogWrite(btnLedArray[btnLed2], 0);
		analogWrite(btnLedArray[btnLed3], 0);
		analogWrite(btnLedArray[btnLed4], 0);
		analogWrite(btnLedArray[btnLed5], 0);

		Serial.println("Game OVer");
		if (currentScore > highScore) {
			while (!eeprom_is_ready()); // Wait for EEPROM to be ready
			cli();
			eeprom_write_word((uint16_t*)(gameLevel * 2), currentScore); // Let's initialize our value into EEPROM
			sei();
		}

		int counter = 0;
		do
		{
			delay(1);
			checkButtonInputs();
			counter++;

		} while ((counter < 1000) && !btnPressed[btn1] && !btnPressed[btn2 && !btnPressed[btn3] &&
			!btnPressed[btn4] && !btnPressed[btn5]]);//wait for the release of the button

		matrix1.print(0);
		matrix1.writeDisplay();
		wackAMoleState = waitForStart;

	}

	else if (btnPressed[gameStepBtn] && (amountOfButtonPresses == 1)) {
		digitalWrite(btnLedArray[gameStepBtn], LOW); //turn off led previous button


		int counter = 0;
		do
		{
			delay(1);
			checkButtonInputs();
			counter++;

		} while ((counter < 1000) && btnPressed[gameStepBtn]);//wait for the release of the button

		previousMillisGameStep = currentMillisGameStep;

		int tempStepBtn = 0;
		do
		{
			tempStepBtn = random(0, 5);
			gameStepLength = random(3000 / gameLevel, 5000 / gameLevel);
		} while (gameStepBtn == tempStepBtn);

		gameStepBtn = tempStepBtn;

		digitalWrite(btnLedArray[gameStepBtn], HIGH);	//light up new button light

		currentScore++;
		matrix1.print(currentScore);
		matrix1.writeDisplay();

		if (currentScore > highScore) {
			matrix2.print(currentScore);
			matrix2.writeDisplay();
		}

	}
}

void ledBlinkStart() //blink a led without delay
{
	unsigned long currentMillisLedBlink = millis();
	if (currentMillisLedBlink - previousMillisLedBlink >= intervalLedBlink) {
		previousMillisLedBlink = currentMillisLedBlink;
		if (ledState[0] == LOW) {
			ledState[0] = HIGH;
		}
		else {
			ledState[0] = LOW;
		}
		digitalWrite(btnLedArray[btnLed4], ledState[0]); //green led/button
	}
}

void startGame() //start sequence of the game itself
{
	unsigned long currentMillisGameStarting = millis();
	digitalWrite(btnLedArray[btnLed4], 0); //turn green led/button off
	currentScore = 0;
	if (gameStartCountDown == 0) {
		playSound(fx3Trigger);
		previousMillisGameStarting = millis();
		gameStartCountDown = 4;
		delay(300);
		matrix1.print(gameStartCountDown, DEC);
		matrix1.writeDisplay();
	}

	if (currentMillisGameStarting - previousMillisGameStarting >= intervalCountDown) {
		previousMillisGameStarting = currentMillisGameStarting;
		if (gameStartCountDown > 0) {
			gameStartCountDown--;
			matrix1.print(gameStartCountDown, DEC);
			matrix1.writeDisplay();
			if (gameStartCountDown == 0) {
				wackAMoleState = running;
			}
		}
	}
}

void initializeSystem()
{
	//add init code here...
	digitalWrite(btnLedArray[btnLed1], HIGH);
	delay(75);
	digitalWrite(btnLedArray[btnLed2], HIGH);
	delay(75);
	digitalWrite(btnLedArray[btnLed3], HIGH);
	delay(75);
	digitalWrite(btnLedArray[btnLed4], HIGH);
	delay(75);
	digitalWrite(btnLedArray[btnLed5], HIGH);
	delay(1000);
	digitalWrite(btnLedArray[btnLed5], LOW);
	delay(75);
	digitalWrite(btnLedArray[btnLed4], LOW);
	delay(75);
	digitalWrite(btnLedArray[btnLed3], LOW);
	delay(75);
	digitalWrite(btnLedArray[btnLed2], LOW);
	delay(75);
	digitalWrite(btnLedArray[btnLed1], LOW);
	delay(1000);

	debug = true;
	wackAMoleState = waitForStart;
	initVolume();
	if (actualVolume > 0) {
		enableAmpPower(true);
	}

}

void checkButtonInputs() //check for button changes
{
	static byte previousstate[5];
	static byte currentstate[5];
	static long lasttime;
	byte index;

	//debounce
	if ((lasttime + debounceTime) > millis()) {
		return;
	}
	lasttime = millis();
	//

	for (index = 0; index < 5; index++) { 
		currentstate[index] = digitalRead(btnArray[index]);   // read the button
		if (currentstate[index] != btnPressed[index]) {
			if ((btnPressed[index] == HIGH) && (currentstate[index] == LOW)) {
				if (debug) {
					Serial.print("button released: ");
					Serial.println(index + 1);
				}
				btnPressed[index] = 0;
			}

			if ((btnPressed[index] == LOW) && (currentstate[index] == HIGH)) {
				if (debug) {
					Serial.print("button pressed: ");
					Serial.println(index + 1);
				}
				btnPressed[index] = 1;
			}
		}
	}
}

void initVolume()
{
	valPotmeterVolume = (analogRead(potmeterVolume) / 30);

	if (actualVolume > valPotmeterVolume) {
		do {
			digitalWrite(fxVolumeArray[fxVolDn], LOW);
			actualVolume--;
			delay(20);
			digitalWrite(fxVolumeArray[fxVolDn], HIGH);
			delay(20);
		} while (actualVolume > valPotmeterVolume);
	}

	else if (actualVolume < valPotmeterVolume) {
		do {
			digitalWrite(fxVolumeArray[fxVolUp], LOW);
			actualVolume++;
			delay(20);
			digitalWrite(fxVolumeArray[fxVolUp], HIGH);
			delay(20);
		} while (actualVolume < valPotmeterVolume);
	}
}

void readVolumePotmeter()
{
	valPotmeterVolume = (analogRead(potmeterVolume) / 30);


	if (actualVolume > valPotmeterVolume) {
		digitalWrite(fxVolumeArray[fxVolDn], LOW);
		actualVolume--;
		delay(40);
		digitalWrite(fxVolumeArray[fxVolDn], HIGH);
		if (actualVolume == 0) {
			enableAmpPower(false);
		}
	}
	else if (actualVolume < valPotmeterVolume) {

		digitalWrite(fxVolumeArray[fxVolUp], LOW);
		if (actualVolume == 0) {
			enableAmpPower(true);
		}
		actualVolume++;
		delay(40);
		digitalWrite(fxVolumeArray[fxVolUp], HIGH);
	}
}

void readGameLevelPotmeter()
{
	valPotmeterGameLevel = analogRead(potmeterGameLevel);

	if (valPotmeterGameLevel < 102) {
		gameLevel = 1;
	}
	else if (valPotmeterGameLevel < 204) {
		gameLevel = 2;
	}
	else if (valPotmeterGameLevel < 306) {
		gameLevel = 3;
	}
	else if (valPotmeterGameLevel < 408) {
		gameLevel = 4;
	}
	else if (valPotmeterGameLevel < 510) {
		gameLevel = 5;
	}
	else if (valPotmeterGameLevel < 612) {
		gameLevel = 6;
	}
	else if (valPotmeterGameLevel < 714) {
		gameLevel = 7;
	}
	else if (valPotmeterGameLevel < 816) {
		gameLevel = 8;
	}
	else if (valPotmeterGameLevel < 918) {
		gameLevel = 9;
	}
	else {
		gameLevel = 10;
	}

	matrix1.print(gameLevel);
	matrix1.writeDisplay();

	while (!eeprom_is_ready()); // Wait for EEPROM to be ready
	cli();
	highScore = eeprom_read_word((uint16_t*)(gameLevel * 2));
	sei();
	if (highScore > -1) {
		matrix2.print(highScore);
	}
	else {
		matrix2.print(0);
	}
	matrix2.writeDisplay();
}

void playSound(int sound)
{
	if (currentPlayingSound != sound) {
		for (int counter = 0; counter < 11; counter++) {
			digitalWrite(fxTriggerArray[counter], HIGH);
		}


		digitalWrite(fxTriggerArray[sound], LOW);
		currentPlayingSound = sound;
	}
}

void enableAmpPower(bool state)
{
	digitalWrite(ampPower, state);
}

Credits

WT040

WT040

2 projects • 11 followers

Comments