Cover by Jeff Fulton, Steve Fulton

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

O'Reilly logo

Another Example: Guess The Letter

Now we will take a quick look at a more involved example of a “Hello World!”-type application, the game “Guess The Letter.” We’ve included this example to illustrate how much more Canvas programming is done in JavaScript than in the Canvas API.

In this game, shown in Figure 1-4, the player’s job is to guess the letter of the alphabet the computer has chosen randomly. The game keeps track of how many guesses the player has made, lists the letters he has already guessed, and tells the player whether he needs to guess higher (toward Z) or lower (toward A).

HTML5 Canvas “Guess The Letter” game

Figure 1-4. HTML5 Canvas “Guess The Letter” game

How the Game Works

This game is set up with the same basic structure as “Hello World!” canvasApp() is the main function, and all other functions are defined as local to canvasApp(). We use a drawScreen() function to render text on the canvas. However, there are some other functions included as well, which are described next.

The “Guess The Letter” Game Variables

Here is a rundown of the variables we will use in the game. They are all defined and initialized in canvasApp(), so they have scope to the encapsulated functions that we define locally.

guesses

This variable holds the number of times the player has pressed a letter. The lower the number, the better he has done in the game.

message

The content of this variable is displayed to give the user instructions on how to play.

letters

This array holds one of each letter of the alphabet. We will use this array to both randomly choose a secret letter for the game, and to figure out the relative position of the letter in the alphabet.

today

This variable holds the current date. It is displayed on the screen but has no other purpose.

letterToGuess

This variable holds the current game’s secret letter that needs to be guessed.

higherOrLower

This variable holds the text “Higher” or “Lower” depending on where the last guessed letter is in relation to the secret letter. If the secret letter is closer to “a,” we give the “Lower” instruction. If the letter is closer to “z,” we give the “Higher” instruction.

lettersGuessed

This array holds the current set of letters the player has guessed already. We will print this list on the screen to help the player remember what letters he has already chosen.

gameOver

This variable is set to false until the player wins. We will use this to know when to put the “You Win” message on the screen, and to keep the player from guessing after he has won.

Here is the code:

   var guesses = 0;
   var message = "Guess The Letter From a (lower) to z (higher)";
   var letters = [
               "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
               "p","q","r","s","t","u","v","w","x","y","z"
               ];
   var today = new Date();
   var letterToGuess = "";
   var higherOrLower = "";
   var lettersGuessed;
   var gameOver = false;

The initGame() Function

The initGame() function sets up the game for the player. The two most important blocks of code are as follows. This code finds a random letter from the letters array and stores it in the letterToGuess variable:

var letterIndex = Math.floor(Math.random() * letters.length);
letterToGuess = letters[letterIndex];

This code adds an event listener to the window object of the DOM to listen for the keyboard keyup event. When a key is pressed, the eventKeyPressed event handler is called to test the letter pressed:

window.addEventListener("keyup",eventKeyPressed,true);

Here is the full code for the function:

function initGame() {
   var letterIndex = Math.floor(Math.random() * letters.length);
   letterToGuess = letters[letterIndex];
   guesses = 0;
   lettersGuessed = [];
   gameOver = false;
   window.addEventListener("keyup",eventKeyPressed,true);
   drawScreen();
}

The eventKeyPressed() Function

This function, called when the player presses a key, contains most of the action in this game. Every event handler function in JavaScript is passed an event object that has information about the event that has taken place. We use the e argument to hold that object.

The first test we make is to see whether the gameOver variable is false. If so, we continue to test the key that was pressed by the player; the next two lines of code are used for that purpose. The first line of code gets the key-press value from the event, and converts it to an alphabetic letter that we can test with the letter stored in letterToGuess:

var letterPressed = String.fromCharCode(e.keyCode);

The next line of code converts the letter to lowercase so that we can test uppercase letters if the player unintentionally has Caps Lock on:

letterPressed = letterPressed.toLowerCase();

Next, we increase the guesses count to display, and use the Array.push() method to add the letter to the lettersGuessed array:

guesses++;
lettersGuessed.push(letterPressed);

Now it is time to test the current game state to give feedback to the player. First, we test to see whether letterPressed is equal to letterToGuess. If so, the player has won the game:

if (letterPressed == letterToGuess) {
   gameOver = true;

If the player has not won, we need to get the index of letterToGuess and the index of letterPressed in the letters array. We are going to use these values to figure out whether we should display “Higher,” “Lower,” or “That is not a letter.” To do this, we use the indexOf() array method to get the relative index of each letter. Because we alphabetized the letters in the array, it is very easy to test which message to display:

} else {
   letterIndex = letters.indexOf(letterToGuess);
   guessIndex = letters.indexOf(letterPressed);

Now we make the test. First, if guessIndex is less than zero, it means that the call to indexOf() returned -1, and the key press was not a letter. We then display an error message:

if (guessIndex < 0) {
   higherOrLower = "That is not a letter";

The rest of the tests are simple. If guessIndex is greater than letterIndex, we set the higherOrLower text to “Lower.” Conversely, if guessIndex is less than letterIndex, we set the higherOrLower test to “Higher”:

   } else if (guessIndex > letterIndex) {
      higherOrLower = "Lower";
   } else {
      higherOrLower = "Higher";
   }

}

Finally, we call drawScreen() to paint the screen:

drawScreen();

Here is the full code for the function:

function eventKeyPressed(e) {
      if (!gameOver) {
         var letterPressed = String.fromCharCode(e.keyCode);
         letterPressed = letterPressed.toLowerCase();
         guesses++;
         lettersGuessed.push(letterPressed);

         if (letterPressed == letterToGuess) {
            gameOver = true;
         } else {

            letterIndex = letters.indexOf(letterToGuess);
            guessIndex = letters.indexOf(letterPressed);
            Debugger.log(guessIndex);
            if (guessIndex < 0) {
               higherOrLower = "That is not a letter";
            } else if (guessIndex > letterIndex) {
               higherOrLower = "Lower";
            } else {
               higherOrLower = "Higher";
            }

         }
         drawScreen();
        }
   }

The drawScreen() Function

Now we get to drawScreen(). The good news is that we have seen almost all of this before—there are only a few differences from “Hello World!” For example, we paint multiple variables on the screen using the Canvas Text API. We only set context.textBaseline = 'top'; once for all the text we are going to display. Also, we change the color using context.fillStyle, and the font with context.font.

The most interesting thing we display here is the content of the lettersGuessed array. On the canvas, the array is printed as a set of comma-separated values, like this:

Letters Guessed: p,h,a,d

To print this value, all we do is use the toString() method of the lettersGuessed array, which prints out the values of an array as—you guessed it—comma-separated values:

context.fillText  ("Letters Guessed: " + lettersGuessed.toString(), 10, 260);

We also test the gameOver variable. If it is true, we put You Got It! on the screen in giant 40px text so the user knows he has won.

Here is the full code for the function:

function drawScreen() {
      //Background
      context.fillStyle = "#ffffaa";
      context.fillRect(0, 0, 500, 300);
      //Box
      context.strokeStyle = "#000000";
      context.strokeRect(5,  5, 490, 290);

      context.textBaseline = "top";
      //Date
      context.fillStyle = "#000000";
      context.font = "10px _sans";
      context.fillText  (today, 150 ,10);
      //Message
      context.fillStyle = "#FF0000";
      context.font = "14px _sans";
      context.fillText  (message, 125, 30);
     //Guesses
      context.fillStyle = "#109910";
      context.font = "16px _sans";
      context.fillText  ('Guesses: ' + guesses, 215, 50);
      //Higher Or Lower
      context.fillStyle = "#000000";
      context.font = "16px _sans";
      context.fillText  ("Higher Or Lower: " + higherOrLower, 150,125);
      //Letters Guessed
      context.fillStyle = "#FF0000";
      context.font = "16px _sans";
      context.fillText  ("Letters Guessed: " + lettersGuessed.toString(), 10, 260);
      if (gameOver) {
         context.fillStyle = "#FF0000";
         context.font = "40px _sans";
         context.fillText  ("You Got It!", 150, 180);
      }
   }

Exporting Canvas to an Image

Earlier, we briefly discussed the toDataUrL() property of the Canvas object. We are going to use that property to let the user create an image of the game screen at any time. This acts almost like a screen-capture utility for games made on Canvas.

We need to create a button in the HTML page that the user can press to get the screen capture. We will add this button to <form> and give it the id createImageData:

<form>
<input type="button" id="createImageData" value="Export Canvas Image">
</form>

In the init() function, we retrieve a reference to that form element by using the getElementById() method of the document object. We then set an event handler for the button “click” event as the function createImageDataPressed():

var formElement = document.getElementById("createImageData");
formElement.addEventListener('click', createImageDataPressed, false);

In canvasApp(), we define the createImageDataPressed() function as an event handler. This function calls window.open(), passing the return value of the Canvas.toDataUrl() method as the source for the window. Since this data forms a valid PNG, the image is displayed in the new window:

function createImageDataPressed(e) {

   window.open(theCanvas.toDataURL(),"canvasImage","left=0,top=0,width=" + 
   theCanvas.width + ",height=" + theCanvas.height +",toolbar=0,resizable=0");
   }

Note

We will discuss this process in depth in Chapter 3.

The Final Game Code

Example 1-4 shows the full code for the Guess The Letter game.

Example 1-4. Guess The Letter game

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CH1EX4: Guesss The Letter Game</title>
<script src="modernizr-1.6.min.js"></script>
<script type="text/javascript">

window.addEventListener('load', eventWindowLoaded, false);

var Debugger = function () { };
Debugger.log = function (message) {
   try {
      console.log(message);
   } catch (exception) {
      return;
   }
}

function eventWindowLoaded() {

   canvasApp();
}

function canvasSupport () {
     return Modernizr.canvas;
}

function eventWindowLoaded() {

   canvasApp();
}

function canvasApp() {
   var guesses = 0;
   var message = "Guess The Letter From a (lower) to z (higher)";
   var letters = [
               "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
               "p","q","r","s","t","u","v","w","x","y","z"
               ];
   var today = new Date();
   var letterToGuess = "";
   var higherOrLower = "";
   var lettersGuessed;
   var gameOver = false;

   if (!canvasSupport()) {
          return;
        }

   var theCanvas = document.getElementById("canvasOne");
   var context = theCanvas.getContext("2d");

   initGame();

   function initGame() {
      var letterIndex = Math.floor(Math.random() * letters.length);
      letterToGuess = letters[letterIndex];
      guesses = 0;
      lettersGuessed = [];
      gameOver = false;
      window.addEventListener("keyup",eventKeyPressed,true);
      var formElement = document.getElementById("createImageData");
      formElement.addEventListener('click', createImageDataPressed, false);
      drawScreen();
   }

   function eventKeyPressed(e) {
      if (!gameOver) {
         var letterPressed = String.fromCharCode(e.keyCode);
         letterPressed = letterPressed.toLowerCase();
         guesses++;
         lettersGuessed.push(letterPressed);

         if (letterPressed == letterToGuess) {
            gameOver = true;
         } else {

            letterIndex = letters.indexOf(letterToGuess);
            guessIndex = letters.indexOf(letterPressed);
            Debugger.log(guessIndex);
            if (guessIndex < 0) {
               higherOrLower = "That is not a letter";
            } else if (guessIndex > letterIndex) {
               higherOrLower = "Lower";
            } else {
               higherOrLower = "Higher";
            }

         }
         drawScreen();
        }
   }

   function drawScreen() {
      //Background
      context.fillStyle = "#ffffaa";
      context.fillRect(0, 0, 500, 300);
      //Box
      context.strokeStyle = "#000000";
      context.strokeRect(5,  5, 490, 290);
      context.textBaseline = "top";
      //Date
      context.fillStyle = "#000000";
      context.font = "10px _sans";
      context.fillText  (today, 150 ,10);
      //Message
      context.fillStyle = "#FF0000";
      context.font = "14px _sans";
      context.fillText  (message, 125, 30);
      //Guesses
      context.fillStyle = "#109910";
      context.font = "16px _sans";
      context.fillText  ('Guesses: ' + guesses, 215, 50);
      //Higher Or Lower
      context.fillStyle = "#000000";
      context.font = "16px _sans";
      context.fillText  ("Higher Or Lower: " + higherOrLower, 150,125);
      //Letters Guessed
      context.fillStyle = "#FF0000";
      context.font = "16px _sans";
      context.fillText  ("Letters Guessed: " + lettersGuessed.toString(), 10, 260);
      if (gameOver) {
         context.fillStyle = "#FF0000";
         context.font = "40px _sans";
         context.fillText  ("You Got It!", 150, 180);
      }
   }

   function createImageDataPressed(e) {

      window.open(theCanvas.toDataURL(),"canvasImage","left=0,top=0,width=" + 
      theCanvas.width + ",height=" + theCanvas.height +",toolbar=0,resizable=0"); 
   }

}

</script>
</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">
<canvas id="canvasOne" width="500" height="300">
 Your browser does not support HTML5 Canvas.
</canvas>
<form>
<input type="button" id="createImageData" value="Export Canvas Image">
</form>
</div>
</body>
</html>

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required