Home/Blog/Learn to Code/Polish your JavaScript: create a whack-a-mole game
Home/Blog/Learn to Code/Polish your JavaScript: create a whack-a-mole game

Polish your JavaScript: create a whack-a-mole game

Saqib Ilyas
Jan 11, 2024
18 min read

When learning to code, practicing by creating applications is extremely important. Early-stage learners often get discouraged by the complexity of it all. Creating games is a great way to practice coding for various reasons. Firstly, we’re familiar with the game rules, which removes some of the complexity from the process. Secondly, games are fun, making the coding process more engaging.

This blog assumes a basic knowledge of HTML, CSS, and JavaScript. It is targeted toward individuals who are learning to create web applications.

In this blog, we’ll develop a game of whack-a-mole in JavaScript. Here are the game requirements:

  • The game will have a 3 x 3 square grid—a total of 9 cells.
  • A mole will appear in a randomly picked cell at a random time.
  • If the user clicks on the mole, the score will increment, and the mole will disappear.
  • If the user doesn’t click the mole, it will disappear after a random interval.
  • After another random amount of time, another mole will appear in another randomly picked cell.
  • The game will be playable for one minute and then will stop.

Here’s a preview of what we aim to create.

Note: Press the “Start” button to play the game. Once the game is over, you may press the “Play again” button to play again.

Game preview

The starter HTML#

From the looks of the above preview, we need the following:

  • A heading element to display the current score and a time remaining indicator.

  • The cards on which a mole appears.

  • A container to hold the cards.

  • A “game over” overlay notification.

Here’s an HTML that fulfils all but the last requirement. We’ll get to the “Game over” notification later. Note that you wouldn’t see anything besides “Score: 0” in the output at this point because all of the div elements have zero width and height by default.

Note: At any point in this blog, you may switch the tabs in the coding playground to view the output or the corresponding code. Feel free to fiddle with the code and see the effect on the output.

HTML for the game

Within the body element, we set up an h2 element and a span element (line 9) to display the score. We set up a div element with an id of grid (line 10). This serves as the container for the cells. Inside it, we have the div elements corresponding to the 9 cells (lines 11–19). For consistent styling of all the cells, we set a class of square for each of these div elements. We also assign each cell a unique id so that we may conveniently access each cell individually in code.

Basic CSS styling#

Although we defined all the div elements in the HTML, we couldn’t see them. Let’s rectify that, as shown below:

CSS Styling

We start with a CSS reset to avoid browser-specific style inconsistencies on lines 1–5. We target all HTML elements with the * selector as well as the ::after and ::before pseudo-selectors (lines 1–5). We set the margins and padding to 0 (lines 2–3). We also set the box-sizing property to border-box, so that the border thickness is included in the box sizes. It is important to note that we refer to the CSS file in the HTML file on line 6.

Why are the width and height properties for the div element with the id of grid set to 322 pixels (line 8 and line 9)? An individual cell looks reasonable with a height and width of 100 pixels. With three cells in a row, we need a width of at least 300 pixels. We don’t want the cells to be cramped into each other, so we’ll need some spacing between them horizontally. That’s why we have left an allowance of 22 pixels in the grid width. The same argument goes for the height.

In the preview, we see that the squares are separated from the edges of the green-shaded playing area. To achieve that, we give the element with an id of grid a padding of 4 pixels on all sides (line 10). We also give it a greenish background color (line 11). We give it rounded edges with the border-radius property (line 12) to match the look in the preview. We apply margin: 50px auto (line 13) so that this element is spaced from the top by 50 pixels and is centered horizontally on the screen.

At this point, you’ll see “Score: 0” at the top-left of the page and a greenish box near the center of the page. The h2 element for the score is too close to the edge of the page, so we need to add some space above and to the left of it. Here’s how we can do it:

Adding margins around the heading

Laying out the cells#

Now, let’s move on to styling the 9 cells. Let’s give them a width and a height of 100 pixels each. Here’s the code with updated CSS:

Adding the cells

Since these cells all have a class of square, we use the class selector, .square (line 20), and set the width and height (lines 21–22). We gave the elements a border on line 23. To have rounded edges, we used the border-radius property on line 24. To introduce some spacing between two rows of cells, we use the margin-bottom property on line 25.

Achieving the 2D layout#

The output in the above coding playground looks nothing like what we want. The div elements with a class of square are just hanging vertically, one in each row, near the left edge of their parent div. This is because the divs are block elements, each starting on a new line by default.

There are several ways to fix that. One is to use CSS Flexbox. Here’s the modified style:

2D layout for cells

We set the parent element’s display property to flex (line 18). To make the cells wrap around to the next row, we set the flex-wrap property to wrap (line 19). Since the parent is 322 pixels wide, and each cell is 100 pixels wide, we get a wrap-around after three inner div elements. We set the justify-content property to space-between (line 20) so that the inner div elements are spaced evenly in a row. Now, the cells are laid out nicely in a 3x3 arrangement.

Can you modify the code in the above playground to turn the board into a 4 x 4 grid?

Cover
The Complete Beginner’s Guide to CSS

Cascading Style Sheets (CSS) is a document type and the associated scripting language that describes the presentation of HTML documents. Using the CSS language, you can control the look and feel of web pages by defining layout, text, and image properties to be applied in a wide array of contexts. This course is a comprehensive introduction to both writing and implementing CSS. Using the document hierarchy model, you’ll learn how to tailor web styles to specific areas. With your hierarchy defined, you’ll dive deep into CSS fundamentals, adding colors, text styles, and backgrounds to your web page. After you’ve defined these elements, you’ll use CSS to define layouts around pictures and text flow and completely control your web page’s look. You’ll wrap up by exploring the essential animation functions of CSS. By the end of this course, you’ll have functional mastery of Cascading Style Sheets and the CSS scripting language. You’ll be prepared to build professional web pages with your code.

4hrs 55mins
Beginner
104 Playgrounds
8 Quizzes

Displaying the mole#

To display the mole in a cell, we can put an img element inside the div element corresponding to that cell. For instance, the following playground shows the mole image in the first cell:

Displaying the mole in the first cell

We add an img element to the div element (line 12 in the HTML). We add a CSS rule for the img element (lines 34–36 in CSS) to set the image width to 90 percent of the parent’s width. The mole image is larger in size than the cells, so skipping this step would cause the mole image to overflow the cell. To vertically and horizontally center the image within the cell, we use Flexbox (line 29) and set the justify-content and align-item properties to the image’s container element to center (lines 30–31).

Displaying the mole in a random cell#

At the moment, we have displayed the mole image in a fixed cell. Here’s how we can display it in a randomly picked cell:

  • Create an img element for the mole.

  • Pick a cell number at random.

  • Select the DOM element corresponding to the randomly picked cell.

  • Add the img element as a child to the above div.

Let’s get to work:

Adding code to display the mole

We create a new img element using the createElement() method on line 2. We store the image URL in a variable on line 3. We set the src attribute of the img element using the setAttribute() method on line 4. We pick an integer from 1–9 at random on line 8 using the Math.random() and Math.floor() methods. The call to Math.random() method returns a positive fractional value less than 1. We multiply it with 9 and take the ceiling results in a positive integer value less than or equal to 9. This gives us a random integer that we can use to pick a cell.

We query for the DOM element with an id attribute equal to this random integer on line 8, and select that object in the variable named cell. Finally, we append the img element as a child to cell (line 9).

Switch to the “Output” tab in the above playground, and you’ll see the mole in a randomly picked cell. Click the “Run” button in the playground, and you’ll see it appear in another randomly picked cell, as the above code would run again.

Removing the mole after a random interval#

The mole needs to disappear after some time. Let’s work on that.

We have the variable named cell that holds the div element in which we displayed the mole, so removing the mole from view is a matter of using cell.removeChild(cell.firstChild). How do we make that happen after a random amount of time? For that, we’ll need to use the setTimeout() method with a randomly drawn duration as an argument. To draw a random interval, we may use the Math.random() method, again. Let’s say we want the mole to stay on the screen for at least 800 milliseconds and as long as 3 seconds (or 3000 milliseconds). The statement, Math.floor(Math.random() * n), generates a random positive integer less than n. In other words, it generates a random integer between 00 and n1n - 1, both inclusive. Since our desired minimum random value is 800800, we can do 800 + Math.floor(Math.random() * n), and that gives us a random integer between 800800 and 800+n1800 + n - 1. If 800+n1800 + n - 1 is to be 30003000, then nn equals 22012201.

Let’s put this in action:

Having the mole disappear after a random period of time

We use this expression on line 11 to draw a random interval duration. We use the setTimeout() method to fire the removeMole() function when this timer expires. In removeMole(), we use the cell variable to remove the mole image from the DOM. Once you’ve read the code, go to the “Output” tab in the above playground and click the “Run” button. A mole will appear in a random cell and then disappear after a random interval. You may click the “Run” repeatedly to observe this in action.

Making the mole reappear periodically#

The mole must be in one cell for some time, disappear, and then reappear in a random cell after a random period of time. Let’s implement that.

To do this, we need to call another function, say addMole(), from the removeMole() function after a random interval. This function will display the mole in another random cell and call the removeMole() function after another random interval. So, we’ll have created a cycle like the following.

Two functions will call each other periodically
Two functions will call each other periodically

Here’s the implementation:

Having the mole disappear after a random period of time

First, we modify the removeMole() function. We generate a random integer to serve as the delay in milliseconds, after which the mole reappears (line 16). We call the setTimeout() funciton to invoke the addMole() function after that delay (line 17). We define the addMole() function on lines 20–26. Lines 21–23 are similar to lines 6–9. We show the mole in a random cell. On line 24, we generate a random integer to serve as the duration for which the mole stays visible. On line 25, we use the setTimeout() function to have the mole disappear through the call to the removeMole() function. Now, the mole will appear, disappear, and reappear repeatedly.

You might have noticed a bit of redundancy in the code above. Lines 6–9 and lines 21–23 serve the same purpose. Let’s fix that now:

Refactoring the code

Click#

The player will try to click the mole to score points. So we'll now manage mouse clicks. We can either add the click event listener on the img element or the div element that contains it. The difference is that the img element occupies 90% of the width of the div element.

If we add the listener to the div element, we favor the player because they have more area available to score on.

We can add that event listener to the addMole() function while adding the img element to the div. The callback can call the removeMole() function to get rid of the mole. Here’s an outline of what we need to do:

  • When we display a mole in a cell, we need to add a click event listener to that cell.

  • To indicate a success, in the event listener, remove the mole from that cell.

Handling clicks

On line 22, we add a mouse-click event listener to the div element and set the hitit() function as the callback. From there, we call the removeMole() function. Now, when we whack the mole, it will disappear.

However, there’s an issue with the above code. We call the removeMole() function if the player clicks on the mole. But, there’s a call to the removeMole() function scheduled on a timeout (line 24). One of these will happen first—it’ll remove the mole. The second call to the removeMole() function won’t find a mole to remove, and line 13 will throw an exception.

To fix this error, we can store the timer ID in a variable and use the clearTimeout() function to cancel the scheduled call to the removeMole() function before calling it on line 28. Here’s the updated code:

Avoiding redundant calls to the removeMole() function

We define a variable to hold the timeout ID on line 3. On lines 16 and 25, we store the timer in the variable. Before calling the removeMole() function from the hitit() function, we call the clearTimeout() function to cancel that timer.

Score#

We haven’t kept track of the score yet. So, let’s do that now.

Here’s an outline of what we need to do:

  • Declare a variable with an initial value of zero since that’s the initial score.

  • Extend our click event handler to increment the score once the user successfully hits a mole.

Keeping track of score

We define a variable to hold the score on line 4. We also define a variable to hold the object corresponding to the span element to display the score (line 18). If the player successfully clicks the mole before it disappears, we update the score variable in the hitit() function on line 33. We display the updated score on line 34.

However, you might notice one problem with the game. We still get a score if we click a cell after the mole has disappeared. Why is that? It is because we registered hitit() as the event listener to that div element, and it is still registered as a callback. We need to remove the event listener once the mole disappears one way or the other.

Removing the event listener after the mole disappears

All it takes is a cell.removeEventListener() on line 18 when removing the mole. With that, the player doesn’t get erroneous scores on clicking the div element after the mole has disappeared.

End game#

We need to give the user a 1-minute interval to play a game. For that, we’ll maintain a timer. Once that timer expires, we clear any scheduled callback, click handler, and display a message that the game is over.

Here’s an outline of what we need to do:

  • Define a variable to hold the time elapsed so far.

  • Update the above variable after a fixed interval.

  • When the above variable reaches its limit (minimum or maximum, depending on our approach), display an HTML element to indicate “Game over.”

Implementing “Game over”

To set a one-minute timer for the “game over,” we define a variable to hold the duration in milliseconds (line 15). We register a function, gameOver(), to be called after this duration (line 16). In the gameOver() function, we clear any timers that are scheduled to display or remove the mole (line 42). We remove the click event listener that might be registered on one of the div elements (line 43).

To display “Game over” on the screen, we create a div element (line 45). We set it’s text to “Game over” (line 46). We add a class of gameover to this div element. We define this style in the CSS file on lines 38–47. To take the div element out of the normal flow of a page and display it at a fixed position, we set its position property to fixed (line 39). But where do we want this fixed position to be? That must be specified as done on lines 40–41. We set the top and left properties to 50%, i.e., center of the screen. But if the element’s top-left corner is at the center of the page, then it won’t appear centered on the page. To fix that, we translate the div element 50% upwards and leftwards (line 42). We set the background color to black with an opacity of 80% (line 43). We set the text color to a contrasting white (line 44). We set the font size to 2rem (line 45). Finally, we apply a padding of 20 pixels on each side so that the text, “Game over,” is a bit displaced from each side.

Remaining time#

Our game doesn’t give the player any feedback about the time remaining. Let’s fix that.

Here’s an outline of what we need to do:

  • Add necessary elements to the HTML to indicate the remaining time.

  • Style the above elements as necessary.

  • Define a variable to keep track of the time remaining.

  • Update the above variable after a fixed interval.

  • Update the HTML elements based on the value of the above variable.

Implementing “Game over”

First, we edit the HTML to introduce a new section on lines 9–14. This serves to encompass both the score and the progress bar neatly. We move the score elements (the div and the span) inside this section and add another div element (lines 11–13). There’s another div element nested inside it on line 12.

Why create this specific structure? We’ll give the outer div element a red background color as well as a specific width, and the inner div a green background color, as well as a width of 100%. So, initially, this progress bar will appear all green. Gradually, we’ll reduce the width of the inner div element, revealing red background color from below. Eventually, the progress bar will become all red when the game is over.

On to CSS. First, we eliminate the styling we applied to the h2 element. Then, we give the top class a height of 50 pixels and a width of a hundred percent (lines 46–47). We set its display property to flex (line 48) so that we can use Flexbox to position its contents. We select even spacing for its content placement (line 49). We also go for vertically centered content (line 50). We then style the progress class with a red background color (line 54). We give it a width of 200 pixels (line 55) and a height of 10 pixels (line 56). To get rounded corners for a cooler look, we give it a border-radius of 10 pixels (line 57). For the remaining class, we use a green background color (line 61), width and height of 100% (lines 62–63), and a border-radius of 10 pixels.

With just the above changes, the progress bar would turn up all green and stay that way. On to the code now.

We don’t need the 60-second timer anymore. We’ll update the progress variable every second. Once it reaches 60 seconds, we’ll call the gameOver() function. So, we get rid of the call to the setInterval() function with the duration of 60000 milliseconds. We define a variable named progress on line 16, initialized to 6000. We set it equal to the value of duration so that the progress bar appears 100% green initially. We want the progress bar to be updated every second, so we call the setInterval() function to invoke the update() function every second (line 17). We acquire an object corresponding to the progress div on line 19 to adjust it with code.

We define the update() function on lines 58–65. Since the progress variable started at 6000 and needs to go down to 0 after a decrement every second, the decrement amount must be 1000. That’s what we’re doing on line 59. We update the percentage width of the progress div on line 60. Finally, if the progress variable reaches 0, we clear the event that triggers every second to update the progress bar (line 62) and call the gameOver() function.

Another improvement in the above code is giving the player feedback that they successfully hit the mole. We do this in the click event handler on lines 41–42. We change the background color of a lighter shade (line 41) and then reset it to its previous value (line 42).

Cover
Building Front-End Web Applications with Plain JavaScript

This course shows you how to build front-end web applications with plain JavaScript without using any (third-party) framework or library. It follows a "learning by doing" approach, which means that you don't have to read lots of text about the intricacies of JavaScript. Instead, you’ll focus on the essential parts of JavaScript and read only the minimum needed to start coding your first app. Learning from the examples provided in the course can quickly improve your understanding of basic concepts and techniques. By working your way through the code of these example apps and using it as a starting point for your projects, you’ll learn best practices and experience the joy of building something that really works and that you fully understand.

22hrs 10mins
Beginner
110 Playgrounds
14 Quizzes

That’s all folks#

That concludes our development of the whack-a-mole game in JavaScript. We hope you enjoyed the experience. Keep learning by extending the game’s functionality and improving its styling. Experiment with making it responsive and mobile-friendly. Also try adding the “Play again” functionality that is available in the preview at the beginning of this blog.


  

Free Resources