-
Notifications
You must be signed in to change notification settings - Fork 185
added Whack a Mole Game #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,42 @@ | ||||||||||||||||||||||||||||||
| # 🦫 Whack-a-Mole Game | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## Description | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| A fun and fast-paced reflex game where players “whack” moles as they randomly pop out of holes. Each successful hit earns you points — but be quick, because the mole disappears in a second! The game runs for a limited time, making it both exciting and challenging. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## Features | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - Start button to begin the game | ||||||||||||||||||||||||||||||
| - Random mole appearances in 9 different holes | ||||||||||||||||||||||||||||||
| - Real-time score tracking | ||||||||||||||||||||||||||||||
| - Automatic 30-second game duration | ||||||||||||||||||||||||||||||
| - Alert message showing final score when time’s up | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## Concepts Practiced | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - DOM manipulation and dynamic element creation | ||||||||||||||||||||||||||||||
| - Event listeners for interactive clicks | ||||||||||||||||||||||||||||||
| - Randomization and timing using `Math.random()` and `setTimeout()` | ||||||||||||||||||||||||||||||
| - Basic game loop logic | ||||||||||||||||||||||||||||||
| - CSS animations for mole movement | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ## Bonus Challenge | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| - Add **levels** (Easy, Medium, Hard) that increase mole speed | ||||||||||||||||||||||||||||||
| - Add a **visible countdown timer** | ||||||||||||||||||||||||||||||
| - Store and display **high scores** using `localStorage` | ||||||||||||||||||||||||||||||
| - Add **sound effects** when the mole is clicked | ||||||||||||||||||||||||||||||
| - Use **images or emojis** to customize the mole design | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+32
|
||||||||||||||||||||||||||||||
| - Add **levels** (Easy, Medium, Hard) that increase mole speed | |
| - Add a **visible countdown timer** | |
| - Store and display **high scores** using `localStorage` | |
| - Add **sound effects** when the mole is clicked | |
| - Use **images or emojis** to customize the mole design | |
| - Add **power-ups** (e.g., double points, freeze mole, extra time) | |
| - Add a **visible countdown timer** | |
| - Store and display **high scores** using `localStorage` | |
| - Add **sound effects** when the mole is clicked | |
| - Use **images or emojis** to customize the mole design | |
| - Add a **leaderboard** to show top scores | |
| - Implement a **multiplayer mode** (local or online) | |
| - Add **custom themes** or color schemes | |
| - Improve **accessibility** (keyboard controls, screen reader support) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Whack-a-Mole — Reflex Game 🦫 | ||
|
|
||
| This is a fun and interactive **Whack-a-Mole** reflex game built using **HTML, CSS, and modern JavaScript (ES Modules)**. | ||
| Players must click (or “whack”) the mole as it randomly pops out of one of the holes before it disappears — testing their speed and reaction time! | ||
|
|
||
| ## Features | ||
|
|
||
| - 🎯 **Fast-paced gameplay** — moles appear randomly across the board. | ||
| - ⏱️ **30-second timer** — challenge yourself to score as high as possible within the time limit. | ||
|
||
| - 🧠 **Live score tracking** — updates instantly with each successful hit. | ||
| - 💥 **Smooth animations** — CSS-based mole pop-up effect for a polished experience. | ||
| - ⚡ Built using **pure HTML, CSS, and JavaScript (no libraries or frameworks)**. | ||
|
|
||
| ## Files | ||
|
|
||
| - `index.html` — main structure and layout of the game board. | ||
| - `styles.css` — visual design, layout grid, and animations. | ||
| - `index.mjs` — game logic for mole appearance, scoring, and timer handling. | ||
|
|
||
| ## How to Play | ||
|
|
||
| 1. Open `index.html` in your browser (Chrome, Edge, or Firefox recommended). | ||
| 2. Click the **“Start Game”** button to begin. | ||
| 3. Moles will pop up randomly in different holes — **click them as fast as you can!** | ||
| 4. Each successful hit gives you **+1 point**. | ||
| 5. After 30 seconds, the game ends and your **final score** is displayed. | ||
| 6. Click **“Start Game”** again to play another round. | ||
|
|
||
| ## Notes | ||
|
|
||
| - Uses **JavaScript’s `setTimeout()`** for mole timing and random placement. | ||
| - A great beginner project to understand **event handling, DOM updates, and randomization**. | ||
| - Fully responsive — works on both **desktop and mobile** devices. | ||
|
|
||
| --- | ||
|
|
||
| ✨ **Test your reflexes and see how many moles you can whack!** | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <title>Whack-a-Mole Game 🦫</title> | ||
| <link rel="stylesheet" href="styles.css" /> | ||
| </head> | ||
| <body> | ||
| <h1>Whack-a-Mole 🦫</h1> | ||
|
|
||
| <div class="scoreboard"> | ||
| <p>Score: <span id="score">0</span></p> | ||
| <label for="difficulty">Difficulty:</label> | ||
| <select id="difficulty"> | ||
| <option value="beginner" selected>Beginner</option> | ||
| <option value="easy">Easy</option> | ||
| <option value="medium">Medium</option> | ||
| <option value="hard">Hard</option> | ||
| </select> | ||
| <p>Time: <span id="time-left">30</span>s</p> | ||
| <button id="start-btn">Start Game</button> | ||
| <button id="reset-btn" disabled>Reset Game</button> | ||
| </div> | ||
|
|
||
| <div class="grid"> | ||
| <!-- 9 holes --> | ||
| <div class="hole" data-index="0"></div> | ||
| <div class="hole" data-index="1"></div> | ||
| <div class="hole" data-index="2"></div> | ||
| <div class="hole" data-index="3"></div> | ||
| <div class="hole" data-index="4"></div> | ||
| <div class="hole" data-index="5"></div> | ||
| <div class="hole" data-index="6"></div> | ||
| <div class="hole" data-index="7"></div> | ||
| <div class="hole" data-index="8"></div> | ||
| </div> | ||
|
|
||
| <script type="module" src="index.mjs"></script> | ||
| </body> | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| const holes = document.querySelectorAll(".hole"); | ||
| const scoreDisplay = document.getElementById("score"); | ||
| const timeLeftDisplay = document.getElementById("time-left"); | ||
| const difficultySelect = document.getElementById("difficulty"); | ||
| const startBtn = document.getElementById("start-btn"); | ||
| const resetBtn = document.getElementById("reset-btn"); | ||
|
|
||
| let score = 0; | ||
| let gameActive = false; | ||
| let moleTimer; // controls despawn/next-spawn timing | ||
| let endTimer; // controls game end | ||
| let tickTimer; // countdown display timer | ||
| let currentMole = null; | ||
|
|
||
| const LEVELS = { | ||
| beginner: { | ||
| moleVisibleMs: 3000, | ||
| respawnDelayMs: 1200, | ||
| totalTimeSec: 30, | ||
| animationSec: 1.8, | ||
| }, | ||
| easy: { | ||
| moleVisibleMs: 2200, | ||
| respawnDelayMs: 900, | ||
| totalTimeSec: 30, | ||
| animationSec: 1.3, | ||
| }, | ||
| medium: { | ||
| moleVisibleMs: 1500, | ||
| respawnDelayMs: 500, | ||
| totalTimeSec: 30, | ||
| animationSec: 1.0, | ||
| }, | ||
| hard: { | ||
| moleVisibleMs: 1000, | ||
| respawnDelayMs: 300, | ||
| totalTimeSec: 30, | ||
| animationSec: 0.7, | ||
| }, | ||
| }; | ||
|
|
||
| // Spawn a mole in a random hole | ||
| function showMole() { | ||
| if (!gameActive) return; | ||
|
|
||
| // Cleanup any existing mole | ||
| if (currentMole && currentMole.isConnected) { | ||
| currentMole.remove(); | ||
| } | ||
|
|
||
| const randomIndex = Math.floor(Math.random() * holes.length); | ||
| const hole = holes[randomIndex]; | ||
|
|
||
| const mole = document.createElement("div"); | ||
| mole.classList.add("mole"); | ||
| hole.appendChild(mole); | ||
| currentMole = mole; | ||
|
|
||
| // Tune the animation speed to the selected difficulty | ||
| const { moleVisibleMs, respawnDelayMs, animationSec } = LEVELS[difficultySelect.value] || LEVELS.easy; | ||
| mole.style.animationDuration = `${animationSec}s`; | ||
|
|
||
| // Despawn after a short time and spawn the next one with a brief delay | ||
| clearTimeout(moleTimer); | ||
| moleTimer = setTimeout(() => { | ||
| if (mole.isConnected) mole.remove(); | ||
| if (gameActive) { | ||
| setTimeout(() => showMole(), respawnDelayMs); | ||
| } | ||
| }, moleVisibleMs); | ||
| } | ||
|
|
||
| // Handle hits (click or touch/pointer) | ||
| holes.forEach((hole) => { | ||
| // Use pointerdown for better responsiveness across mouse/touch | ||
| hole.addEventListener("pointerdown", (e) => { | ||
| const target = e.target; | ||
| if (target && target.classList && target.classList.contains("mole")) { | ||
| // Register hit | ||
| score++; | ||
| scoreDisplay.textContent = score; | ||
|
|
||
| // Remove current mole and spawn the next immediately | ||
| clearTimeout(moleTimer); | ||
| target.remove(); | ||
| if (gameActive) { | ||
| const { respawnDelayMs } = LEVELS[difficultySelect.value] || LEVELS.easy; | ||
| // Add a smooth delay after whack before next mole | ||
| setTimeout(() => showMole(), respawnDelayMs); | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| // Start game logic | ||
| startBtn.addEventListener("click", () => { | ||
| if (gameActive) return; | ||
|
|
||
| // Reset state | ||
| score = 0; | ||
| scoreDisplay.textContent = score; | ||
| gameActive = true; | ||
| startBtn.textContent = "Playing..."; | ||
| startBtn.disabled = true; | ||
| resetBtn.disabled = false; | ||
| difficultySelect.disabled = true; // Lock difficulty during game | ||
|
|
||
| const { totalTimeSec } = LEVELS[difficultySelect.value] || LEVELS.beginner; | ||
| timeLeftDisplay.textContent = totalTimeSec; | ||
|
|
||
| // Kick off the loop | ||
| showMole(); | ||
|
|
||
| // Stop after specified duration | ||
| clearTimeout(endTimer); | ||
| clearInterval(tickTimer); | ||
|
|
||
| const start = Date.now(); | ||
| const totalMs = totalTimeSec * 1000; | ||
|
|
||
| // Countdown display (per second) | ||
| tickTimer = setInterval(() => { | ||
| const elapsed = Date.now() - start; | ||
| const remaining = Math.max(0, Math.ceil((totalMs - elapsed) / 1000)); | ||
| timeLeftDisplay.textContent = remaining; | ||
| }, 200); | ||
|
|
||
| endTimer = setTimeout(() => { | ||
| gameActive = false; | ||
| clearTimeout(moleTimer); | ||
| clearInterval(tickTimer); | ||
| timeLeftDisplay.textContent = 0; | ||
| if (currentMole && currentMole.isConnected) currentMole.remove(); | ||
| startBtn.textContent = "Start Game"; | ||
| startBtn.disabled = false; | ||
| difficultySelect.disabled = false; // Re-enable difficulty selection | ||
| alert(`⏱️ Time's up! Your score: ${score}`); | ||
| }, totalMs); | ||
| }); | ||
|
|
||
| // Reset game logic | ||
| resetBtn.addEventListener("click", () => { | ||
| if (!gameActive) return; | ||
|
|
||
| // Stop the game | ||
| gameActive = false; | ||
| clearTimeout(moleTimer); | ||
| clearTimeout(endTimer); | ||
| clearInterval(tickTimer); | ||
|
|
||
| // Clean up mole | ||
| if (currentMole && currentMole.isConnected) currentMole.remove(); | ||
|
|
||
| // Reset UI | ||
| score = 0; | ||
| scoreDisplay.textContent = score; | ||
| const { totalTimeSec } = LEVELS[difficultySelect.value] || LEVELS.beginner; | ||
| timeLeftDisplay.textContent = totalTimeSec; | ||
| startBtn.textContent = "Start Game"; | ||
| startBtn.disabled = false; | ||
| resetBtn.disabled = true; | ||
| difficultySelect.disabled = false; // Allow difficulty change after reset | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bonus challenge suggests adding a visible countdown timer, but the implementation already includes a countdown timer displayed in the UI (
<span id=\"time-left\">). This should be updated to reflect that a timer is already implemented and suggest alternative enhancements.