Chapter 9. Hangman

Hangman
  • CSS styles

  • generating markup for alphabet buttons

  • using an array for a sequence of drawings

  • using a character string for the secret word

  • an external script file for the word list

  • setting up and removing event handling

Introduction

The goal for this chapter is to continue demonstrating programming techniques and the features of HTML5, Cascading Style Sheets (CSS), and JavaScript, combining dynamic creation of HTML markup along with drawing graphics and text on the canvas. The example for this chapter is another familiar game—the paper-and-pencil game of Hangman.

Just in case you need to brush up on the rules, the game is played as follows: One player thinks of a secret word and writes out dashes to let the other player know how many letters are in that word. The other person guesses individual letters. If the letter appears in the word, player one replaces the dash representing each occurrence of the guessed letter with the actual letter. If the letter does not appear in the secret word, the first player draws the next step in a progression of stick figure drawings of a hanging. In my example shown in Figure 9-1, the gallows are already on the screen. Next comes the head, then the body, left arm, right arm, left leg, right leg, and finally, the rope. Players can come to an agreement on how many steps are allowed. Player two loses the game if the hanging is complete before the word is guessed. Yes, this is a ghoulish game, but it is popular and even considered educational.

In our game, the computer takes the role of player one and picks the secret word from a word list (in this case an admittedly very short list). You may use my list. When you make your own game, use your own. It makes sense to start small and, once you are happy with your game, make a longer list. My technique of using an external file for the word list supports this approach.

For the user interface, I chose to place blocks with each letter of the alphabet on the screen. The player chooses a letter by clicking a block. After a letter is selected, its block disappears. This decision was influenced by the fact that most people playing the pencil-and-paper version write out the alphabet and cross out the letters as they are chosen.

Figure 9-1 shows the opening screen. The computer has selected a word with four letters. Notice that in our program, the gallows appears on the screen already. Alternatively, you can choose to make that the first one or two steps of the progression of drawings.

Opening screen

Figure 9.1. Opening screen

One advantage to using a small word bank is that I know what the word is now, even though my coding uses a random process to select the word. This means I can develop the game without any stress in playing it. I decided to select an a first. As Figure 9-2 shows, this letter does not appear in the secret word, so an oval for a head is drawn on the screen, and the block for the letter a disappears.

Screenshot after guessing an a

Figure 9.2. Screenshot after guessing an a

Working through the vowels, I guess an e, with results shown in Figure 9-3.

The game after guessing an e

Figure 9.3. The game after guessing an e

Next, I guess an i, resulting in my third wrong move, as shown in Figure 9-4.

The game screen after three incorrect selections

Figure 9.4. The game screen after three incorrect selections

Now, I guess an o, and this turns out to be correct (as I knew since I have insider information), and an o appears as the third letter in the word, as shown in Figure 9-5.

A correct guess of o

Figure 9.5. A correct guess of o

I try the next vowel, u, and that is correct also, as Figure 9-6 indicates.

Two letters have been identified.

Figure 9.6. Two letters have been identified.

I now make some more guesses, first a t, as shown in Figure 9-7.

Another wrong guess of t

Figure 9.7. Another wrong guess of t

Then, I make another wrong guess, this time, an s, as shown in Figure 9-8.

After a wrong guess of s

Figure 9.8. After a wrong guess of s

Figure 9-9 shows yet another wrong guess.

After a wrong guess of d

Figure 9.9. After a wrong guess of d

I decide to make a correct guess, namely m. Figure 9-10 shows three identified letters and most of the person drawn on the screen.

After a correct guess of m

Figure 9.10. After a correct guess of m

At this point, I am trying to lose, so I guess b. This results in what is depicted in Figure 9-11.

Game lost

Figure 9.11. Game lost

Notice that the drawing shows a noose; the complete secret word is revealed; and a message appears telling the player that the game is lost and to reload to try again.

Figure 9-12 shows a screenshot from another game, and the computer has responded to a guess of the letter e by showing it in two positions. Handling letters appearing more than once is not difficult, but that certainly was not obvious to me before I started the programming.

In this game, e appears in two spots.

Figure 9.12. In this game, e appears in two spots.

I make some other guesses and finally get this word correct. Again, the list from which the choices are made is not very long, so I can guess the words from the number of letters. Figure 9-13 shows a screenshot from a winning game. Notice that there are two e's and three f's in the secret word.

Winning the game

Figure 9.13. Winning the game

The programming techniques and language features include manipulating character strings; using an array holding the letters of the English alphabet; creating markup elements to hold the alphabet and the spaces that represent the secret word, which may or may not be replaced by letters; handling events for the created alphabet blocks; setting up a set of functions for drawing the steps of the hanging; and placing the names of the functions in an array. This implementation also demonstrates the use of external script files for holding the word list. This game has turns within a game, unlike, say, rock, paper, scissors, so the program must manage the game state internally as well as display it on the screen.

Critical requirements

As was true in the previous chapter, the implementation of this game makes use of many HTML5 and JavaScript constructs demonstrated in earlier chapters, but they are put together here in different ways. Programming is similar to writing. In programming, you put together various constructs, just like you write sentences composed of words that you know, and then put these into paragraphs, and so on. While reading this chapter, think back to what you have learned about drawing lines, arcs, and text on the canvas; creating new HTML markup; setting up a mouse click event for markup on the screen; and using if and for statements.

To implement Hangman, we need access to a list of words. Creating and testing the program does not require a long list, which could be substituted later. I decided to make it a requirement that the word list be separate from the program.

The user interface for player moves could have manifested in one of several ways, for example, an input field in a form. However, I decided a better approach was to make the interface include graphics representing the letters of the alphabet. It was necessary to make each of the graphics act as a clickable button and provide a way to make each letter disappear after it has been selected.

The pencil-and-paper version of the game involves a progression of drawings ultimately resulting in a stick figure with a noose around its neck. The computer game must show the same progression of drawings. The drawings can be simple lines and ovals.

The secret word must be represented on the screen, initially as all blanks and then filled in with any correctly identified letters. I chose to use double lines as blanks, because I wanted identified letters to be underlined. An alternative could be question marks.

Last, the program must monitor the progress of the game and correctly determine when the player has lost and when the player has won. The game state is visible to the player, but the program must set up and check internal variables to make the determination that the game is won or lost.

HTML5, CSS, JavaScript features

Let's now look at the specific features of HTML5, CSS, and JavaScript that provide what we need to implement Hangman. Except for basic HTML tags and the workings of functions and variables, the explanations here are complete. However, much of this chapter repeats explanations given in earlier chapters. As before, you may choose to look at all the code in the "Building the Application" section first and return to this section if you need explanations of specific features.

Storing a word list as an array defined in an external script file

The Hangman game requires access to a list of legal words, which can be called the word bank. It would be a pretty sure bet to say that one approach is to use an array. The short array we'll use for this initial example follows:

var words = [
    "muon", "blight","kerfuffle","qat"
     ];

Notice that the words are all different lengths. This means that we can use the random processing code that we will want for the final version and still know what word has been selected when we're testing. We'll make sure the code uses words.length so that when you substitute a bigger array, the coding still works.

Now, the question is how to use different arrays for this purpose if we want to bring in a different list of words. It certainly is possible to change the HTML document. However, in HTML5 (or previous versions of HTML), it is possible to include a reference to an external script file in place of or in addition to a script element in the HTML document. We can take the three lines that declare and define the variable words and place them in a file named words1.js. We can include this file with the rest of the document using the following line of code:

<script src="words1.js" defer></script>

The defer method will cause this file to be loaded while the browser is continuing with the rest of the base HTML document. We could not load these two files simultaneously if the external file contained part of the body, but it works in this situation.

A more elaborate program could include multiple files with code for the player to select from among different levels or languages.

Generating and positioning HTML markup, then making the markup be buttons, and then disabling the buttons

The creation of the alphabet buttons and the secret word dashes is done with a combination of JavaScript and CSS.

We'll write code to create HTML markup for two parts of the program: the alphabet icons and the blanks for the secret word. (You can go to the quiz game in Chapter 6 for more on creating HTML markup.) In each case, HTML markup is created using the following built-in methods:

  • document.createElement(x): Creates HTML markup for the new element type x

  • document.body.appendChild (d): Adds the d element as another child element of the body element

  • document.getElementById(id): Extracts the element with id the value of id

The HTML is created to include a unique id for each element. The code involves setting certain properties:

  • d.innerHTML is set to hold the HTML

  • thingelem.style.top is set to hold the vertical position

  • thingelem.style.left is set to hold the horizontal position

With this background, here is the coding for setting up the alphabet buttons. We first declare a global variable alphabet:

var alphabet = "abcdefghijklmnopqrstuvwxyz";

The setupgame function has this code for making the alphabet buttons:

var i;
   var x;
   var y;
   var uniqueid;
   var an = alphabet.length;
   for(i=0;i<an;i++) {

      uniqueid = "a"+String(i);
      d = document.createElement('alphabet'),
       d.innerHTML = (
         "<div class='letters' id='"+uniqueid+"'>"+alphabet[i]+"</div>");
      document.body.appendChild(d);
      thingelem = document.getElementById(uniqueid);
      x = alphabetx + alphabetwidth*i;
      y = alphabety;
      thingelem.style.top = String(y)+"px";
      thingelem.style.left = String(x)+"px";
      thingelem.addEventListener('click',pickelement,false);
   }

The variable i is used for iterating over the alphabet string. The unique id is a concatenated with the index value, which will go from 0 to 25. The HTML inserted into the created element is a div with text containing the letter. The string is surrounded by double quotation marks, and the attributes inside this string are surrounded by single quotation marks. The elements are spaced across the screen, starting at the position alphabetx, alphabety (each global variable is declared earlier in the document), and incremented horizontally by alphabetwidth. The top and left attributes need to be set to strings and end with "px", for pixels. The last step is to set up event handling so these elements act as buttons.

The creation of the elements for the secret word is similar. A difference is that each of these elements has two underscores as its text content. On the screen, these two underscores look like one long underscore. The assignment to ch (for choice) is how our program selects the secret word.

var ch = Math.floor(Math.random()* words.length);
   secret = words[ch];
   for (i=0;i<secret.length;i++) {
      uniqueid = "s"+String(i);
      d = document.createElement('secret'),
          d.innerHTML = (
            "<div class='blanks' id='"+uniqueid+"'> __ </div>");
      document.body.appendChild(d);
      thingelem = document.getElementById(uniqueid);
      x = secretx + secretwidth*i;
      y = secrety;
      thingelem.style.top = String(y)+"px";
      thingelem.style.left = String(x)+"px";
   }

At this point, you may be asking, how did the alphabet icons get to be letters inside blocks with borders? The answer is that I used CSS. The usefulness of CSS goes far beyond fonts and colors. The styles provide the look and feel of critical parts of the game. Notice that the alphabet div elements have a class setting of 'letters', and the secret word letter div elements have a setting of 'blanks'. The style section contains the following two styles:

<style>
.letters {position:absolute;left: 0px; top: 0px; border: 2px; border-style: double;
Generating and positioning HTML markup, then making the markup be buttons, and then disabling the buttons
margin: 5px; padding: 5px; color:#F00; background-color:#0FC; font-family:"Courier
Generating and positioning HTML markup, then making the markup be buttons, and then disabling the buttons
New", Courier, monospace; } .blanks {position:absolute;left: 0px; top: 0px; border:none; margin: 5px; padding:
Generating and positioning HTML markup, then making the markup be buttons, and then disabling the buttons
5px; color:#006; background-color:white; font-family:"Courier New", Courier,
Generating and positioning HTML markup, then making the markup be buttons, and then disabling the buttons
monospace; text-decoration:underline; color: black; font-size:24px; } </style>

The designation of a dot followed by a name means this style applies to all elements of that class. This is in contrast to just a name, such as form in the last chapter, in which a style was applied to all form elements, or to a # followed by a name that refers to the one element in the document with an id of that name. Notice that the style for letters includes a border, a color, and a background color. Specifying a font family is a way to pick your favorite font for the task and then specify backups if that font is not available. This feature of CSS provides a wide latitude to designers. My choices here are "Courier New", with a second choice of Courier, and a third choice of any monospace font available (in a monospace font, all the letters are the same width). I decided to use a monospace font to facilitate making icons that are the same in size and space nicely across the screen. The margin attribute sets to the spacing outside the border, and padding refers to the spacing between the text and the border.

We want the buttons representing letters of the alphabet to disappear after they are clicked. The code in the pickelement function can use the term this to refer to the clicked object. These two statements (which could be squeezed into one) make this happen by setting the display attribute:

var id = this.id;
document.getElementById(id).style.display = "none";

When the game is over, either through a win or a loss, we remove the click event handling for all the letters by iterating over all the elements:

for (j=0;j<alphabet.length;j++) {
    uniqueid = "a"+String(j);
    thingelem = document.getElementById(uniqueid);
    thingelem.removeEventListener('click',pickelement,false);
}

The removeEventListener event does what it sounds like: it removes the event handling.

Creating progressive drawings on a canvas

In the chapters so far, you have read about drawing rectangles, text, images, and also paths. The paths consist of lines and arcs. For Hangman, the drawings are all paths. For this application, code has set the variable ctx to point to the 2D context of the canvas. Drawing a path involves setting a line width by setting ctx.lineWidth to a numerical value and setting ctx.strokeStyle to a color. We will use different line widths and colors for various parts of the drawing.

The next line in the code is ctx.beginPath();, and it's followed by a sequence of operations to draw lines or arcs or move a virtual pen. The method ctx.moveTo moves the pen without drawing and ctx.lineTo specifies the drawing of a line from the current pen position to the point indicated. Please keep in mind that nothing is drawn until the call of the stroke method. The moveTo, lineTo, and arc commands set up the path that is drawn whenever either the stroke or fill methods are invoked. In our draw functions, the next step is calling ctx.stroke();, and the last step is calling ctx.closePath(); to end the path. For example, the gallows is drawn by the following function:

function drawgallows() {
   ctx.lineWidth = 8;
   ctx.strokeStyle = gallowscolor;
   ctx.beginPath();
   ctx.moveTo(2,180);
   ctx.lineTo(40,180);
   ctx.moveTo(20,180);
   ctx.lineTo(20,40);
   ctx.moveTo(2,40);
   ctx.lineTo(80,40);
   ctx.stroke();
   ctx.closePath();
}

The head and the noose require ovals. The ovals will be based on circles, so first I will review how to draw a circle. You also can go back to Chapter 2. Drawing a circular arc is done with the ctx.arc command with the following parameters: coordinates for the center of the circle, a length for the radius, the starting angle in radians, the ending angle, and false for counter-clockwise or true for clockwise. Radians are intrinsic measurements in which a full circle is Math.PI*2. The conversion from degrees to radians is to divide by Math.PI and multiply by 180, but that is not needed for this example because we are drawing complete arcs.

However, we want to draw an oval in place of a circle for the head (and later for part of the noose). The solution is to use ctx.scale to change the coordinate system. In Chapter 4, we changed the coordinate system to rotate the rectangle representing a cannon. Here, we manipulate the coordinate system to squeeze one dimension to make a circle an oval. What our code does is first use ctx.save() to save the current coordinate system. Then for the head, it uses ctx.scale(.6,1); to shorten the x axis to 60 percent of its current value and keep the y axis the same. Use the code for drawing an arc and then use ctx.restore(); to restore the original coordinate system. The function for drawing the head follows:

function drawhead() {
   ctx.lineWidth = 3;
   ctx.strokeStyle = facecolor;
   ctx.save();  //before scaling of circle to be oval
   ctx.scale(.6,1);
   ctx.beginPath();
   ctx.arc (bodycenterx/.6,80,10,0,Math.PI*2,false);
   ctx.stroke();
   ctx.closePath();
   ctx.restore();
}

The drawnoose function makes use of the same technique, except that, for the noose, the oval is wide as opposed to narrow; that is, the vertical is squeezed and not the horizontal.

Each step in the progression of drawings is represented by a function, such as drawhead and drawbody. We list all of these in an array called steps:

var steps = [
      drawgallows,
      drawhead,
      drawbody,
      drawrightarm,
      drawleftarm,
      drawrightleg,
      drawleftleg,
      drawnoose
      ];

A variable, cur, keeps track of the current step, and when the code confirms the condition that cur is equal to the length of steps, the game is over.

After experimenting with these, I decided that I needed to draw the head and draw a neck on top of the noose. This is done by putting in calls to drawhead and drawneck in the drawnoose function. The order is important.

Use the draw functions as models for you to make your own drawings. Do change each of these individual functions. You also can add or take away functions. This means you would be changing the number of steps in the progression, that is, the number of wrong guesses the player can make before losing the game.

Tip

If you haven't done so already (or even if you have), experiment with drawing. Create a separate file just for drawing the steps of the hanging. Experiment with lines and arcs. You also can include images.

Maintaining the game state and determining a win or loss

The requirement to encode and maintain the state of an application is a common one in programming. In Chapter 2, our program kept track of whether the next move was a first throw or a follow-up throw of the dice. The state of the Hangman game includes the identity of the hidden word, what letters in the word have been correctly guessed, what letters of the alphabet have been tried, and the state of the progression of the hanging.

The pickelement function, invoked when the player clicks on an alphabet block, is where the critical action takes place, and it performs the following tasks:

  • Check if the player's guess, kept in the variable picked, matches any of the letters in the secret word held in the variable secret. For each match, the corresponding letter in the blank elements is revealed by setting textContent to that letter.

  • Keep track of how many letters have been guessed using the variable lettersguessed.

  • Check if the game has been won by comparing lettersguessed to secret.length. If the game is won, remove event handling for the alphabet buttons and display the appropriate messages.

  • If the selected letter did not match any letters in the secret word (if the variable not is still true), advance the hanging using the variable cur for an index into the array variable steps

  • Check if the game has been lost by comparing cur to steps.length. If the two values are equal, reveal all the letters, remove event handling, and display the appropriate messages.

  • Whether or not there is a match, make the clicked alphabet button disappear by setting the display attribute to none.

These tasks are performed using if and for statements. The check if the game has been won is done after determining that a letter has been guessed correctly. Similarly, the check if the game has been lost is done only when it is determined that a letter has not been correctly identified and the hanging has advanced. The state of the game is represented in the code by the secret, lettersguessed, and cur variables. The player sees the underscores and filled-in letters of the secret word and the remaining alphabet blocks.

The code for the whole HTML document with line-by-line comments is in the "Building the Application" section. The next section describes the critical first task of handling a player's guess. One general tactic to keep in mind is that several tasks are accomplished by doing something for every member of an array even if it may not be necessary for certain elements of the array. For example, when the task is to reveal all the letters in the secret word, all have the textContent changed even if some of them have already been revealed. Similarly, the variable not may be set to false multiple times.

Checking a guess and revealing letters in the secret word by setting textContent

The player makes a move by clicking a letter. The pickelement function is set up as the event handler for each letter icon. Therefore, within the function, we can use the term this to refer to the object that received (listened for and heard) the click event. Consequently, the expression this.textContent will hold the selected letter. Therefore, the statement

var picked = this.textContent;

assigns to the local variable picked the specific letter of the alphabet the player is guessing. The code then iterates over all the letters in the secret word held in the variable secret and compares each letter to the guess of the player. The created markup that starts out being the double underlines corresponds to the letters in the secret word, so when there is a correct guess, the corresponding element will be changed; that is, its textContent will be set to the letter guessed by the player, which is held in picked:

for (i=0;i<secret.length;i++) {
      if (picked==secret[i]) {
         id = "s"+String(i);
         document.getElementById(id).textContent = picked;
                    not = false;
                    lettersguessed++;
                     ...

The iteration does not stop when a guess is correct; it keeps going. This means that all instances of any one letter will be discovered and revealed. The variable not is set to false each time there is a match. If there were two or more instances of the same letter, this variable is set more than once, which is not a problem. I included the word kerfuffle to make sure that repeated letters were handled correctly (besides the fact that I like the word). You can examine all the code in the next section.

Building the application and making it your own

The Hangman application makes use of CSS styles, HTML markup created by JavaScript, and JavaScript coding. There are two initializing and set up functions (init and setupgame) and the function that does most of the work (pickelement), plus eight functions that draw steps in the hanging. The functions are described in Table 9-1.

Table 9.1. Functions Invoked or Called by Calls

Function

Invoked / Called By

Calls

init

Invoked by the action of onLoad in the <body> tag

setupgame

setupgame

init

The first of the drawing functions, namely drawgallows

pickelement

Invoked by the action of the addEventListener calls in setupgame

One of the drawing functions through call of steps[cur]()

drawgallows

Call of steps[cur]() in pickelement

 

drawhead

Call of steps[cur]() in pickelement, drawnoose

 

drawbody

Call of steps[cur]() in pickelement

 

drawrightarm

Call of steps[cur]() in pickelement

 

drawleftarm

Call of steps[cur]() in pickelement

 

drawrightleg

Call of steps[cur]() in pickelement

 

drawleftleg

Call of steps[cur]() in pickelement

 

drawnoose

Call of steps[cur]() in pickelement

drawhead, drawnoose

drawneck

drawnoose

 

Note the indirect pattern of most of the function calls. This pattern provides considerable flexibility if you decide to change the hanging progression. Note also that you can remove the very first call in the setupgame function if you want the player to start with a blank page and not with the representation of the wooden beams of the gallows.

The complete implementation of Hangman is shown in Table 9-2.

Table 9.2. The Complete Implementation of Hangman

Code

Explanation

<html>

Opening tag

<head>

Opening tag

    <title>Hangman</title>

Completes the title element

<style>

Opens the style element

.letters {position:absolute;left:   0px;
The Complete Implementation of Hangman
top: 0px; border: 2px; border-style: double;
The Complete Implementation of Hangman
margin: 5px; padding: 5px; color:#F00;
The Complete Implementation of Hangman
background-color:#0FC; font-family:
The Complete Implementation of Hangman
"Courier New", Courier, monospace;

Specifies styling for any element with designated class letters, including the border, colors, and font

}

Closing style directive

.blanks {position:absolute;left: 0px;
The Complete Implementation of Hangman
top: 0px; border:none; margin: 5px;
The Complete Implementation of Hangman
padding: 5px; color:#006; background-color:
The Complete Implementation of Hangman
white; font-family:"Courier New", Courier,
The Complete Implementation of Hangman
monospace; text-decoration:underline; color: black;

Specifies styling for any element with designated class blanks, including the border, spacing, color, and font, and puts in underlines

}

Closing style directive

</style>

Closes the style element

<script src="words1.js" defer></script>

Element calling for inclusion of external file, with directive to load the file at same time as the rest of this document

    <script >

Opening tag for the script element

  var ctx;

Variable used for all drawing

   var thingelem;

Variable used for created elements

   var alphabet = "abcdefghijklmnopqrstuvwxyz";

Defines letters of the alphabet, used for alphabet buttons

    var alphabety = 300;

Vertical position for all alphabet buttons

   var alphabetx = 20;

Starting alphabet horizontal position

   var alphabetwidth = 25;

Width allocated for the alphabet elements

   var secret;

Will hold the secret word

   var lettersguessed = 0;

Keeps count of letters guessed

   var secretx = 160;

Horizontal starting position for secret word

   var secrety = 50;

Vertical position for secret word

   var secretwidth = 50;

Width allocated for each letter in display of secret word

   var gallowscolor = "brown";

Color for the gallows

   var facecolor = "tan";

Color for the face

   var bodycolor = "tan";

Color for the body

   var noosecolor = "#F60";

Color for the noose

   var bodycenterx = 70;

Horizontal position for the body

   var steps = [

Holds the functions constituting the sequence of drawings for the progression toward the hanging

      drawgallows,

Draws the gallows

      drawhead,

Draws the head

      drawbody,

Draws the body

      drawrightarm,

Draws the right arm

      drawleftarm,

Draws the left arm

      drawrightleg,

Draws the right leg

      drawleftleg,

Draws the left leg

      drawnoose

Draws the noose

      ];

Ends the array steps

   var cur = 0;

Points to the next drawing in steps

function drawgallows() {

Header for the function drawing the gallows

   ctx.lineWidth = 8;

Sets the line width

   ctx.strokeStyle = gallowscolor;

Sets the color

   ctx.beginPath();

Begins the drawing path

   ctx.moveTo(2,180);

Moves to the first position

   ctx.lineTo(40,180);

Draws a line

   ctx.moveTo(20,180);

Moves to the next position

   ctx.lineTo(20,40);

Draws a line

   ctx.moveTo(2,40);

Moves to the next position

   ctx.lineTo(80,40);

Draws the line

   ctx.stroke();

Actually draws the whole path

   ctx.closePath();

Closes the path

}

Closes the function

function drawhead() {

Header for the function drawing the head of the victim

   ctx.lineWidth = 3;

Sets the line width

   ctx.strokeStyle = facecolor;

Sets the color

   ctx.save();

Saves the current stage of the coordinate system

   ctx.scale(.6,1);

Applies scaling, namely squeezes the x axis

   ctx.beginPath();

Start a path

   ctx.arc (bodycenterx/.6,80,10,0,
The Complete Implementation of Hangman
Math.PI*2,false);

Draws an arc. Note that the x coordinate is modified to work for the scaled coordinate system. The complete arc will be an oval.

   ctx.stroke();

Actually does the drawing

   ctx.closePath();

Closes the path

   ctx.restore();

Restores (goes back to) the coordinates before the scaling

}

Closes function

function drawbody() {

Header for the function that draws the body, a single line

   ctx.strokeStyle = bodycolor;

Sets the color

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx,90);

Moves to the position (right below head)

   ctx.lineTo(bodycenterx,125);

Draws the line

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

}

Closes the function

function drawrightarm() {

Header for the function that draws the right arm

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx,100);

Moves to the position

   ctx.lineTo(bodycenterx+20,110);

Draws the line

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

}

Closes the function

function drawleftarm() {

Header for the function that draws the left arm

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx,100);

Moves to the position

   ctx.lineTo(bodycenterx-20,110);

Draws the line

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

}

Closes the function

function drawrightleg() {

Header for the function that draws the right leg

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx,125);

Moves to the position

   ctx.lineTo(bodycenterx+10,155);

Draws the line

    ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

}

Closes the function

function drawleftleg() {

Header for the function that draws the left leg

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx,125);

Moves to the position

   ctx.lineTo(bodycenterx-10,155);

Draws the line

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

}

Closes the function

function drawnoose() {

Header for the function that draws noose

   ctx.strokeStyle = noosecolor;

Sets the color

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx-10,40);

Moves to the position

   ctx.lineTo(bodycenterx-5,95);

Draws the line

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

   ctx.save();

Saves the coordinate system

   ctx.scale(1,.3);

Does the scaling, which, squeezes the image vertically (on the y axis)

   ctx.beginPath();

Starts a path

   ctx.arc(bodycenterx,95/.3,8,0,Math.
The Complete Implementation of Hangman
PI*2,false);

Draws a circle (which will become an oval)

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

   ctx.restore();

Restores the saved coordinate system

   drawneck();

Draws the neck on top of the noose

   drawhead();

Draws the head on top of the noose

}

Closes the function

function drawneck() {

Header for the function for drawing the neck

   ctx.strokeStyle=bodycolor;

Sets the color

   ctx.beginPath();

Starts the path

   ctx.moveTo(bodycenterx,90);

Moves to the position

   ctx.lineTo(bodycenterx,95);

Draws the line

   ctx.stroke();

Actually draws the path

   ctx.closePath();

Closes the path

}

Closes the function

function init(){

Header for the function called on document load

    ctx = document.getElementById
The Complete Implementation of Hangman
('canvas').getContext('2d'),

Sets up the variable for all drawing on canvas

   setupgame();

Invokes the function that sets up the game

   ctx.font="bold 20pt Ariel";

Sets the font

}

Closes the function

function setupgame() {

Header for the function that sets up the alphabet buttons and the secret word

   var i;

Creates the variable for iterations

   var x;

Creates the variable for position

   var y;

Creates the variable for position

   var uniqueid;

Creates the variable for each of each set of created HTML elements

   var an = alphabet.length;

Will be 26

   for(i=0;i<an;i++) {

Iterates to create alphabet buttons

      uniqueid = "a"+String(i);

Creates a unique identifier.

      d = document.createElement('alphabet'),

Creates an element of type alphabet

       d.innerHTML = (

Defines the contents as specified in the next line

         "<div class='letters'
The Complete Implementation of Hangman
id='"+uniqueid+"'>"+alphabet[i]+"</div>");

Specifies a div of class letters with a unique identifier and text content, which is the ith letter of the alphabet

      document.body.appendChild(d);

Adds to body

      thingelem = document.getElementById
The Complete Implementation of Hangman
(uniqueid);

Gets the element with the id

      x = alphabetx + alphabetwidth*i;

Computes its horizontal position

      y = alphabety;

Sets the vertical position

      thingelem.style.top = String(y)+"px";

Using the style top, sets the vertical position

      thingelem.style.left = String(x)+"px";

Using the style left, sets the horizontal position

        thingelem.addEventListener('click',
The Complete Implementation of Hangman
pickelement,false);

Sets up event handling for the mouse click event

   }

Closes the for loop

   var ch = Math.floor(Math.random()*
The Complete Implementation of Hangman
words.length);

Chooses, at random, an index for one of the words

   secret = words[ch];

Set the global variable secret to be this word

   for (i=0;i<secret.length;i++) {

Iterates for the length of the secret word

      uniqueid = "s"+String(i);

Creates a unique identifier for the word

      d = document.createElement('secret'),

Creates an element for the word

d.innerHTML = "<div class='blanks' id='"
The Complete Implementation of Hangman
+uniqueid+"'> __ </div>");

Sets the contents to be a div of class blanks, with the id of the word the uniqueid just created. The text content will be an underscore.

   document.body.appendChild(d);

Appends the created element as a child of the body

      thingelem = document.getElementById
The Complete Implementation of Hangman
(uniqueid);

Gets the created element

      x = secretx + secretwidth*i;

Calculates the element's horizontal position

      y = secrety;

Sets its vertical position

      thingelem.style.top = String(y)+"px";

Using the style top, sets the vertical position

      thingelem.style.left = String(x)+"px";

Using the style left, sets the horizontal position

   }

Closes the for loop

   steps[cur]();

Draws the first function in the steps list, the gallows

   cur++;

Increments cur

   return false;

Returns false to prevent any refreshing of the HTML page

}

Closes the function

 function pickelement(ev) {

Header for the function invoked as a result of a click

    var not = true;

Sets not to true, which may or may not be changed

    var picked = this.textContent;

Extracts the text content, namely the letter, from the object this references

   var i;

Iterates

   var j;

Iterates

   var uniqueid;

Used to create unique identifiers for elements

   var thingelem;

Holds the element

   var out;

Displays a message

   for (i=0;i<secret.length;i++) {

Iterates over the letters in the secret word

      if (picked==secret[i]) {

Says, "If the player guessed letter is equal to this letter in secret..."

         id =   "s"+String(i);

Constructs the identifier for this letter

           document.getElementById(id).
The Complete Implementation of Hangman
textContent = picked;

Changes the text content to be the letter

         not = false;

Sets not to false

       lettersguessed++;

Increment the number of letters identified correctly

         if (lettersguessed==secret.length) {

Says, "If the whole secret word has been guessed..."

             ctx.fillStyle=gallowscolor;

Sets the color, which uses the brown of the gallows but could be anything

         out = "You won!";

Sets the message

         ctx.fillText(out,200,80);

Displays the message

      ctx.fillText("Re-load   the page to
The Complete Implementation of Hangman
try again.",200,120);

Displays another message

        for (j=0;j<alphabet.length;j++) {

Iterates over the whole alphabet

          uniqueid = "a"+String(j);

Constructs the identifier

       thingelem = document.getElementById
The Complete Implementation of Hangman
(uniqueid);

Gets the element

          thingelem.removeEventListener('click',
The Complete Implementation of Hangman
pickelement,false);

Removes the event handling

           }

Closes the j for loop iteration

        }

Closes if (lettersguessed....), that is, the all-done test

      }

Closes the if (picked==secret[i]) true clause

      }

Closes the for loop over letters in the secret word iteration

   if (not) {

Checks if no letters were identified

      steps[cur]();

Proceeds with the next step of the hanging iteration

      cur++;

Increments the counter

      if (cur>=steps.length) {

Checks to see if all steps are finished

      for (i=0;i<secret.length;i++) {

Starts a new iteration over the letters in the secret word to reveal all the letters

         id = "s"+String(i);

Constructs the identifier

      document.getElementById(id).textContent
The Complete Implementation of Hangman
= secret[i];

Obtains a reference to the element and sets it to that letter in the secret word

      }

Close the iteration

      ctx.fillStyle=gallowscolor;

Set the color

      out = "You lost!";

Sets the message

      ctx.fillText(out,200,80);

Displays the message

                    ctx.fillText("Re-load the
The Complete Implementation of Hangman
page to try again.",200,120);

Displays the reload message

      for (j=0;j<alphabet.length;j++) {

Iterates over all of the letters in the alphabet

        uniqueid =   "a"+String(j);

Constructs the unique identifier

       thingelem =   document.getElementById
The Complete Implementation of Hangman
(uniqueid);

Gets the element

            thingelem.removeEventListener('click',
The Complete Implementation of Hangman
pickelement,false);

Removes the event handling for this element

                }

Closes the j iteration

        }

Closes the cur test to determine if the hanging is complete

   }

Closes the if (not) test (bad guess by player)

   var id = this.id;

Extracts the identifier for this element

   document.getElementById(id).style.display
The Complete Implementation of Hangman
= "none";

Makes this particular alphabet button disappear

 }

Closes the function

</script>

Closes the script

</head>

Closes the head

<body onLoad="init();">

Opening tag that sets up call to init

<h1>Hangman</h1>

Puts the name of game in big letters

<p>

Opening tag for paragraph

<canvas id="canvas" width="600" height="400">

Opening tag for canvas element. Includes dimensions.

Your browser doesn't support the HTML5
The Complete Implementation of Hangman
element canvas.

Message for people using browsers that don't recognize canvas

</canvas>

Closing tag for canvas

</body>

Closes the body

</html>

Closes the document

A variation of Hangman uses common sayings in place of words. Building on this game to create that one is a challenge for you. The critical steps are handling of blanks between the words and the punctuation. You probably want to reveal each instance of blanks between words and periods, commas, and question marks immediately, making these things hints to the player. This means that you need to make sure that lettersguessed starts off with the correct count. Do not be concerned that the selected letters are compared to blanks or punctuation.

Another variation would be to change the alphabet. I carefully replaced all the instances of 26 with alphabet.length. You would also need to change the language for the messages for winning and losing.

A suitable enhancement of the game is to make a New Word button. To do so, you need to split up the workings of the setupgame button into two functions: One function creates new alphabet icons and the positions for the longest possible secret word. The other makes sure all the alphabet icons are visible and set up for event handling and then selects and sets up the blanks for secret word, making sure the appropriate number are visible. If you do this, you may want to include display of a score and a number of games.

Continuing with the educational idea and assuming you use unusual words, you may want to include definitions. The definition can be revealed at the end, by writing text on the canvas. Or you can make a button to click to reveal the definition as a hint to the player. Alternatively, you could create a link to a site such as Dictionary.com.

Testing and uploading the application

To test this application, you can download my word list or create your own. If you create your own, start off with a short word list prepared as plain text, giving it the name words1.js. When testing, do not always guess in the same pattern, such as choosing the vowels in order. Misbehave and try to keep guessing after the game is over. When you are satisfied with the coding, create a longer word list, and save it under the name words1.js. Both the HTML and words1.js files need to be uploaded to your server.

Summary

In this chapter, you learned how to implement a familiar game using features of HTML5, JavaScript, and CSS along with general programming techniques, which included the following:

  • using the scale method to change the coordinate system to draw an oval, as opposed to a circle, by saving and restoring before and after

  • creating HTML markup dynamically

  • setting up and removing event handling using addEventListener and removeEventListener for individual elements

  • using styles to remove elements from display

  • using arrays of function names to set up a progression of drawings

  • manipulating variables to maintain the state of the game, with calculations to determine if there is a win or a loss

  • creating an external script file to hold the word list for increased flexibility

  • using CSS, including font-family for the selection of fonts, color, and display

The next and final chapter of this book will describe the implementation of the card game, blackjack, also called 21. It will build on what you have learned already and describe some new techniques in programming, elements added to HTML5, and more CSS.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.113.208