A JavaScript project walk-through for novice programmers.
In this project you will make a game called Thumb Wrestling. Check it out.
This project assumes you have completed Becoming dangerous in JS + HTML + CSS.
- PART 1. JAVASCRIPT
- Lecture 1. Initial setup
- Lecture 2. Detecting key presses
- Step 1. Detecting a keypress
- Step 2. Figuring out which key was pressed
- Step 3. Disable browser scrolling
- Lecture 3. Creating the board
- Step 1. CSS
- Step 2.
createThumbWrestling(...)
- Step 3. Adding a single cell
- Step 4. Adding a row of cells
- Step 5. Build the full grid
- Lecture 4. Arrows
- Step 1. Game state
- Step 2. Drawing arrows
- Step 3. A dash of refactoring
- Step 4. Arrows point the correct direction
- PART 2. CHALLENGES
You will need to learn some new JavaScript to make this game. This part of the project teaches you this new JS.
In the same directory, create four files index.html
, style.css
, jquery.js
, thumb-wrestling.js
, as so:
<!DOCTYPE html>
<html>
<head>
<title>Thumb Wrestling</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<script src="thumb-wrestling.js"></script>
</head>
<body>
</body>
</html>
Just create and save a blank file for now.
Download jQuery from here,
rename the file to jquery.js, and put it in the same folder with index.html
, style.css
, etc.
Just create and save a blank file for now.
View index.html
It's just a blank page.
In this game, players move their arrows by pressing keys on the keyboard. Therefore, you need to write JavaScript that detects whenever a key is pressed.
In thumb-wrestling.js
, write a function called keydown(event)
:
function keydown(event) {
alert("A key was pressed");
}
The function doesn't need to be named keydown
; you can name it whatever you want.
Then you "register" the keydown(...)
function with document.onkeydown
, as so:
document.onkeydown = keydown;
Now, whenever someone presses a key, the keydown (...)
function will be called,
and an alert will pop up.
View index.html
Press some keys.
When the browser calls the keydown
function, it passes it an event
object.
event.keyCode
indicates which key was pressed. For example:
- If
event.keyCode == 38
, then that means the up key was pressed - If
event.keyCode == 87
, then that means the W key was pressed
We are only interested in detecting (1) keypresses for the arrow keys (for the red player), and (2) keypresses for W, A, S, D (for the green player).
Here's a table that shows the keyCode values associated with every key we're interesetd in.
keyCode |
Key |
---|---|
87 | W |
83 | S |
65 | A |
68 | D |
38 | up |
40 | down |
37 | left |
39 | right |
Using the above table, we create a new function getPlayerMovment(keyCode)
:
function getPlayerMovment(keyCode) {
var keyCodeMap = {
87: ["green", "up"],
83: ["green", "down"],
65: ["green", "left"],
68: ["green", "right"],
38: ["red", "up"],
40: ["red", "down"],
37: ["red", "left"],
39: ["red", "right"]
};
return keyCodeMap[keyCode];
}
Here's what the function does:
- If the keyCode is for a key we're uninterested in, it returns
undefined
- Otherwise (if the keyCode is associated with a player), return the color of the keyCode's player and the direction associated with that keyCode
Then, modify the keydown(...)
function:
function keydown(event) {
var playerMovement = getPlayerMovment(event.keyCode);
// If the user pressed a key we're uninterested in
if (playerMovement == undefined) {
return;
}
var [color, direction] = playerMovement;
alert(color + " " + direction);
}
The code is straightforward: if the user presses one of the player's keys it pops up an alert saying the player color and the direction.
View index.html
Press some keys, including the arrow keys and w, a, s, d.
By default, the browser scrolls when an arrow key is pressed.
We want to disable this default behavior, since we don't want the web page to scroll when the red player presses an arrow key.
To accomplish this feat we modify the keydown(...)
function, by adding the
following code:
// disable browser scrolling on arrow keys
if (color == "red") {
event.preventDefault();
}
The new keydown(...)
function looks like this:
function keydown(event) {
var playerMovement = getPlayerMovment(event.keyCode);
// If the user pressed a key we're uninterested in
if (playerMovement == undefined) {
return;
}
var [color, direction] = playerMovement;
// disable browser scrolling on arrow keys
if (color == "red") {
event.preventDefault();
}
alert(color + " " + direction);
}
View index.html
Press some keys, including the arrow keys and w, a, s, d.
Recall, in the Lights Out game there was a 4 × 4 grid of lights. The HTML to produce this grid was tedious and repetitive:
<!DOCTYPE html>
<html>
<head>
<title>Lights Out</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="lights-out.js"></script>
<script src="jquery.js"></script>
</head>
<body>
<div class="row">
<div class="light" id="light-0-0" onclick="lightClick(0, 0)"></div>
<div class="light" id="light-0-1" onclick="lightClick(0, 1)"></div>
<div class="light" id="light-0-2" onclick="lightClick(0, 2)"></div>
<div class="light" id="light-0-3" onclick="lightClick(0, 3)"></div>
</div>
<div class="row">
<div class="light" id="light-1-0" onclick="lightClick(1, 0)"></div>
<div class="light" id="light-1-1" onclick="lightClick(1, 1)"></div>
<div class="light" id="light-1-2" onclick="lightClick(1, 2)"></div>
<div class="light" id="light-1-3" onclick="lightClick(1, 3)"></div>
</div>
<div class="row">
<div class="light" id="light-2-0" onclick="lightClick(2, 0)"></div>
<div class="light" id="light-2-1" onclick="lightClick(2, 1)"></div>
<div class="light" id="light-2-2" onclick="lightClick(2, 2)"></div>
<div class="light" id="light-2-3" onclick="lightClick(2, 3)"></div>
</div>
<div class="row">
<div class="light" id="light-3-0" onclick="lightClick(3, 0)"></div>
<div class="light" id="light-3-1" onclick="lightClick(3, 1)"></div>
<div class="light" id="light-3-2" onclick="lightClick(3, 2)"></div>
<div class="light" id="light-3-3" onclick="lightClick(3, 3)"></div>
</div>
</body>
</html>
In this game, there is a 10 × 10 grid of cells. It would be even more tedious and repetitive to hand write the HTML for 100 cells.
To avoid such tedium, and to be more elegant, we will write a short bit of code that will produce the HTML for the grid of cells.
But first, let's write the CSS for the cell elements:
.row {
clear: left;
}
.cell {
height: 50px;
width: 50px;
margin-left: 1px;
margin-top: 1px;
float: left;
background-color: lightgray;
line-height: 50px;
}
Notice, it is very similar to the Lights Out CSS.
Add <div id="board"></div>
inside the <body>
tag.
Then add the following <script>
element after the <body>
tag.
<script type="text/javascript">
createThumbWrestling("#board");
</script>
Finally, add the following function to thumb-wrestling.js
:
function createThumbWrestling(boardId) {
$(boardId).text("Hello.");
}
Your index.html
file should look like this:
<!DOCTYPE html>
<html>
<head>
<title>Thumb Wrestling</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<script src="thumb-wrestling.js"></script>
</head>
<body>
<div id="board"></div>
</body>
<script type="text/javascript">
createThumbWrestling("#board");
</script>
</html>
Here's what's going on.
The <div id="board"></div>
will contain the grid of cells (inside the <div>
).
When the browser calls createThumbWrestling("#board")
it executes $(boardId).text("Hello.")
.
It first executes $("#board")
which is a jQuery function that selects the <div>
with id
equal to board
. Then the .text("Hello")
function call appends Hello
inside the
<div>
tag.
Therefore, the resultant HTML looks like this:
<!DOCTYPE html>
<html>
<head>
<title>Thumb Wrestling</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<script src="thumb-wrestling.js"></script>
</head>
<body>
<div id="board">Hello.</div>
</body>
<script type="text/javascript">
createThumbWrestling("#board");
</script>
</html>
Now, we're going to modify the createThumbWrestling(...)
function to add a single cell to the board (instead of saying Hello).
Modify createThumbWrestling(...)
as so:
function createThumbWrestling(boardId) {
var cellTag = "<div class='cell'></div>"
$(boardId).append(cellTag);
}
Now, the dynamically generated HTML will look like this:
<!DOCTYPE html>
<html>
<head>
<title>Thumb Wrestling</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<script src="thumb-wrestling.js"></script>
</head>
<body>
<div id="board">
<div class='cell'></div>
</div>
</body>
<script type="tesxt/javascript">
createThumbWrestling("#board");
</script>
</html>
View index.html
In this step, we create a single row of cells.
But first, we need to define numRows
and numCols
at the top of thumb-wrestling.js
:
var numRows = 10;
var numCols = 10;
Now we can modify createThumbWrestling(...)
as so:
function createThumbWrestling(boardId) {
var row = 0;
var rowId = "row-" + row;
var rowTag = "<div id='" + rowId + "' class='row'></div>"
$(boardId).append(rowTag);
for (var col = 0; col < numCols; col++) {
var cellId = "cell-" + row + "-" + col;
var cellTag = "<div id='" + cellId + "' class='cell'></div>";
$("#" + rowId).append(cellTag);
}
}
First we add a row <div>
with $(boardId).append(rowTag);
Next we have for-loop that adds each of the cells with $("#" + rowId).append(cellTag);
.
Observe that we select the board <div>
by using $(boardId)
,
yet we select the row <div>
by using $("#" + rowId)
(with a pound sign prefixing rowId
).
You may wonder why we need to prefix rowId
with a pound sign, but not boardId
.
Here's what's up: every time we select an element using jQuery (i.e. $(elementId)
), we must
prefix the elementId
with a pound sign. Recall from index.html
that we invoke
createThumbWrestling(boardId)
with boardId
equal to "#board"
-- therefore we select
the board element simply with $(boardId)
since the pound sign is already included in boardId
.
On the other hand, the rowId
variable is not prefixed with a pound sign -- therefore
we must select the row element with $("#" + rowId)
.
Now, the dynamically generated HTML will look like this:
<!DOCTYPE html>
<html>
<head>
<title>Thumb Wrestling</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="jquery.js"></script>
<script src="thumb-wrestling.js"></script>
</head>
<body>
<div id="board">
<div id="row-0" class="row">
<div id="cell-0-0" class="cell"></div>
<div id="cell-0-1" class="cell"></div>
<div id="cell-0-2" class="cell"></div>
<div id="cell-0-3" class="cell"></div>
<div id="cell-0-4" class="cell"></div>
<div id="cell-0-5" class="cell"></div>
<div id="cell-0-6" class="cell"></div>
<div id="cell-0-7" class="cell"></div>
<div id="cell-0-8" class="cell"></div>
<div id="cell-0-9" class="cell"></div>
</div>
</div>
</body>
<script type="text/javascript">
createThumbWrestling("#board");
</script>
</html>
View index.html
Simply add a another for loop to iterate over the rows:
function createThumbWrestling(boardId) {
for (var row = 0; row < numRows; row++) {
var rowId = "row-" + row;
var rowTag = "<div id='" + rowId + "' class='row'></div>"
$(boardId).append(rowTag);
for (var col = 0; col < numCols; col++) {
var cellId = "cell-" + row + "-" + col;
var cellTag = "<div id='" + cellId + "' class='cell'></div>";
$("#" + rowId).append(cellTag);
}
}
}
View index.html
In this lecture, we add arrows to the game board.
Recall in the Lights Out game, we stored the game state in a matrix.
A matrix isn't suitable for storing the game state in Thumb Wrestling, since we only need to keep track of two items: a red arrow, and a green arrow.
We'll use the following data structure to keep track of the red and green arrows:
var gameState = {
"red": {
row: numRows / 2 - 1,
col: numCols / 2 - 1,
dir: "right"
},
"green": {
row: numRows / 2 - 1 ,
col: numCols / 2,
dir: "left"
}
};
Type in that code at the top of thumb-wrestling.js
, just below the definitions for numRows
and numCols
.
Now, we can access the gameState
like so:
gameState["red"].row
gameState["green"].dir
// etc.
This data structure is convenient, as we'll see later.
First download these image files into your Thumb Wrestling directory:
Then define the following function:
function drawArrow(color) {
var row = gameState[color].row;
var col = gameState[color].col;
var cellId = "#cell-" + row + "-" + col;
var arrowId = color + "-arrow";
var src = color + "-arrow.png";
var imgTag = "<img id='" + arrowId + "' src='" + src + "''>";
$(cellId).append(imgTag);
}
This may be the first time you have seen the HTML <img>
tag. If so, look it up on Google.
Assuming you are familiar with <img>
, the functionality and operation of drawArrow(...)
should be clear to you.
Invoke the drawArrow(...)
function inside the createThumbWrestling(...)
function:
function createThumbWrestling(boardId) {
for (var row = 0; row < numRows; row++) {
var rowId = "row-" + row;
var rowTag = "<div id='" + rowId + "' class='row'></div>"
$(boardId).append(rowTag);
for (var col = 0; col < numCols; col++) {
var cellId = "cell-" + row + "-" + col;
var cellTag = "<div id='" + cellId + "' class='cell'></div>";
$("#" + rowId).append(cellTag);
}
}
drawArrow("red"); // <----------------------------
drawArrow("green"); // <----------------------------
}
View index.html
Notice that both arrows point to the right, even though the green arrow points to the left (according to gameState
).
It should be clear to you why both arrows point to the right.
Soon, we will make the arrows point in the directions according to gameState
.
Notice we have code that computes cellId
twice: once in drawArrow(...)
and once in createThumbWrestling(...)
:
function drawArrow(color) {
var row = gameState[color].row;
var col = gameState[color].col;
var cellId = "#cell-" + row + "-" + col; // <----------------------------------------------
var arrowId = color + "-arrow";
var src = color + "-arrow.png";
var imgTag = "<img id='" + arrowId + "' src='" + src + "''>";
$(cellId).append(imgTag);
}
function createThumbWrestling(boardId) {
for (var row = 0; row < numRows; row++) {
var rowId = "row-" + row;
var rowTag = "<div id='" + rowId + "' class='row'></div>"
$(boardId).append(rowTag);
for (var col = 0; col < numCols; col++) {
var cellId = "cell-" + row + "-" + col; // <----------------------------------------------
var cellTag = "<div id='" + cellId + "' class='cell'></div>";
$("#" + rowId).append(cellTag);
}
}
drawArrow("red");
drawArrow("green");
}
We want to avoid code duplication, so we hoist out these computations for cellId
into a new function:
function getCellId(row, col) {
return "cell-" + row + "-" + col;
}
Then we modify drawArrow(...)
and createThumbWrestling(...)
to invoke our new function:
function drawArrow(color) {
var row = gameState[color].row;
var col = gameState[color].col;
var cellId = "#" + getCellId(row, col); // <----------------------------------------------
var arrowId = color + "-arrow";
var src = color + "-arrow.png";
var imgTag = "<img id='" + arrowId + "' src='" + src + "''>";
$(cellId).append(imgTag);
}
function createThumbWrestling(boardId) {
for (var row = 0; row < numRows; row++) {
var rowId = "row-" + row;
var rowTag = "<div id='" + rowId + "' class='row'></div>"
$(boardId).append(rowTag);
for (var col = 0; col < numCols; col++) {
var cellId = getCellId(row, col); // <----------------------------------------------
var cellTag = "<div id='" + cellId + "' class='cell'></div>";
$("#" + rowId).append(cellTag);
}
}
drawArrow("red");
drawArrow("green");
}
We want each arrow to point in the correct direction (i.e. the graphical representation of each arrow should match
gameState[color].dir
).
First, we need to add the following CSS into style.css
:
.right {
transform: rotate(0deg);
}
.down {
transform: rotate(90deg);
}
.left {
transform: rotate(180deg);
}
.up {
transform: rotate(270deg);
}
Here's how it works: if an <img>
tag has class="down"
, then it's image will be rotated clockwise 90 degrees.
Since red-arrow.png
and green-arrow.png
point to the right, rotating 90 degrees causes the arrow to point down.
And so on for 0deg
, 180deg
, and 270deg
.
Then, we update drawArrow(...)
to rotate arrows:
function drawArrow(color) {
var row = gameState[color].row;
var col = gameState[color].col;
var dir = gameState[color].dir; // <-------------------------------------------------------------------
var cellId = "#" + getCellId(row, col);
var arrowId = color + "-arrow";
var src = color + "-arrow.png";
var imgTag = "<img id='" + arrowId + "' src='" + src + "' class='" + dir + "'>"; // <-------------------
$(cellId).append(imgTag);
}
View index.html
Modify thumb-wrestling.js
so that the appropriate arrows move across the board whenever a player presses a movement key (instead of popping up an alert
).
You do not need to worry about the following cases:
- Going out of bounds
- Bumping into an opponent
But first, you need to learn a new jQuery method: $("#" + elementId).remove()
removes the element with id
equal to elementId
from the HTML document. This will allow you to remove an arrow before redrawing it (otherwise, there would
be many duplicate arrows left on the board).
View index.html
Modify thumb-wrestling.js
so that arrows cannot go out of bounds.
View index.html
Modify thumb-wrestling.js
so that when one arrow bumps into the other, it stays put.
View index.html
Modify thumb-wrestling.js
to:
- Detect when a victory occurs
- Freeze the game when a victory occurs
Hints:
View index.html
Modify thumb-wrestling.js
to visualize a victory as so:
- If red wins, change the color of every cell to "pink"
- If green wins, change the color of every cell to "lightgreen"
But first, you need to learn a little more jQuery.
Recall, $("#" + elementId)
selects the element with id
equal to elementId
.
You can use a slightly different jQuery invocation to select all the elements that have a particular class.
$("." + className)
selects all the elements that have class className
.
For example, to select every cell you could do:
$(".cell")
View index.html
There are two steps to handling arrow movement. Every time a movement key is pressed:
- Update the game state
- Update the graphical representation
Back to Challenge 1.
To update the game state:
In the keyDown(...)
function replace the alert(color + " " + direction);
line with
move(color, direction);
.
Then define the function move(color, direction)
:
function move(color, direction) {
gameState[color].dir = direction;
gameState[color].row = // the arrow's new row
gameState[color].col = // the arrow's new column
}
Back to Challenge 1.
This is my preferred way of computing the arrow's new row and column:
// returns a 2-tuple [dr, dc], where:
// dr == difference in row
// dc == difference in column
function drdc(direction) {
if (direction == "up") {
return [-1, 0];
} else if (direction == "down") {
return [1, 0];
} else if (direction == "left") {
return [0, -1];
} else if (direction == "right") {
return [0, 1];
} else {
console.error("Bad direction: " + direction)
}
}
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
gameState[color].row += dr;
gameState[color].col += dc;
}
Back to Challenge 1.
Here's how you update the graphical representation to match the new game state:
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
gameState[color].row += dr;
gameState[color].col += dc;
drawArrow(color); // <-----------------------------------------------
}
And modify drawArrow(...)
to remove the old arrow, before drawing the new arrow:
function drawArrow(color) {
var row = gameState[color].row;
var col = gameState[color].col;
var dir = gameState[color].dir;
var cellId = "#" + getCellId(row, col);
var arrowId = color + "-arrow";
var src = color + "-arrow.png";
var imgTag = "<img id='" + arrowId + "' src='" + src + "' class='" + dir + "'>";
$("#" + arrowId).remove(); // <--------------------------------------------------------------
$(cellId).append(imgTag);
}
Back to Challenge 1.
function keydown(event) {
var playerMovement = getPlayerMovment(event.keyCode);
// If the user pressed a key we're uninterested in
if (playerMovement == undefined) {
return;
}
var [color, direction] = playerMovement;
// disable browser scrolling on arrow keys
if (color == "red") {
event.preventDefault();
}
move(color, direction); // <--------------------------------------------------------------
}
// returns a 2-tuple [dr, dc], where:
// dr == difference in row
// dc == difference in column
function drdc(direction) {
if (direction == "up") {
return [-1, 0];
} else if (direction == "down") {
return [1, 0];
} else if (direction == "left") {
return [0, -1];
} else if (direction == "right") {
return [0, 1];
} else {
console.error("Bad direction: " + direction)
}
}
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
gameState[color].row += dr;
gameState[color].col += dc;
drawArrow(color);
}
function drawArrow(color) {
var row = gameState[color].row;
var col = gameState[color].col;
var dir = gameState[color].dir;
var cellId = "#" + getCellId(row, col);
var arrowId = color + "-arrow";
var src = color + "-arrow.png";
var imgTag = "<img id='" + arrowId + "' src='" + src + "' class='" + dir + "'>";
$("#" + arrowId).remove(); // <--------------------------------------------------------------
$(cellId).append(imgTag);
}
Back to Challenge 1.
Here's what the old move(...)
function looks like.
You can't just update the gameState
based on dr
and dc
.
First, you need make sure that updating the gameState
won't cause an arrow to go out of bounds.
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
gameState[color].row += dr;
gameState[color].col += dc;
drawArrow(color);
}
Back to Challenge 2.
You want to update gameStatep[color].dir
and call drawArrow(...)
regardless of whether
the movement succeeds or the movement is cancelled (because the movement would go out of bounds).
Back to Challenge 2.
function inBounds(row, col) {
return row >= 0 &&
row < numRows &&
col >= 0 &&
col < numCols;
}
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
var newRow = gameState[color].row + dr;
var newCol = gameState[color].col + dc;
if (inBounds(newRow, newCol)) {
gameState[color].row = newRow;
gameState[color].col = newCol;
}
drawArrow(color);
}
Back to Challenge 2.
// returns true iff there is an arrow at (row, col)
function occupied(row, col) {
return (gameState["red"].row == row &&
gameState["red"].col == col) ||
(gameState["green"].row == row &&
gameState["green"].col == col);
}
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
var newRow = gameState[color].row + dr;
var newCol = gameState[color].col + dc;
if (inBounds(newRow, newCol) && !occupied(newRow, newCol)) { // <-----------------------------------
gameState[color].row = newRow;
gameState[color].col = newCol;
}
drawArrow(color);
}
Back to Challenge 3.
A victory occurs when the following conditions hold:
- The arrow is attempting to move into an occupied cell
- The arrows are not facing each other
Back to Challenge 4.
The arrows are facing each other when they are pointing in opposite directions.
Back to Challenge 4.
- If one arrow is pointing up, then the opposite direction is down.
- If one arrow is pointing left, then the opposite direction is right.
etc.
Back to Challenge 4.
Use these functions:
function oppositeDirection(direction) {
var oppositeMap = {
"up": "down",
"down": "up",
"left": "right",
"right": "left"
}
return oppositeMap[direction];
}
// returns true iff the arrows are facing each other, i.e. the arrows
// are facing opposite directions
function facingEachOther() {
return gameState["red"].dir == oppositeDirection(gameState["green"].dir);
}
Back to Challenge 4.
Modify the move(...)
function as follows:
function move(color, direction) {
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
var newRow = gameState[color].row + dr;
var newCol = gameState[color].col + dc;
if (occupied(newRow, newCol) && !facingEachOther()) { // <-------------------------------------------
// game over!
} else if (inBounds(newRow, newCol) && !occupied(newRow, newCol)) {
gameState[color].row = newRow;
gameState[color].col = newCol;
}
drawArrow(color);
}
Back to Challenge 4.
To freeze the game after a victory, create a global boolean variable called gameOver
and initialize it to false
.
When a victory occurs set gameOver
to true
.
All that is left to do, is use is check for gameOver
every time move(...)
is invoked.
If gameOver
is true
, then what should you do to freeze the game?
Back to Challenge 4.
At the top of thumb-wrestling.js
:
var numRows = 10;
var numCols = 10;
var gameOver = false;
...
Then add oppositeDirection(...)
and facingEachOther(...)
:
function oppositeDirection(direction) {
var oppositeMap = {
"up": "down",
"down": "up",
"left": "right",
"right": "left"
}
return oppositeMap[direction];
}
// returns true iff the arrows are facing each other, i.e. the arrows
// are facing opposite directions
function facingEachOther() {
return gameState["red"].dir == oppositeDirection(gameState["green"].dir);
}
Modify move(...)
as follows:
function move(color, direction) {
if (gameOver) { // <----------------------------------------------------------------------
return;
}
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
var newRow = gameState[color].row + dr;
var newCol = gameState[color].col + dc;
if (occupied(newRow, newCol) && !facingEachOther()) { // <--------------------------------
gameOver = true;
} else if (inBounds(newRow, newCol) && !occupied(newRow, newCol)) {
gameState[color].row = newRow;
gameState[color].col = newCol;
}
drawArrow(color);
}
Back to Challenge 4.
$(".cell").css("background", "pink")
changes every cell to have a pink background color.
Back to Challenge 5.
Define the following drawVictory(...)
function:
function drawVictory(color) {
var cellColor;
if (color == "green") {
cellColor = "lightgreen";
} else if (color == "red") {
cellColor = "pink";
} else {
console.error("Bad color: " + color);
}
$(".cell").css("background", cellColor);
}
Then add one additional line to move(...)
:
function move(color, direction) {
if (gameOver) {
return;
}
gameState[color].dir = direction;
var [dr, dc] = drdc(direction);
var newRow = gameState[color].row + dr;
var newCol = gameState[color].col + dc;
if (occupied(newRow, newCol) && !facingEachOther()) {
gameOver = true;
drawVictory(color); // <---------------------------------------------------------
} else if (inBounds(newRow, newCol) && !occupied(newRow, newCol)) {
gameState[color].row = newRow;
gameState[color].col = newCol;
}
drawArrow(color);
}
Back to Challenge 5.