Object-Oriented Game Development – Whack-a-Mole
Learn more about Object Oriented Game Devlopment using Whack-a-Mole!
Lesson: Object-Oriented Game Development – Whack-a-Mole
In this lesson we will study how Object-Oriented Programming (OOP) principles can be applied to build a fun game: Whack-a-Mole.
We will not just play the game, but also learn the design concepts behind it, how the code is organized, and why OOP is powerful for games and larger software projects.
This lesson combines:
- Teaching text to explain programming ideas step by step.
- Code examples in JavaScript.
- FRQ-style practice scattered throughout to simulate AP CSP exam thinking.
- Connections to Big Ideas (abstraction, data, algorithms, impact).
You should have made a copy of the game to follow along and complete the hacks at the end of the lesson.
Learning Objectives
By the end of this lesson, you should and will be able to:
- Define and identify OOP principles (encapsulation, inheritance, polymorphism, composition).
- Explain how OOP helps organize complex programs like games.
- Understand how local storage can persist data beyond runtime.
- Analyze and modify code to meet new requirements (FRQs).
The Whack-a-Mole Concept
In Whack-a-Mole, the computer manages a grid of holes. Randomly, a mole (or sometimes a power-up) appears in a hole. The player must click or tap the mole before it disappears.
Key Rules:
- Moles give points when clicked.
- Power-ups may give bonus points or extra time.
- The game tracks score and saves the high score using local storage.
This simple idea is perfect for studying OOP: each part of the game (holes, moles, power-ups, score) can be represented as an object.
OOP Structure
We will design the game using classes:
Game
→ manages the entire program (composition).Hole
→ represents a place where something can appear.Entity
(abstract base class) → shared logic for anything that appears in a hole.Mole
→ a specific type ofEntity
.PowerUp
→ another type ofEntity
.
👉 This is an example of inheritance: both Mole
and PowerUp
inherit from Entity
.
👉 This is also composition: the Game
is composed of multiple Hole
objects.
Spawning Entities
The game must randomly choose where to put new moles or power-ups. This is handled inside the Game
class.
Here is the method that chooses an empty hole and places something in it:
spawnEntity() {
let emptyHoles = this.holes.filter(h => !h.entity);
if (emptyHoles.length > 0) {
let hole = emptyHoles[Math.floor(Math.random() * emptyHoles.length)];
if (Math.random() < 0.8) {
hole.entity = new Mole(hole, "normal");
} else {
hole.entity = new PowerUp(hole, "bonus");
}
}
}
Cell In[5], line 2
let emptyHoles = this.holes.filter(h => !h.entity);
^
SyntaxError: invalid syntax. Perhaps you forgot a comma?
Teaching Explanation
filter()
→ removes all holes that already have something inside.Math.random()
→ makes the game unpredictable.- If chance < 0.8, spawn a mole (80% chance). Otherwise, spawn a power-up (20% chance).
👉 Notice how the Game class does not directly create pixels on screen. Instead, it uses the object system: Hole
manages its own location, Entity
manages its own lifespan, etc.
📝 FRQ Checkpoint 1
Prompt:
- Explain why it is better for the
Game
class to delegate work to theHole
andEntity
classes instead of doing everything itself. - Modify the logic so that
GoldenMole
appears 5% of the time, worth double points. - Justify how this change demonstrates polymorphism.
Handling Player Input
When a player clicks, the game checks whether the click intersects with a mole or power-up. If so, it calls the object’s onHit()
method.
handleClick(mx, my) {
this.holes.forEach(hole => {
if (hole.entity &&
mx >= hole.x - hole.size/2 &&
mx <= hole.x + hole.size/2 &&
my >= hole.y - hole.size/2 &&
my <= hole.y + hole.size/2) {
hole.entity.onHit(this);
}
});
}
Teaching Explanation
- Each
Entity
object controls what happens when hit (encapsulation). - The
Game
just forwards the event. - Polymorphism:
onHit()
means something different for aMole
(gain points) versus aPowerUp
(bonus effects).
Debugging
Debugging is a critical part of learning to code games. Below are step-by-step debugging tips for different parts of the Whack-a-Mole lesson. Use these to find and fix problems as you build. There will be debugging steps all through the lesson.
🟢 Game Setup Issues
- Problem: Nothing appears on the canvas.
✅ Check: Did you correctly reference the<canvas id="gameCanvas">
in your HTML?
✅ Check: Did you create theGame
object at the bottom withnew Game("gameCanvas")
?
✅ Tool: Useconsole.log(this.canvas)
inside theGame
constructor to confirm the canvas is found.
🟡 Entity (Mole/PowerUp) Bugs
- Problem: Moles don’t disappear.
✅ Check: IsEntity.update()
being called each frame?
✅ Check: Does the code setthis.hole.entity = null
when the mole times out or gets hit?
✅ Tool: Addconsole.log("Mole expired")
inside the update to verify.
📝 FRQ Checkpoint 2
Prompt:
- Suppose the player clicks slightly outside the hole’s boundary. How does the code prevent counting that as a hit?
- Write pseudocode for a new entity
Bomb
that subtracts points when hit. - Explain how this uses inheritance and overriding methods.
Persisting High Scores with Local Storage
Without local storage, scores vanish when you refresh the page. Local storage lets us keep the high score.
Example implementation:
this.highScore = localStorage.getItem("highScore") || 0;
addScore(points) {
this.score += points;
if (this.score > this.highScore) {
this.highScore = this.score;
localStorage.setItem("highScore", this.highScore);
}
}
Debugging
🔵 Score & Storage
-
Problem: High score never saves.
✅ Check: IslocalStorage.setItem("whackAMoleHighScore", this.highScore);
called at game over?
✅ Check: Isthis.highScore = localStorage.getItem("whackAMoleHighScore") || 0;
in the constructor?
✅ Tool: Open DevTools → Application → Local Storage to see if the key is set. -
Problem: Multiplier doesn’t reset.
✅ Check: InupdateGame()
, does the code correctly checkif (Date.now() > this.multiplierEnd)
?
Teaching Explanation
localStorage.setItem(key, value)
→ saves data to browser storage.localStorage.getItem(key)
→ retrieves saved data.- This persists across sessions (until the user clears it).
👉 This is an example of data abstraction: we don’t worry about how the browser stores data, we just use a simple API.
📝 FRQ Checkpoint 3
Prompt:
- Explain why local storage is useful in a game but not always appropriate in secure applications.
- Modify the game so it also saves the last score in addition to high score.
- How does this relate to Big Idea 5: Impact of Computing?
Big Ideas Connections
- Big Idea 1: Creative Development → Using OOP to design reusable code.
- Big Idea 2: Data → High score stored in local storage.
- Big Idea 4: Algorithms → Random entity spawning and event handling.
- Big Idea 5: Impact → Ethical considerations of storing persistent data.
This project demonstrates how classroom theory applies to real-world interactive applications.
Reflection Questions
- How does encapsulation make debugging easier?
- How could inheritance reduce duplicated code in larger games?
- Why is polymorphism important when designing new entity types?
- What are some limitations of local storage for more advanced games?
Hack Homework
Try these hacks to extend your Whack-a-Mole game and deepen your understanding of OOP:
- Add a New Mole Type
- Create a blue mole worth +50 points but also speeds up the game.
- Change the Grid Size
- Modify the game to support a 4×4 grid instead of 3×3.
- Add Sound Effects
- Play a hit sound when a mole is clicked and a game over sound when lives reach 0.
- Track Combos
- Add a combo counter that rewards extra points if you hit 3 or more moles in a row without missing.
- FRQ-style Reflection
- Explain in 2–3 sentences how the game uses inheritance and polymorphism.
- Local Storage Challenge
- Store the player’s last 5 game scores in
localStorage
and display them on the game over screen.
- Store the player’s last 5 game scores in