To try out the final product, click here!
Welcome back! In Part 1, we built the skeleton of our app and created the basic logic of the game loop. Our first challenge will be to get a new image for each turn. Luckily, there’s an API for that! There are a few actually, but I’ve decided to go with Lorem Picsum.
Before we jump in, there are a couple things to consider. For one, since we’re going to need to make an HTTP request, that means we’re going to have to consider that we’ll have an asynchronous function built in to our game loop. We will have to be strategic about when the image fetch will happen, what will trigger it, and what to do in the mean time while we wait for the response from the server!
Let’s go back to our beginNewTurn() function, which is called to kick off the first turn when the user click the “Start Game” button. We’re going to immediately fetch the image at the beginning of the turn, because we can’t really do anything else unless we have it!
To get a random, square image from Lorem Picsum, you can submit an HTTP request to https://picsum.photos/700 where the 700 represents the size of the image in pixels. Once we receive the image, we can “officially” start the turn. Since we’re waiting on the resolution of this Promise, check out what functions we will have in the response of this fetch:

Notice that we don’t add the event listeners until everything is in place, because we don’t want the user to be able to choose if there is no image!
Lets take a look at what happens when the user makes a choice. The onclick handler calls one function, handleClick. You must pass a single, named function to .addEventListener in order to remove the same function using .removeEventListener, so handleClick is that function. Here, two things happen:

First, we remove event handlers to prevent duplicate clicks. Then we call submitChoice, passing in the element’s ID representing the button that the user clicked. This function grabs the current Turn, and compares the User’s choice with the computer’s choice. Remember, the three possible cases are 1) User is correct, 2) User passed, or 3) User is incorrect. For each of these cases, I have separated their concerns into their own functions. First take a look at the logic, then we’ll go through the respective functions below.

Pretty straightforward, right? Before going into each case, let’s revisit what needs to happen for each function.
User chose correctly:
- Show image
- Play “correct” sound
- Increment the turn counter
User chose incorrectly:
- Display the correct choice to user by highlighting the button
- Play “incorrect” sound
- Increment the turn counter
User passed:
- Display the correct choice to user by highlighting the button
For each of these bullets, I have again separated their concerns into their own functions, since they are reused in different cases. They are descriptive enough that you should be able to read through them with no problem, as the lower-level code that interacts with the HTML has been factored out. The unique function here is continueGame. See if you can guess what that does 🙂

No matter what happens, we want to continue the game, but if the user guesses correctly, we need to give the user a little more time to view the image. So, we pass in the number of milliseconds before the game continues. If the game has reached the end, we don’t want to continue, but we still want the user to view the image. For this reason, we add the gameIsOver() check in this function:

You can see that if the game is not over, we go right back to the beginning with our beginNewTurn() function! This marks the end of a completed turn. Since we are using setTimeout to call beginNewTurn, the delay that we specify coincides perfectly with the HTTP request. Therefore, there will be an additional small delay while we wait for the response from the server, but it’s mostly unnoticeable!
If the game is over, we call gameOver(), which contains all the end-of-game actions:

We have already seen the first three functions; displayScore simply shows the score modal, which does some math and shows the user how they did:

We pass into the reduce function an initial value of an object: {correct: 0, incorrect: 0}. We iterate through each turn, and use these keys to count correct versus incorrect choices. Finally, we calculate the percent correct and show it to the user!
For clarity’s sake, I split my JS files into game.js, which holds the game logic, and ui.js, which holds the code for interacting with HTML. To view the repo and check out the details of those functions, click here!
That’s it! We did it. I urge you to train your ESP and let me know how you do! Thanks for reading, and please comment your thoughts or feedback!