© Jeanine Meyer 2018
Jeanine MeyerThe Essential Guide to HTML5https://doi.org/10.1007/978-1-4842-4155-4_10

10. Blackjack

Jeanine Meyer1 
(1)
Purchase, NY, USA
 
In this chapter, we cover
  • The footer and header tags, which are new to HTML5

  • Capturing key presses

  • Programmer-defined objects

  • Generating Image elements using a set of external image files

  • Shuffling a deck of cards

Introduction

The objective of this chapter is to combine programming techniques and HTML5 and JavaScript features to implement the card game blackjack, also called 21. The implementation will use new tags introduced in HTML5, namely footer and header . We will use the footer to give credit to the source for the card images and the website we are using for the shuffling algorithm. The cards are created using programmer-defined objects and Image objects , with coding to generate the names of the image files. The player makes moves using key presses.

The rules of blackjack are as follows :

The player plays against the dealer (also known as the House). The player and dealer are each dealt two cards. The first card of the dealer is hidden from the player, but the other is visible. The value of a card is its face value for the numbered cards, 10 for a jack, queen, or king, and either 1 or 11 for an ace. The value of a hand is the sum of the cards. The object of the game is to have a hand with a value as close to 21 as possible without going over and to have a value greater than the other person. Thus an ace and a face card count as 21, a winning hand. The actions the player can take are to request another card or to hold .

Since this is a two-person game, our player will play against “the computer,” and, as was the case with rock-paper-scissors, we have the task of generating the computer moves. However, we are guided by the practice of casinos—the dealer (House) will use a fixed strategy. Our dealer will request another card if the value of the hand is under 17 (the game strategy in casinos may be slightly more complicated and may be dependent on the presence of aces). Similarly, our game does declare a tie if the player and House have the same total if the total is under 21; some casinos may have a different practice.

An opening screenshot is shown in Figure 10-1.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig1_HTML.jpg
Figure 10-1

Opening screen for blackjack

After the user presses the n key, the next screen would look something like Figure 10-2. Remember that there are random processes involved, so this same set of cards is not guaranteed to appear each time.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig2_HTML.jpg
Figure 10-2

Cards dealt

Figure 10-2 shows what the player sees: all of his or her own hand and all but one card of the dealer's hard. The virtual dealer does not have knowledge of the player's hand. In this situation, the player's hand has a value of 2 plus 10 for a total of 12. The dealer is showing a 3. The player asks for another card by pressing d. Figure 10-3 shows the result.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig3_HTML.jpg
Figure 10-3

Player with 20

The player now has a hand with value 20 and clicks h for hold to stop play and to see what the dealer has. The result is shown in Figure 10-4.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig4_HTML.jpg
Figure 10-4

Player wins with 20 and the House going over.

The player wins since the House went over and the player did not.

The player can start a new game by pressing the n key or reloading the document. Reloading the document would mean starting with a complete, freshly shuffled deck. Pressing the n key continues with the current deck. Anyone who wants to practice card counting , a way of keeping track of what still is in the deck and varying your play accordingly, should opt to press the n key.

Figure 10-5 shows a new game.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig5_HTML.jpg
Figure 10-5

A new game

This time, the player presses h for hold, and Figure 10-6 shows the result.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig6_HTML.jpg
Figure 10-6

Player loses

The dealer was holding four cards for a total of 21. Remember that ace counts as 1 or 11. The player had 14 and, consequently, lost.

Figure 10-7 shows the results of another game. The initial deal to the player was two face cards for a total of 20. The player pressed h for hold and the House played two more cards and went over.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig7_HTML.jpg
Figure 10-7

The player wins

The actual practices of dealers at casinos may be different from this. This is an opportunity for research! The player also can bluff the House by going over and not revealing it. This may lead the House to request another card and go over also. The game is decided if and only if the player clicks the h key to hold, and thus stops drawing cards.

You may want to provide feedback to the player when a key that is not d, h, or n is pressed, as shown in Figure 10-8.
../images/214814_2_En_10_Chapter/214814_2_En_10_Fig8_HTML.jpg
Figure 10-8

Feedback when a wrong key is pressed

Critical Requirements

The blackjack game will use many of the HTML5, CSS, and JavaScript features described for the previous games.

The first issue I had when starting the implementation was to find a source of images for the card faces. I knew I could make my own drawings, but I preferred something more polished than I could produce.

The next challenge was how to design what a card was in programming terms so that I could implement dealing cards, showing the back or the face. I also wanted to investigate how to shuffle the deck.

Another challenge was implementing the way a player would play the game. I chose to use key presses: d to deal, h to hold, and n to begin a new game. There are, of course, alternatives, for example, displaying buttons with words or graphics or using other keys, such as the arrow keys. The absence of a clear, intuitive interface made it necessary to display the directions on the screen.

The last challenges are the general ones of maintaining the state of the game, the visible display, and internal information; generating the computer moves, and following the rules.

HTML5, CSS, and JavaScript Features

Let’s now look at the specific features of HTML5, CSS, and JavaScript that provide what we need to implement the blackjack card game. Except for basic HTML tags and functions and variables, the explanations here are complete. If you have read the other chapters, you will notice that much of this chapter repeats explanations given previously. Remember that you can skip ahead to the “Building the Application” section to see the complete code for the game with comments and then return to this section for more explanation.

Source for Images for Card Faces and Setting Up the Image Objects

When working on the first edition, I did find an excellent source for the card faces, which came with a Creative Commons license and was happy to show the link and the license, but the site no longer exists. I found another source, at the American Contract Bridge League. The digital files were labeled as free but I still did ask for and received permission and you can see from the screenshots that I indicated the source of the digital files on the web page.

After copying the files to your computer, you need a way to access the 52 card face image files without writing 52 different statements. (Note that the card back image file is accessed in a different place, namely the init function.) This can be accomplished because the file names follow a pattern. The pattern for the new card images was slightly different than the original one, and the coding was actually easier. The builddeck function is the following:
function builddeck() {
 var n;
            var si;
            var suitnames =["C","H","S","D"];
            var i;
            i=0;
            var picname;
            var nums=["A","2","3","4","5","6","7","8","9","10","J","Q","K"];
            for (si=0;si<4;si++) {
                for (n=0;n<13;n++) {
                    picname=nums[n]+suitnames[si]+".png";
                    deck[i]=new MCard(n+1,suitnames[si],picname);
                    i++;
                }
            }
   }
 }
}

Notice the nested for loops . The outer loop handles the suits and the inner loops the 13 cards in a suit.

In this function, the outer loop manages the suits and the inner loop the cards within each suit. The picname variable will be set to the names of the files that we downloaded from the source. The MCard function is the constructor function to create a MCard object, that is, objects of the class we defined as a programmer-defined class of objects. n+1 will be used as the value of the card, and there will be some adjustment for the face cards.

Note

The three statements in the nested for loops could be combined into deck[i++]=new MCard(n+1,suitnames[si], suitnames[si]+"-"+nums[n]+".png");.

This is because the ++ iteration operator takes place after the value has been generated for indexing the deck array. However, I recommend that in this learning example you don't do it! Using three statements is much easier to write and to understand.

Creating the Programmer-Defined Object for the Cards

As we have seen in previous chapters, for example, Chapter 4 for the slingshot game, JavaScript provides a way for programmers to create programmer-defined objects to group together data; the different pieces of data called attributes or properties , and we use dot notation to get at the different attributes. It is also possible to associate code with the data by defining methods, but we don’t need to do that in this example. As a reminder, the function setting up the new object is called the constructor function. For cards, I defined MCard as the constructor, which was shown in use in the previous section in the builddeck function . The definition of this function follows:
function MCard(n, s, picname){
   this.num = n;
   if (n>10) n = 10;
   this.value = n;
   this.suit = s;
   this.picture = new Image();
   this.picture.src = picname;
   this.dealt = 0;
 }
The line of the function
   if (n>10) n = 10;

will be triggered by the face cards (jack, queen, and king); remember, the value of each is 10. This line corrects the value to be 10 in these cases.

Notice that this if statement is structurally different from previous if statements. There are not any opening and closing curly brackets setting off the if-true clause. The single-statement clause is a legitimate form of the if statement. I generally avoid this form because if I later decide to add another statement, I will need to insert the curly brackets. However, I decided that it was okay in this situation and you will see both variations when examining code. Notice that nothing special is done when n equals 1. The rule for two possible values for an ace is handled elsewhere in the program.

The properties of MCard objects include a newly created Image object with its src attribute set to the picname passed in. The last attribute, dealt, initialized to 0, will be set to 1 or 2 depending on whether the card goes to the player or the dealer.

Starting a Game

For my implementation of the game, the player chooses to start a new game with the current deck by pressing the n key. If the player wants to start with a new deck, the player reloads the HTML document. In fact, in the casinos, the dealer, not the player, decides when to use a new deck. Making this change would be a good addition to the implementation. I should also note that some casinos use multiple decks to discourage a practice called card counting. It occurs to me that an application could be built providing players a way to practice card counting.

Another issue concerns player behavior. As I have revealed, I tend to assume that players will behave properly. What should be done if a player clicks on d for deal one more card or h for hold when a game has not been started? In situations like this involving player non-standard behavior, the choices we as application builders face include: displaying a message; trying to guess what the player wants to do, for example, start a new game; or do nothing. I decided to display a message. To keep track of whether or not a game has been started, I use a global variable, gamestart, which is initialized to false. By the way, a term for such variables is flag. It is present in four functions (deal, dealfromdeck, playerdone, and newgame) and you can examine them in context in the code tables.

Dealing the Cards

The builddeck function constructs the deck array of MCard objects. The player’s hand is kept in an array called playerhand with pi holding the index of the next position. Similarly, the dealer’s hand is kept in an array called househand with hi holding the index of the next position. An example showing the syntax (punctuation) for referencing an attribute of an MCard object when the object is an element of an array is playerhand[pi].picture.

The dealstart function has the task of dealing the first four cards: two to the player and two to the dealer. One of the dealer’s cards is not shown; that is, the card’s back is shown. The deal function is invoked when the player requests a new card (see later in this section). The deal function will deal a card to the player and see if the dealer is to get a new card. Both dealstart and deal accomplish the actual dealing by invoking the dealfromdeck function , adding the cards to the playerhand and househand arrays and drawing the cards on the canvas. Formally, the dealfromdeck is a function that returns a value of type MCard. Its call appears on the right side of assignment statements. If the face of the card is to show, the Image object drawn is referenced by the card. If the back of the card is to show, the Image object is held in the variable back.

Here is the dealstart function . Cards are added to the playerhand array and the househand array. Elements can be added to an array two distinct ways. One way is to use the push method. Another way, which is what I demonstrate here, uses an index value where the index value is the current length of the array. That is, this places the value in the next position in the array. Notice the four similar sets of statements: get the card, draw the image, increment the x position for the next time, and increase indexing variable, pi or hi, are used to deal out the four cards, two for the player and two for the House.
function dealstart() {
    playerhand[pi] = dealfromdeck(1);
    ctx.drawImage(playerhand[pi].picture,playerxp,playeryp,cardw,cardh);
    playerxp = playerxp+30;
    pi++;
    househand[hi] = dealfromdeck(2);
    ctx.drawImage(back,housexp,houseyp,cardw,cardh);
    housexp = housexp+20;
    hi++;
    playerhand[pi] = dealfromdeck(1);
    ctx.drawImage(playerhand[pi].picture,playerxp,playeryp,cardw,cardh);
    playerxp = playerxp+30;
    pi++;
    househand[hi] = dealfromdeck(2);
    ctx.drawImage(househand[hi].picture,housexp,houseyp,cardw,cardh);
    housexp = housexp+20;
    hi++;
  }
The deal function is similar. A card is added to the player's hand and to the House if more_to_house returns true.
function deal() {
    if (gamestart)  {
      playerhand[pi] = dealfromdeck(1);
      ctx.drawImage(playerhand[pi].picture,playerxp,playeryp,cardw,cardh);
      playerxp = playerxp+30;
      pi++;
      if (more_to_house()) {
        househand[hi] = dealfromdeck(2);
        ctx.drawImage(househand[hi].picture,housexp,houseyp,cardw,cardh);
        housexp = housexp+20;
        hi++;
      }
 }
   else{
      alert("Press n to start a new game with the same deck.
                 Reload page to start a game with a new deck.");
            }
 }

Note that more_to_house is a function that generates a true or false value. This value will be based on a calculation of the dealer’s total. If the total is 17 or greater, the value returned will be false; otherwise, it will be true. The function call is used as the condition of an if statement , so if more_to_house returns true, the statements within the if clause will be executed. The more_to_house code could be put inside the deal function, but dividing up large tasks into smaller ones is good practice. It means I can keep working on the deal function and postpone temporarily writing the more_to_house function . If you want to refine the more_to_house calculation, you know exactly where to do it.

Determining the specific card from the deck is the task of the dealfromdeck function . Again, I make this well-defined task its own function. The parameter is the recipient of the card. We don’t need to keep track of which recipient in this application, but we’ll keep that information in the code in to prepare for building other card games. What is critical is that the card has been dealt to someone. The dealt attribute changes from 0. Notice the line return card;, which does the work of making an MCard object be the result of invoking the function.
function dealfromdeck(who) {
  var card;
  var ch = 0;
  while ((deck[ch].dealt>0)&&(ch<51)) {
    ch++;
  }
  if (ch>=51) {
    ctx.fillText("NO MORE CARDS IN DECK. Reload. ",200,200);
    ch = 51;
    gamestart = false;
  }
  deck[ch].dealt = who;
  card = deck[ch];
  return card;
}

Keep in mind that the deck array is indexed from 0 to 51. A while statement is another type of looping construction. In most computer programming languages, a while loop is a control flow statement that allows code to be executed repeatedly based on a given Boolean condition; the while loop can be thought of as a repeating if statement . The statements inside the curly brackets will execute as long as the condition inside the parentheses remains true. It is up to the programmer to make sure that this will happen—that the loop won’t go on forever. The while loop in our application stops when a card is identified that has not been dealt, that is, its dealt attribute is 0. This function will say there are no more cards when the last card, the 51st card, is available and dealt. If the player ignores the message and asks for another card again, the last card will be dealt again.

As an aside, the issue of when the dealer chooses to gather the used cards together or go to a new deck is significant for card counters attempting to figure out what cards remain. At many casinos, dealers use multiple decks of cards to impede card counting. My program does not give the House that capability. You can build on this program to simulate these effects if you want a program to practice card counting. You can put the number of decks under player control, use random processing, wait until the count of remaining cards is under a fixed amount, or perhaps something else.

The dealer may request another card when the player requests another card or when the player decides to hold. As mentioned earlier, the function to evaluate if the dealer asks for another card is more_to_house . The calculation is to add up the values of the hand. If there are any aces, the function adds an extra 10 points if that will make the total 21 or less—that is, it makes 1 ace count as 11. Then, it evaluates if the sum is less than 17. If it is, it returns true, which tells the calling function to request a new card. If the value exceeds 17, it returns false.
function more_to_house(){
  var ac = 0;
  var i;
  var sumup = 0;
    for (i=0;i<hi;i++) {
    sumup += househand[i].value;
    if (househand[i].value==1) {ac++;}
  }
  if (ac>0) {
    if ((sumup+10)<=21) {
       sumup += 10;
    }
  }
  housetotal = sumup;
    if (sumup<17) {
   return true;
  }
  else {
    return false;
  }
}

If you want to experiment with a different strategy for the House, more_to_house is the function you change.

Starting a new game can be a challenge for programmers. First of all, it is necessary to understand what starting again means. For this implementation of blackjack, I provide an option to the player for starting a new hand, which means continuing with the same deck. To start with a fresh deck that has no cards dealt out, the player must reload the document. My name for the function that is invoked when the player presses the n key is newgame . The required actions are to clear the canvas and reset the pointers for the player’s and dealer’s hands, as well as the variables holding the horizontal position for the next card. This function closes with a call to dealstart .
function newgame() {
  if (!gamestart) {
     gamestart = true;
     ctx.clearRect(0,0,cwidth,cheight);
     pi=0;
     hi=0;
     playerxp = 100;
     housexp= 500;
     dealstart();
  }
}

Shuffling the Deck

The technique for shuffling featured in the concentration game (see Chapter 5) represented an implementation of what my children and I did when playing the game: we spread out the cards and seized pairs and switched their places. For blackjack, a friend pointed me to a website by Eli Bendersky ( http://eli.thegreenplace.net/2010/05/28/the-intuition-behind-fisher-yates-shuffling/ ) explaining the Fisher-Yates algorithm . The strategy of this algorithm is to make a random determination for each position in the deck, starting from the end and working toward the start. The calculation determines a random position in the deck from 0 up to and including the current position and does a swap. The main shuffle function follows:
function shuffle() {
  var i = deck.length - 1;
  var s;
  while (i>0) {
      s = Math.floor(Math.random()*(i+1));
      swapindeck(s,i);
      i--;
  }
  }
Recall that Math.random() * N returns a number from zero up to but not including N. Taking Math.floor of the result returns an integer from zero up to N. So if we want a number from 0 to i, we need to write Math.floor(Math.random()*(i+1)). To make the shuffle function easier to read, I made a separate function called swapindeck that swaps the two cards that are located at the positions indicated by the parameters to the function. To perform a swap, an extra place is needed and this is the variable hold. This extra place is needed because the two assignment statements cannot be accomplished at the same time.
 function swapindeck(j,k) {
    var hold = new MCard(deck[j].num,deck[j].suit,deck[j].picture.src);
    deck[j] = deck[k];
    deck[k] = hold;
 }

Capturing Key Presses

The use of the arrow keys was described in the maze game in Chapter 7. This essentially is a repeat of that explanation.

Detecting that a key on the keyboard has been pressed and determining which key is termed capturing the key strokes . The code must set up the response to a key event and is analogous to setting up a response to a mouse event. The coding starts with invoking the addEventListener method , this time for the window for this application.
window.addEventListener('keydown',getkey,false);

This means the getkey function will be invoked if and when a key is pressed.

Note

There also are keyup and keypress events . The keydown and keyup fire only once. The keypress event will occur again after some amount of time if the player holds down the key.

Now, as you may expect at this point, the coding to get the information for which key involves code for different browsers. The following code, with two ways to get the number corresponding to the key, works for Chrome, Firefox, and Safari:
if(event == null)
  {
    keyCode = window.event.keyCode;
    window.event.preventDefault();
  }
  else
  {
    keyCode = event.keyCode;
    event.preventDefault();
  }
The preventDefault function does what it sounds like: it prevents any default action, such as special shortcut actions associated with particular keys. The only keys of interest in this application are the three keys d, h, and n. The following switch statement determines which key is pressed and invokes the correct function: deal, playerdone, or newgame. A switch statement compares the value in the parentheses with the values after the term case and starts executing the statements with the first one that matches. The break; statement causes execution to jump out of the switch statement. The default clause is what it sounds like. It is not necessary, but if it is present, the statement or statements following default: are executed if nothing matches the case values provided.
  switch(keyCode)  {
    case 68:  //d
       deal();
       break;
    case 72:  //h
       playerdone();
       break;
    case 78:  //n
       newgame();
       break;
    default:
       alert ("Press d, h, or n.");
  }
Recall that you can determine the key code of any key by modifying the whole switch statement to have just the following line in the default case:
      alert(" You just pressed keycode "+keyCode);

and doing the experiment of pressing the key and writing down what number shows up.

Caution

If, like I sometimes do, you move among different windows on your computer, you may find that when you return to the blackjack game and press a key, the program does not respond. You will need to click the mouse on the window holding the blackjack document. This lets the operating system restore the focus on the blackjack document so the listening for the key press can take place.

Using Header and Footer Element Types

HTML5 added some new built-in element types , including header and footer . The rationale behind these and other new elements (for example, article and nav) was to provide elements that serve standard purposes so that search engines and other programs would know how to treat the material, though it still is necessary to specify the formatting. These are the styles we will use in this example:
footer {
    display:block;
    font-family:Tahoma, Geneva, sans-serif;
    text-align: center;
    font-style:oblique;
}
header {
    width:100%;
    display:block;
}

The display setting can be block or inline. Setting these to block forces a line break. Note that forcing the line break may not be necessary for certain browsers, but using it does not hurt. The font-family attribute is a way to specify choices of fonts. If Tahoma is available on the user's computer, it will be used . The next font to try will be Geneva. If neither one is present, the browser will use the sans-serif font set up as the default. The text-align and font-style settings are what they appear to be. The width setting sets this element to be the whole width of the containing element, in this case the body. Feel free to experiment!

Note that you cannot assume the footer is at the bottom of the screen, nor the header at the top. I made that happen by using positioning in the HTML document .

I used the footer to display the sources for the card images and the shuffle algorithm. Providing credit, showing copyright, and displaying contact information are all typical uses of footer element , but there are no restrictions on how you use any of these new elements or on where you put them in the HTML document and how you format them.

Building the Application and Making It Your Own

The functions used in this game are described in Table 10-1.
Table 10-1

The Blackjack Functions

Function

Invoked/Called by

Calls

init

Invoked by the onLoad function in the <body> tag

builddeck, shuffle, and dealstart

getkey

Invoked by the window.addEventListener call in init

deal, playerdone, and newgame

dealstart

init

dealfromdeck four times

deal

getkey

Two calls to dealfromdeck and one call to more_to_house

more_to_house

deal, playerdone

 

dealfromdeck

deal, dealstart, playerdone

 

builddeck

init

MCard

MCard

builddeck, swapindeck

 

add_up_player

playerdone

 

playerdone

getkey

more_to_house, dealfromdeck showhouse, and add_up_player

newgame

getkey

dealstart

showhouse

playerdone

 

shuffle

init

swapindeck

swapindeck

shuffle

MCard

The functions in this example feature a pattern of procedural calls with only init and getkey invoked as a result of events. Please appreciate the fact that there are many ways to program an application, including the definition of functions. Generally, it is a good practice to split code up into small functions, but it is not necessary. There are many places where similar lines of codes are repeated, so there is opportunity to define more functions. The annotated document follows in Table 10-2.
Table 10-2

The Annotated Code for the Blackjack Game

Code

Explanation

<html>

Opening html tag.

<head>

Opening head tag.

    <title>Black Jack</title>

Complete title element.

 <style>

Opening style tag.

 body {

Specifies the style for the body element.

    background-color:white;

Sets the background color.

    color: black;

Sets the color of the text.

    font-size:18px;

Sets the font size.

    font-family:Verdana, Geneva, sans-serif;

Sets the font family.

}

Closes the style.

footer {

Specifies the style for the footer.

    display:block;

Treats this element as a block.

    font-family:Tahoma, Geneva, sans-serif;

Sets the font family.

    text-align: center;

Aligns the text in the center.

    font-style:oblique;

Makes the text slanted.

}

Closes style.

header {

Specifies the style for the header.

    width:100%;

Makes it take up the whole window.

    display:block;

Treats it as a block.

}

Closes style.

 </style>

Closes the style element.

    <script>

Starts the script element.

var cwidth = 800;

Sets the width of the canvas; used when clearing the canvas.

var cheight = 500;

Sets the height of the canvas; used when clearing the canvas.

var cardw = 75;

Sets the width of each card.

var cardh = 107;

Sets the height of each card.

var playerxp = 100;

Sets the starting horizontal position for the cards in the player's hand.

var playeryp = 300;

Sets the vertical position for the cards in the player's hand.

var housexp = 500;

Sets the starting horizontal position for the cards in the dealer’s hand.

var houseyp = 100;

Sets the vertical position for the cards in the dealer’s hand.

var housetotal;

For the total value of the dealer’s hand.

var playertotal;

For the total value of the player’s hand.

var pi = 0;

Index for the next card in player's hand.

var hi = 0;

Index for the next card in the dealer’s hand.

var deck = [];

Holds all the cards.

var playerhand = [];

Holds the cards for the player.

var househand = [];

Holds the cards for the dealer.

var back = new Image();

Used for the card back.

var ctx;

Used to hold canvas context.

var gamestart = false;

Used to check if game has started.

function init() {

Function called by onLoad in body to performs initialization tasks.

  ctx = document.getElementById('canvas').getContext('2d');

Sets the variable used for all drawing.

  ctx.font="italic 20pt Georgia";

Sets the font.

  ctx.fillStyle = "blue";

Sets the color.

  builddeck();

Invokes the function to build the deck of cards.

  back.src ="cardback.png";

Specifies the image for the back of card (note that only one back appears: the dealer’s hidden card) .

    canvas1 = document.getElementById('canvas');

Sets the variable for event handling.

    window.addEventListener('keydown',getkey,false);

Sets up event handling for keydown presses.

  shuffle();

Invokes the function to shuffle.

  dealstart();

Invokes the function to deal out the first four cards.

 }

Closes the function.

 function getkey(event) {

Function to respond to keydown events.

  var keyCode;

Holds the code designating the key.

  if(event == null)

Browser-specific code to determine if the event is null.

  {

Open clause.

    keyCode = window.event.keyCode;

Gets the key code from window.event.keyCode.

    window.event.preventDefault();

Stops other key responses.

  }

Closes the clause.

  else   {

Clause.

    keyCode = event.keyCode;

Picks up the key code from event.keyCode.

    event.preventDefault();

Stops other key responses.

  }

Closes the clause.

  switch(keyCode)  {

Header for the switch statement based on keyCode.

      case 68:

d key has been pressed.

      deal();

Deals out another card to the player and maybe to the dealer.

      break;

Leaves the switch.

    case 72:

h key has been pressed.

     playerdone();

Invokes the playerdone function.

      break;

Leaves the switch.

    case 78:

n key has been pressed.

     newgame();

Invokes the newgame function.

      break;

Leaves the switch.

    default:

Default choice, which may be appropriate to remove if you don’t feel the need to provide feedback to players if they use an unrecognized key.

    alert("Press d, h, or n.");

Feedback message.

      }

Closes the switch.

 }

Closes the function.

function dealstart() {

Header for the function for initially dealing cards.

    playerhand[pi] = dealfromdeck(1);

Gets the first card for player.

    ctx.drawImage(playerhand[pi].picture,playerxp,playeryp,cardw,cardh);

Draws on the canvas.

    playerxp = playerxp+30;

Adjusts the horizontal pointer.

    pi++;

Increases the count of cards to the player.

    househand[hi] = dealfromdeck(2);

Gets the first card for the dealer.

    ctx.drawImage(back,housexp,houseyp,cardw,cardh);

Draws a card’s back on the canvas.

    housexp = housexp+20;

Adjusts the horizontal pointer.

    hi++;

Increases the count of cards to the dealer.

   playerhand[pi] = dealfromdeck(1);

Deals a second card to the player.

    ctx.drawImage(playerhand[pi].picture,playerxp,playeryp,cardw,cardh);

Draws on canvas.

    playerxp = playerxp+30;

Adjusts the horizontal pointer.

    pi++;

Increases the count of cards to the player.

    househand[hi] = dealfromdeck(2);

Deals a second card to the dealer.

    ctx.drawImage(househand[hi].picture,housexp,houseyp,cardw,cardh);

Draws on the canvas.

    housexp = housexp+20;

Adjusts the horizontal pointer.

    hi++;

Increases the count of cards to the House.

  }

Closes the function.

 function deal() {

Header for the function for dealing through the game.

if (gamestart) {

Checks if game has been started.

    playerhand[pi] = dealfromdeck(1);

Deals a card to the player.

    ctx.drawImage(playerhand[pi].picture,playerxp,playeryp,cardw,cardh);

Draws on the canvas.

    playerxp = playerxp+30;

Adjusts the horizontal pointer.

    pi++;

Increases the count of cards to the player.

    if (more_to_house()) {

if function to say there should be more cards for the dealer.

      househand[hi] = dealfromdeck(2);

Deals a card to the House.

    ctx.drawImage(househand[hi].picture,housexp,houseyp,cardw,cardh);

Draws a card on canvas.

    housexp = housexp+20;

Adjusts the horizontal pointer.

    hi++;

Increases the count of cards to the dealer.

    }

Closes the if true clause.

}

Closes if true clause for if(gamestart).

else{

                alert("Press n to start a new game with the same deck. Reload page to start a game with a new deck.");

Prints out message to player to start a new game or reload to get new deck.

}

Closes else for game NOT started.

}

Closes the function.

function more_to_house(){

Header for the function determining the dealer’s moves.

  var ac = 0;

Variable to hold the count of aces.

  var i;

Variable for iteration

  var sumup = 0;

Initializes the variable for the sum.

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

Iterates over all the cards.

    sumup += househand[i].value;

Adds value of cards in the dealer’s hand.

    if (househand[i].value==1) {ac++;}

Keeps track of the number of aces.

  }

Closes the for loop.

  if (ac>0) {

if statement to determine if there were any aces.

    if ((sumup+10)<=21) {

If so, asks if making one of the aces take on the value of 11 still yield a total less than 21.

       sumup +=10;

If yes, do it.

    }

Closes inner if.

  }

Closes outer if.

  housetotal = sumup;

Sets the global variable to be the sum.

  if (sumup<17) {

Asks if the sum is under 17.

   return true;

Returns true if so, meaning it’s okay to get one more card.

  }

Closes clause.

  else {

Begins else clause.

    return false;

Returns false, meaning the dealer won't get another card.

  }

Closes the else clause .

}

Closes the function.

function dealfromdeck(who) {

Header for the function to deal from the deck.

  var card;

Holds the card.

  var ch = 0;

Holds the index for the next undealt card.

  while ((deck[ch].dealt>0)&&(ch<51)) {

Asks if this card has been dealt.

    ch++;

Increases ch to go on to the next card.

  }

Closes the while loop.

  if (ch>=51) {

Asks if there were no undealt cards.

    ctx.f illText("NO MORE CARDS INDECK. Reload. ",200,250);

Displays a message directly on the canvas.

    ch = 51;

Sets ch to 51 to make this function work.

 gamestart = false;

Prevents response to any player call for new card.

  }

Closes the if true clause.

  deck[ch].dealt = who;

Stores who, a nonzero value, so this card is marked as having been dealt.

  card = deck[ch];

Sets a card.

  return card;

Returns a card.

}

Closes the function.

function builddeck() {

Header for the function that builds the MCard objects.

 var n;

Variable used for inner iteration.

 var si;

Variable used for outer iteration, over the suits.

 var suitnames= ["clubs","hearts","spades","diamonds"];

Names of suits.

 var i;

Keeps track of elements put into the deck array.

 i=0;

Initializes the array to 0.

 var picname;

Simplifies the coding.

 var nums=["a","2","3","4","5","6","7","8","9","10","j","q","k"];

The names for all the cards.

 for (si=0;si<4;si++) {

Iterates over the suits.

   for (n=0;n<13;n++) {

Iterates over the cards in a suit.

     picname=suitnames[si]+"-"+nums[n]+"-75.png";

Constructs the name of the file.

     deck[i]=new MCard(n+1,suitnames[si],picname);

Constructs an MCard with the indicated values.

     i++;

Increments i.

   }

Closes the inner for loop .

 }

Closes the outer for loop.

}

Closes the function.

function MCard(n, s, picname){

Header for the constructor function for making objects.

   this.num = n;

Sets the num value.

   if (n>10) n = 10;

Makes an adjustment in the case of the face cards.

   this.value = n;

Sets the value.

   this.suit = s;

Sets the suit.

   this.picture = new Image();

Creates a new Image object and assigns it as an attribute.

   this.picture.src = picname;

Sets the src attribute of this Image object to the picture file name.

   this.dealt = 0;

Initializes the dealt attribute to 0.

 }

Closes the function.

function add_up_player() {

Header for the function determining the value of player's hand.

var ac = 0;

Holds the count of aces.

var i;

For iteration.

var sumup = 0;

Initializes the sum.

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

Loops over the cards in the player's hand.

    sumup += playerhand[i].value;

Increments the value of the player’s hand.

    if (playerhand[i].value==1)

Asks if the card is an ace.

      {ac++;

Increments the count of aces.

    }

Closes the if statement.

  }

Closes the for loop.

  if (ac>0) {

Asks if there were any aces.

    if ((sumup+10)<=21) {

If this doesn't make sum go over.

      sumup +=10;

Makes one ace an 11.

    }

Closes the inner if.

  }

Closes the outer if.

  return  sumup;

Returns the total.

}

Closes the function.

function playerdone() {

Header for the function invoked when player says hold.

If (gamestart) {

Checks if game has been started.

   while(more_to_house()) {

The more_to_house function indicates the dealer should get another card.

    househand[hi] = dealfromdeck(2);

Deals a card to the dealer.

    ctx.drawImage(back,housexp,houseyp,cardw,cardh);

Draws the card on the canvas.

    housexp = housexp+20;

Adjusts the horizontal pointer.

    hi++;

Increases the index for the dealer’s hand.

   }

Closes the while loop.

  showhouse();

Reveals the dealer’s hand.

    playertotal = add_up_player();

Determines the player’s total.

    if (playertotal>21){

Asks if the player was over.

      if (housetotal>21) {

Asks if the House was over.

        ctx.fillText("You and house bothwent over.",30,100);

Displays a message.

      }

Closes the inner if statement.

      else {

Begins else clause.

     ctx.fillText("You went over and lost.",30,100);

Displays a message.

      }

Closes the else clause.

    }

Closes the outer clause (player is over).

    else

else the player is not over.

      if (housetotal>21) {

Asks if the dealer was over.

        ctx.fillText("You won. House wentover.",30,100);

Displays a message.

      }

Closes the clause.

      else

Else.

      if (playertotal>=housetotal) {

Compares the two amounts.

        if (playertotal>housetotal) {

Performs a more specific comparison.

        ctx.fillText("You won. ",30,100);

Displays the winner message.

        }

Closes the inner clause.

        else {

Begins the else clause.

         ctx.fillText("TIE!",30,100);

Displays a message.

        }

Closes the else clause.

        }

Closes the outer clause.

      else

Else.

        if (housetotal<=21) {

Checks if the dealer is under.

        ctx.fillText("You lost. ", 30,100);

Displays a message.

        }

Closes the clause.

        else {

Begins the else clause .

         ctx.fillText("You won becausehouse went over.");

Displays a message (player under, House over).

        }

Closes the clause.

 gamestart = false;

Resets gamestart.

}

Closes if true class for if(gamestart).

else{

                alert("Press n to start a new game with the same deck. Reload for a new deck and then press n to start a game.");

            }

Message to player.

  }

Closes the function.

function newgame() {

Header for the function for a new game.

  ctx.clearRect(0,0,cwidth,cheight);

Clears the canvas.

  pi=0;

Resets the index for the player.

  hi=0;

Resets the index for the dealer.

  playerxp = 100;

Resets the horizontal position for the first card of the player's hand.

  housexp= 500;

Resets the horizontal position for the dealer’s hand.

  dealstart();

Calls the function to initially deal the cards.

}

Closes the function.

function showhouse() {

Header for the function to reveal the dealer’s hand.

  var i;

For iteration.

  housexp= 500;

Resets the horizontal position.

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

for loop over the hand.

     ctx.drawImage(househand[i].picture,housexp,houseyp,cardw,cardh);

Draws the card.

     housexp = housexp+20;

Adjusts the pointer.

  }

Closes the for loop.

}

Closes the function.

function shuffle() {

Header for the shuffle.

  var i = deck.length - 1;

Sets the initial value for the i variable to point to the last card.

  var s;

Variable used for the random choice.

  while (i>0) {

As long as i is greater than zero.

      s = Math.floor(Math.random()*(i+1));

Makes a random pick.

      swapindeck(s,i);

Swaps with the card in the i position.

      i--;

Decrements.

  }

Closes the while loop.

  }

Closes the function.

 function swapindeck(j,k) {

Helper function for the swapping.

    var hold = new MCard(deck[j].num,deck[j].suit,deck[j].picture.src);

Saves the card in position j.

    deck[j] = deck[k];

Assigns the card in the k position to the j position.

    deck[k] = hold;

Assigns the hold to card in the k position.

 }

Closes the function.

</script>

Closes the script element.

</head>

Closes the head element.

<body onLoad="init();">

Opening tag to set the call to init.

<header>

    Press <b>n</b> for a new game (same deck), <b>d</b> for deal 1 more card, <b>h</b> for hold. Reload for a new deck and then press n for a new game.<br></header>

Header element containing instructions.

<canvas id="canvas" width="800" height="500">

Canvas opener.

Your browser doesn't support the HTML5element canvas.

Warning to noncompliant browsers.

</canvas>

Closes the element.

<footer>Card images obtained courtesy of the American Contract Bridge Association,

   <a href="http://acbl.mybigcommerce.com/52-playing-cards/">52 playing cards </a>

    <br/>

Opens the footer element, which gives credit and a link to the source for the playing card images.

Fisher-Yates shuffle explained athttp://eli.thegreenplace.net/2010/05/28/the-intuition-behind-fisher-yates-shuffling

Adds the credit for article on the shuffle algorithm.

</footer>

Closes the footer.

</body>

Closes the body.

</html>

Closes the HTML file.

You can change the look and feel of this game in many ways, including offering different ways for the player to request to be dealt a new card, to hold with the current hand, or to request a new hand. You can create or acquire your own set of card images. Keeping score from hand to hand, perhaps including some kind of betting, would be a fine enhancement. Changing the rules for the dealer’s play is possible. As I indicated earlier, implementing that starting a new deck is under computer/dealer control, based on a score or done by a calculation involving random processing, is a idea to consider. Another way to make the game more difficult is to use multiple decks. Keeping score is an obvious feature and one approach is to add a wallet feature, starting off with some amount of money, which is reduced at each game and increased upon wins. Scores and/or more complete results can be stored on the local computer using localstorage.

Testing and Uploading the Application

This program requires considerable testing. Remember that the testing is not finished when you, acting as tester, have won. It is finished when you have gone through many different scenarios. I did my first testing of the game with an unshuffled deck. I then put in the shuffling and kept track of the cases that the testing revealed. I pressed the d key for dealing one more card, the h for holding, and the n for a new game in different circumstances. This is definitely a situation when you want to bring in other people to test your application.

Uploading the application requires uploading all the images. You will need to change the builddeck function to construct the appropriate names for the files if you use something different than what I demonstrate here.

Summary

In this chapter, you learned how to implement a card game using features of HTML5, JavaScript, and CSS along with general programming techniques. These included:
  • Generating a set of Image objects based on names of external files

  • Designing a programmer-defined class of objects for cards, incorporating the Image elements, the card suit, and the card value

  • Drawing images and text on the screen

  • Using for, while, and if to implement the logic of blackjack

  • Using calculations and logic to generate the computer's moves

  • Establishing event handling for the keydown event so that the player could indicate a request to deal a new card, hold, or start a new game and using switch to distinguish between the keys

  • Using the header and footer elements, new to HTML5, for directions and giving credit to sources

This is the last chapter of this book. I hope you take what you have learned and produce enhanced versions of these games and games of your own invention. Enjoy!

My HTML5 and JavaScript Projects book, 2nd edition, has been updated to include implementation of a game called Add to 15, the use of new media, and an introduction to tools to make your projects responsive to different devices with different screen dimensions and touch as opposed to mouse events OR accessible to people constrained to just using the keyboard. In terms of programming techniques, it is an appropriate next book for you. If you want to explore a different programming language, please consider Programming 101: The How and Why of Programming Revealed Using the Processing Programming Language.

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

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