Learn to Code via Tutorials on Repl.it

← Back to all posts
6
Javascript Games: Lesson 1 -- GuessIt

Introduction

This is the first tutorial in a series designed to teach basic JavaScript game programming. It's the spiritual successor to BASIC Computer Games, in which David H. Ahl provided source code for 100 BASIC games. Today, we would think of those games as "terminal programs", displayed using ASCII text. We will be running our games in a web browser, and while they will initially be text-based, we will quickly move into the realm of canvas graphics.

But first things first. In this tutorial, we will cover the boilerplate concepts behind all games: variables, output, input, functions, loops, and flow-of-control. Don't worry if you don't know what any of those mean -- that's what the tutorial is for. Once we have discussed these fundamentals, we'll present the code for our first game: GuessIt.

Fundamentals

All games rely on the following foundational concepts. More advanced games involve additional ideas, but every game needs these:

  • variables -- named buckets that hold data
  • output -- how we show the game to the user
  • input -- how we accept data from the user
  • functions -- how we re-use code from at different points in a program
  • loops -- how we perform repeated tasks efficiently
  • flow-of-control -- how we make the game choose one path among many

Variables
Variables are just chunks of computer memory that hold data for us. You can think of a variable as a bucket with a name. You can put things in the bucket and take them out again.

In some languages, there are different kinds of buckets for different kinds of data. Text goes in one kind of bucket, decimal numbers in another, and integers in yet another. In JavaScript, all buckets can hold anything you want. This simplifies life in some ways, and complicates it in others.

To define a new bucket, we use the 'var' keyword, followed by the name of the bucket:

var myName;

(the semicolon tells JavaScript that it has reached the end of this line of code).

Once you have defined a new bucket, you can refer to it by name elsewhere in your code. You can use operators like the equals sign to manipulate the data inside the bucket.

For example, in JavaScript, the single equals sign (=) tells the computer to take whatever is on the right hand side and put it into the thing on the left hand side:

myName = "Mark";

takes the text data inside the double quotes and sticks it in the bucket called myName.

You can combine the definition of the bucket and the assignment of its value into a single line, if you like:

var myAge = 49;

Now the variable 'myAge' has a value of 49.

Variables can hold text data, numerical data, code, graphics, sound, and any combination of these things wrapped into an 'object' (which we will cover in later tutorials).

Variables can also contain lists of data. These lists are often called "Arrays", and you create a list by enclosing the entries in square brackets ('[' and ']') and separating them by commas:

var myDogs = ["Hershey", "Koda"];

You can also break this up over multiple lines:

var myDogs = [
    "Hershey",
    "Koda"
];

That's a lot to remember, but don't sweat it. The most important thing is:
variables are buckets that hold data.

Output
All games need to output data to the player(s). Our first games will run on a web page, so we will use HTML for output. We will rely on the "write line" command supplied by the document in which we are running:

document.writeln("Your message goes here...");

Note that in HTML, the "string" of letters '<br>' (without quotes) is a "line break" that marks the end of a line of text. Use this to move down to the next line.

For example:

document.writeln("First line.<br>");
document.writeln("Second line.<br>");

These will appear as two separate lines in the web browser. Without the line breaks, they would appear together on a single line, even though we used two different writeln statements.

Input
Players need a way to affect the games they play, whether it's telling the dealer to "hit me" in Blackjack, or firing a weapon in Doom. In our case, we will be responding to keyboard events. To do this, we will rely on the "event listener" system supplied by the document in which we are running.

An "event" is a change in the computer environment that affects the document. This could be many things: a key press, a mouse click, a message telling the browser to resize itself. Web documents supply an "event listener" system to capture and respond to important events. To listen for a particular event, we will call the document's "addEventListener" code:

document.addEventListener("keydown", function(event) {
    // ...your code here...
});

The above line tells the document to execute "...your code here..." whenever the user presses key down. Don't worry about the 'function(event)' bit -- we'll discuss that in the next section. For now, let's consider this concrete example:

document.addEventListener("keydown", function(event) {
    document.writeln("Someone pressed a key!");
});

This displays the message, "Someone pressed a key!" each time the user presses a key.

There are many other events to which the document can respond, like "keyup", "mousedown", "mouse", and "mousemove", just to name a few. We'll get to those when we need them. For now, "keydown" is good enough.

Sometimes, you want your game to ignore events to which it previously responded. You can accomplish this using the "removeEventListener" command:

document.removeEventListener("keydown");

This would undo the "addEventListener" we used above.

Functions
Often, you write a chunk of a code that you want to use over and over. For example, suppose you wrote a chess program. Games of chess can last many turns. You would hope that you could write the code to play a single turn, then simply re-use that code for each subsequent turn.

Functions allow you to do this. A function is a chunk of self-contained code designed to perform a specific task. Functions often require data to complete their task, so when you define the function, you must indicate what data it's expecting as input. In JavaScript, function definitions look like this:

function(list of input data) {
    // Chunk of code to execute using the input data
};

If this seems confusing, think of a function as a meat grinder: you put stuff in, you turn the crank, and something useful comes out. The "stuff you put in" is the "list of input data". The "turn the crank" bit is the "chunk of code" part. The "something useful comes out," well...we haven't covered that. It's called the 'return value'. A specific example will clear all this up. Consider a function that computes the area of a rectangle:

function(length, width) {
    var area = length * width;

    return area;
};

You send in a length and a width, it multiplies them together to find the area, then it 'returns' that area back to you.

Remember that addEventListener stuff? Recall that the function looked like this:

function(event) {
    document.writeln("Someone pressed a key!");
}

Now you know what the 'function' is for -- that tells JavaScript what function to execute when someone presses a key. The 'event' data is passed into the function from the web browser, and it tells the function things like what key was pressed. In fact, we could change the function to display that information as follows:

function(event) {
    document.writeln("Someone pressed the " + event.key + " key!");
}

Notice that this function doesn't return anything (there is no 'return' statement at the end). That's fine. Not all functions return a value. Sometimes you use them just to produce output when the crank is turned.

One last note: you can store a function inside a variable. Doing so lets you execute (or call) the function using the bucket's name. This is handy shorthand for executing code!

Example:

var print = function(theMessage) {
    document.writeln(theMessage + "<br>");
};

Now, any time you want to display a message, you can use this:

print("Fred ate bread!");

This will send the data "Fred ate bread" to the above function and print it out, automatically appending a <br> so that subsequent messages appear on subsequent lines in the document.

Applying this to our area function, we get:

var areaOfRect = function(length, width) {
    var area = length * width;
    return area;
}

And we could write a short program as follows:

var area = areaOfRect(3, 4);
print("The area is " + area);

What do you think would be the output of this program?

Loops
Often, games need to rapidly repeat a task. For example, if you are drawing a square grid, you must print multiple horizontal and vertical lines. For something like tic-tac-toe, this isn't so bad, but for a game like Go, you wouldn't want to draw every line with a new command.

Loops allow programmers to efficiently perform duplicate commands. There are several kinds of loops, all of them follow the same philosophy: the programmer defines a loop condition during which the program repeats a chunk of code over and over. The programmer follows the loop condition by the code to be repeated:

[Loop Condition Goes Here]
{
    // ...code to repeat goes here
}

Notice that in JavaScript, we use curly bracers ('{' and '}') to "section off" the code that will be repeated.

JavaScript supports several different kinds of loops. We will start with just two: the 'while' loop and the 'for' loop (which is really just a 'while' loop in fancy clothes).

The while loop looks like this:

while (...something is true...) {
    // Do this code
}

Consider this example:

var greets = 0;
while (greets < 5) {
    print("Hey there!");
    greets = greets + 1;
}

This probably looks confusing, especially this bit:

greets = greets + 1;

Clearly, this is nonsense! There is no number that equals itself plus one.

True...but remember -- in JavaScript, the '=' symbol differs from what you learned in Math. In JavaScript, it says, "takes what's on the right-hand side and put it into the thing on the left-hand side." Also remember that 'greets' is a bucket that we initially fill with the value 0. So this:

greets = greets + 1;

reads as:

"Take the value in greets (currently 0), add one to it, and place this back in the greets bucket."

So, after executing this line the first time, greets' value changes from 0 to 1.

We then hit the bottom curly brace and bounce back up to the 'while' statement, where we check the loop condition. Is the value in greets less than 5?

Yep! So we once again execute the code inside the curly braces. While doing so, we again hit the line

greets = greets + 1;

but this time, greets contains '1'. So what happens? We add one to it, and store the resulting value (2) back in greets.

Then we hit the bottom curly brace, bounce back up to the top of the loop, and check the conditions. 2 is still less than 5, so we go around again...
...and again...
...and again...
...and again...
...and finally we bounce back to the top, check the condition and see that greets is equal to 5, rather than less than 5, so the loop condition is false and we break out of the loop by instantly jumping past the last curly brace.

That might seem like a silly example: why would we want to print out the same message 5 times? So let's consider a more useful example. Remember my dogs?

var myDogs = ["Hershey", "Koda"];
var count = 0;

while (count < myDogs.length) {
    print(myDogs[count] + " is barking.");
    count = count + 1;
}

Now we're using the while statement to iterate (or step through) a list of data. Notice that we use square brackets ('[' and ']') to access a single element of the list, and that the first element in the list is number 0. In other words:

myDogs[0]

contains the word "Hershey", and

myDogs[1]

contains the word "Koda".

Also note that JavaScript will tell you how long any list is when you use the ".length" accessor:

myDogs.length

is 2.

While loops are handy, but after you've written a few, you'll see that most of them fall into the following pattern:

var some_loop_counter = some initial value;
while (condition_involving_the_loop_counter) {
    // ...loop code...
    some_loop_counter = some_loop_counter + some increment;
}

Recognizing this pattern, the authors of JavaScript created the for loop to make your code more concise. The for loop works exactly like the while loop, but it lets you compress the syntax:

for (initialize the counter; check the condition; update the counter) {
    // ...loop code...
}

For example, our doggy-loop above would look like this when written as a for loop:

for (var count = 0; count < myDogs.length; count = count + 1) {
    print("My dog " + myDogs[count] + " is barking.");
}

If you want to compress this even further, you could take advantage of the math operators '+=' or '++':

count += 1;

is shorthand for

count = count + 1;

And both

count++
and
++count

tell JavaScript to increase the value of count by 1. There is a difference between these ++count and count++, but we'll save that for another tutorial.

So, the super-compressed version of our doggy loop would be this:

for (var count = 0; count < myDogs.length; ++count) {
    print("My dog " + myDogs[count] + " is barking.");
}

Flow-of-Control
Finally, let's talk about "flow of control." That's just a fancy way of saying, "my game can execute one of several options at the appropriate time." For example, suppose you are making a dungeon crawler and you have AI monsters that can take one of several actions when encountering the player. You would use flow of control statements to pick one of those actions when an encounter occurs.

As with loops, there are several flavors of decision-making in JavaScript. For now, we will restrict ourselves to the "if...then" statement. Generally speaking, that structure looks like this:

If (condition 1 is true)
{
    // Do this code
}
else if (condition 2 is true)
{
    // Do *this* code instead
}
else if (condition 3 is true)
{
    // etc.
}
// and so on for as many conditions as you like, until finally
else {
    // Do this if nothing else up to this point was true.
}

Let's look at an example by modifying our doggy loop:

for (var i=0; i<myDogs.length; ++i) {
    if (myDogs[i] === "Hershey") {
        print(myDogs[i] + " is barking.");
    }
   else if (myDogs[i] === "Koda") {
        print(myDogs[i] + " is farting.");
    }
   else {
       print("That's not my dog!");
    }
}

Note that the triple-equals ('===') in JavaScript asks the question, "is equal to". So a line like this:

if (myDogs[i] === "Hershey")

Reads as, "if the ith element in myDogs is equal to the word Hershey".

As you can see, this will print out:

Hershey is barking.
Koda is farting.

Which might seem like a fart joke, but is sadly the status quo with my black lab, who is currently practicing his "art" 6 feet from my desk.
Ugh.

Conclusion

You now have all the building blocks you need to make a simple game. For our first outing, we will write "GuessIt" -- a variation on the card game Acey Ducey. In that game, you flip two cards, then place a bet as to whether a third card, when revealed, will fall between the first two. In GuessIt, we use randomly-generated numbers from 0 to 9, but otherwise, the game is identical.

Take a look at the source code. We have broken it up into two files:

definitions.js -- in which we define all the functions and variables used in the game
commands.js -- which actually executes the commands defined in definitions.js to start the game

If you just loaded definitions.js, the game would never start, because the definitions don't do anything but fill buckets with values and functions.
If you just loaded commands.js, the program would throw an error because the commands rely on the buckets defined in definitions.js.

You don't have to lay out your JavaScript programs this way -- we just did it to drive home the point that defining variables and functions is a passive activity, like setting up the game board. It doesn't actually result in the activity of playing the game itself. To do that, you have to execute the commands you created earlier.

The code for GuessIt contains a lot of comments to help you understand what we're doing at each point. Some of it repeats what we have covered here, but in a more specific context. Once you are comfortable with what it's doing, try modifying the game. Here are some suggestions:

Change the background color by using the command:
document.body.style.backgroundColor = "gray"; // Or whatever common color name you want

Change the amount of starting cash given to the user
Change the payout formula

Or, the biggie:
Add a new eventListener when the player loses that resets her starting cash and allows her to play again

Good luck, and have fun!

Commentshotnewtop
1

Hi, Mark.

Works great; thanks!

If you'd like to see them, I have some "grammatical opportunities" for you; just send an e-mail to me at [email protected] if you would like them.

1

Nice! You should use markdown to style your post :)

1

@timmy_i_chen Er...I thought I did. :) I used headers, bold, italic, lists, and code segments. I guess I need to up my game...

1

@MarkKreitler

Code segments can be syntax-highlighted as well - should really be used anytime there's a code block :)

1

@timmy_i_chen Didn't know that! Thanks for the tip. I will fix that right up. :)

1

@MarkKreitler Nice! Looks a lot better already :)

1

@timmy_i_chen Thanks for the tip! You do an amazing job curating the content!