Assignment: Concentration¶
The game of Concentration (which also goes by many other names) is very old. This version was developed by Ellen Hildreth for CS 110.
- Goal
- Reference Version
- Demo
- Game Play
- Making the images disappear
- Setup
- The Supplied Code
- Building the Concentration Game Incrementally
- Stage 1: Reveal and conceal images on the game board
- Stage 2 Handler Attachment
- Stage 3: Distinguish the first and second clicks
- Stage 4: Check for Matches
- Stage 5: Keep track of the status of the game
- Stage 6: Allow the user to start a new game
- Final Checklist
- How to turn this in
- Tutors/Graders
Goal¶
You'll build a web application that will have grid of images, all of them blank (displaying a 150x150 pixel image of white pixels). However, each is clickable to reveal a picture (like turning over a card). You click on them in pairs.
- for the first click, the picture is revealed. (if the picture is already revealed, you can do nothing).
- for the second click, the picture is also revealed, but then the pictures are compared, and if they aren't the same, they both get hidden again, after a short delay (1 second) so that the user can see the pictures.
Reference Version¶
Here is my obfuscated reference solution
You are welcome to use my HTML and CSS as your starting point. You
should use the supplied.js
file, which provides some helper
functions.
You must replace the solution.js
file with your own code.
Note: if you click too fast, or if you click where you're not supposed to (such as on a previously matched picture), you can confused the app. Don't do that. You may make the same assumption in your own app: it'll be played properly, so you don't have to protect against improper usage.
Demo¶
If you want to see a demo of the game being played, you can view this:
Game Play¶
Your Game of Concentration will be played on a 4x4 game board whose cells initially contain blank images, as shown in this initial game display. Each cell will have a "hidden" image that appears when the user clicks on the blank cell. In each round of the game, the user will select two blank cells by clicking on them, one after the other. As the user clicks on each blank cell, the hidden image should appear. If the two cells contain matching images, the images should remain visible. If the two images do not match, a message should appear on the page in a visible location indicating that there is no match.
Your program should keep track of, and display, the total number of clicks and matches as the game progresses, as shown in this game in progress, which has three pairs of matching images that were found after 16 clicks. When the user succeeds at finding all 8 pairs of matching images, a message should appear on the page, congratulating the user on their success and indicating how many clicks were needed to finish the game. The object of this version of the game is to finish with the fewest number of clicks (beware! once you have a working game, it can become addictive!). The game display should include a button that the user can click to start a new game, as shown in the bottom right corner of the sample game displays.
Making the images disappear¶
There's one tricky thing that we need to help you with, and that is
getting the images to disappear after 1 second. JavaScript has a built-in
function called setTimeout
that takes two arguments: a
function and an integer. The integer says how long to wait (in
milliseconds) before executing the function. Copy/paste the following
into a JavaScript console to see how it works:
setTimeout( function () { alert("done!"); }, 1000);
So, to make the images disappear after after 1 second, just write the
appropriate function and use setTimeout
to invoke it after
1000 milliseconds.
(This is yet another callback function.)
Setup¶
- Create a
concentration
folder for this work - Copy the
game.html
file from the reference solution. The CSS is included - Copy the folder of images
- Create an empty
solution.js
file
The following commands will get you started:
cd ~/public_html/cs204-assignments/
mkdir concentration
cp ~cs204/pub/assignments/concentration/solution/game.html concentration/
cp ~cs204/pub/assignments/concentration/solution/blank.jpg concentration/
cp -r ~cs204/pub/assignments/concentration/solution/images/ concentration/
Try that page in the browser to make sure everything works.
The Supplied Code¶
We provide two JavaScript functions, named shuffleImages()
and
getImage()
, to assist with your implementation of the game. The
definitions of these functions are stored in an external JavaScript file
that can be loaded with the following statement that you should add to the
bottom of your page, after the loading of jQuery, and before your own
code. Here's the URL:
https://cs.wellesley.edu/~cs204/assignments/concentration/supplied.js
(You're welcome to look at that code; there are no secrets.
The shuffleImages()
function generates a random configuration of the 8
pairs of images for the game board. This function has no inputs or outputs
and can be invoked as follows:
shuffleImages();
Note that the shuffleImages()
function initializes the board,
so you must invoke that before the getImage()
function will
work.
The getImage()
function will be used to get the filename
for the hidden image associated with each cell on the game board. The
getImage()
function has a single input that is the
id
for a particular img
element on the game board
(e.g. "cell1", "cell2", etc.). This function returns a string that
specifies a relative URL for the hidden image for the cell associated
with the input id
. For example:
> getImage('cell4')
< "images/im1.jpg"
The value is the URL for the hidden image associated with the img
element whose id
is "cell4" (top right corner). The URL,
images/im1.jpg
, is the image for the cardinal. Try it out a little
using the JS Console, so that you are comfortable with it. You can try
it in the reference solution as well.
Note that the relative URL assumes that the images are in a subfolder
called images
, so don't use a different name for your folder of
images. The cp -r
command, above, will give you a folder with that
name. If you want to use different pictures, you're welcome to do so,
but put them in that folder and rename them.
While shuffling is important to have a real game, it makes things harder
to test. If you add ?grading
to the end of the URL, the
shuffleImages()
function will set up the board but not shuffle it, so
the 3rd row is the same as the first row and the fourth row is the same as
the second row, which makes testing a lot easier!
Building the Concentration Game Incrementally¶
When faced with a large programming task, it is always best to develop the implementation incrementally. We recommend that you work "bottom" up, building pieces that you can test to make sure they work.
Stage 1: Reveal and conceal images on the game board¶
After completing this first stage, you'll be able to click on each blank cell on the game board and the following actions will take place: (1) the hidden image will appear in the cell, and 1 second later, the image will disappear. To complete this stage, add JavaScript code to do the following:
(1) invoke the shuffleImages()
function to create a
random configuration of hidden images. This should happen when the
page loads, so it only happens once.
(2) define a function named showImage()
that displays the
hidden image for a blank cell that the user clicked. This function
should have one parameter that is the id
for the img
element associated with the cell that the user clicked. The
showImage()
function should do the following
- change the SRC attribute for the image with the given ID to be
the url returned by
getImage()
You can and should test the showImage()
function using the JS
console. For example, the following will reveal the image associated with
cell 4, which is the upper right cell:
showImage('cell4')
(3) define a function named hideImage()
that changes a cell
back to the blank image. This function should also have one parameter
that is the id
for the <img>
element
associated with the cell that the user clicked. The
hideImage()
function should change the SRC attribute for the
image with the given ID back to the blank.jpg
image.
(4) You can and should also test this function using the JS console. The following will conceal the image associated with the upper right cell. (You can try this with the reference solution.)
hideImage('cell4')
(5) define a function named processClick()
that processes the
user's clicks. This function will take one argument, the ID of the image
that was clicked on. This API allows you to easily test it from the JS
console and makes the event handler as lean as possible. (The
processClick()
function is not the event handler;
it can't be, because it takes a different kind of argument.) For this
first stage, the processClick()
function should just do the
following steps:
- invoke the
showImage()
function with the ID as an argument, in order to to reveal the hidden image, - it should use
setTimeout()
to make have the image be hidden again 1 second later. - the function in the timeout will invoke
hideImage()
with the ID as an argument, to conceal the image again.
Testing Your Code¶
You can test the processClick
function in the JS console (as you can all
of these) just by calling them. E.g. to simulate clicking on the upper
left cell, do:
processClick('cell4');
You can try pairs of clicks in the reference solution as well.
Stage 2 Handler Attachment¶
(Revised for Fall 2021)
Finally, you must invoke processClick()
when one of the
img
elements is clicked. This can be done by attaching a function
(that invokes processClick
with the
appropriate argument) to every game image as a click handler.
You should do this the same way the event handlers were added to each thumbnail in Ottergram, using the jQuery each method. This would be a good time to go back and review Ottergram, looking carefully at how the event handlers were attached to the images. The basic outline is:
- gather a jQuery set of the clickable images
- use the
.each
method on that array to - add an event handler to each one
Note that you will have to make sure each event handler knows the ID
of the image that it's attached to, since that's the argument to
processClick
. There are several ways to do this.
- You can read the
id
attribute off the IMG element, using the one-argument version of the jQuery attr method, or - You can construct the
id
from the index that is supplied by the.each
method as the first argument of the callback.
Ideally, you should make sure this code works before proceeding to the next stage, but it's possible that you will get stuck, because attaching the event handlers is a little tricky. So, if you do get stuck, you can instead proceed to the next stage, improving the click handling and testing your click handling in the JS console like this:
processClick('cell4');
processClick('cell5');
(This simulates clicking on those two cells).
Still, it'll be nice when you can test your code by clicking, so it's worth getting the handlers to work.
Finally, a common mistake in attaching handlers is addressed here in functions as arguments. If you have trouble, I recommend refreshing your memory on that.
Another common mistake is to have a click handler to add the click handlers or, sometimes, to have the click handler add itself. Adding click handlers is not a recursive process. Just as Ottergram and in all our assignments so far, your code to add event handlers will run when the page loads.
When you're successful with this state, you've connected clicks to behavior; from now on, we'll be improving the behavior.
Stage 3: Distinguish the first and second clicks¶
So far, the processClick
function treats every click the same. We
need to change it to treat first clicks differently from second
clicks.
After completing this second stage, you'll be able to click on pairs of blank cells on the game board and have both hidden images revealed. After a few seconds, both images will be hidden again.
Create a global variable to store whether the user is clicking for the first time or the second time. I suggest it have values of 1 or 2. Initialize it to 1.
Create a second global variable to store the ID of the first cell that was clicked on.
Modify the processClick
function so that if it's a first click, the
image is not hidden, but the cell's ID is stored in the global
variable. If it's a second click, both cells are hidden again after
a suitable delay.
The processClick
function also needs to manage the variable about
whether it's a first click or a second click. That's probably the
trickiest part of this stage. Here's the idea:
- If it's a first click, show the image and set the click number to 2.
- If it's a second click, hide the images and set the click number back to 1.
- You can tell which it is by looking at the click number global variable.
Test to make sure this code works before going on. You should be able to:
- click on a first image, having it stay up indefinitely until
- you click on a second image, then both disappear after a short delay.
- And repeat.
Stage 4: Check for Matches¶
Again, we'll modify the processClick
function. Make sure you don't
break the previous behavior. It might be smart to save a copy of your
solution to stage 3, in case you get confused during stage 4 and want
to start over.
In the code to process a second click, modify the code to compare the
URLs that are returned by getImage
for the first click (you saved
its ID in a global variable) and for the second click (the argument).
If the URLs are the same, then the images match.
- If so, put a message on the screen to inform the user, and don't hide the two images.
- If they don't match, hide the two images as usual.
Test your code for this stage to be sure it works correctly before proceeding to the next stage!
Stage 5: Keep track of the status of the game¶
At this point, the game pretty much works, but we need to count stuff to keep track of how the player is proceeding. We'll count tries and matches.
Create two additional global variables to store the following information:
- the number of tries the user has made so far, initialized to 0
- the number of matches the user has found so far, initialized to 0
Modify the processClick()
function to record and display
the status of the game. In handling second clicks, increment the
number of tries and, if there's a match, increment the number of
matches.
Update the text on the page to show these numbers.
If the number of matches is 8, the game is over; that's a different message. Congratulate the user.
Test your code for this stage to be sure it works correctly before proceeding to the next stage!
Stage 6: Allow the user to start a new game¶
After this final stage is complete, the user will be able to start a new game without having to reload the page, by clicking the button. This final stage can be completed as follows:
- Define a function named
startNewGame()
that starts a new game for the user. This function should have no parameters and should perform the following actions: - invoke
shuffleImages()
to generate a new random arrangement of images for the new game - reset the global variables for the start of a new game
- reset the text on the webpage to indicate 0 clicks and 0 matches
- change all the cells of the board to the blank image (you
can use your
hideImage()
function here, multiple times, or think of an even better way using jQuery). - Attach the
startNewGame()
function to be the click handler for the button on your page.
If you invoke this function when the page loads, you'll be all set for
the first game, and you won't have to invoke shuffleImages()
by
hand.
Test this. When it works, you're done!
Note You can assume that the user plays the game correctly! For example, you can assume that the user only clicks on blank cells, and that they only click on two cells for each round of the game.
Final Checklist¶
- Your name in all files that you wrote
- The
'use strict';
directive in all.js
files - All functions and top-level variables properly documented
- All code neatly laid out, and lines not too long (80 characters is a good limit)
- Comments as needed
- Folder and files properly named
- Submit to Gradescope
How to turn this in¶
We'll grade the work in your cs204-assignments/concentration
folder. Just submit the Gradescope item.
Tutors/Graders¶
You can use the password to view the Concentration solution.
Time and Work
Finally, when you have completed the assignment, make sure you fill out the Time and Work report. That report is required.