Chapter 22
Script Example 5: BlackJack

In This Chapter

Game Time

After last chapter’s return-to-algebra and the headaches that go with it, it’s time for some fun and games! Let’s play a few hands of cards and prove that JavaScript is an effective environment for game creation. To this end, you'll cook up a little script that plays a simple game of BlackJack.

First, let’s define the ground rules for the game:

Okay, so we cut a few corners with the rules. If you want to extend the game to handle such things as splits, doubles, betting, and Aces that are 1 or 11, please feel free to experiment.

 

Browser Caution

This particular BlackJack example is functional and attractive, but it does make use of the JavaScript 1.1
images[] array to display the playing cards on the page as each hand is drawn. However, don’t forget that 
only Netscape Navigator 3.0 or greater supports JavaScript 1.1 (as of the time of this writing). Therefore, 
this program will not work as-is on Internet Explorer 3.0, because IE doesn’t support the image 
replacement functions.

To revise this example for Internet Explorer, you’d have to strip the image-related functions from the 
program and replace them with a less-advanced feedback system (such as window alerts) in order to
report the results of each card drawn.

Pick a Card

In Chapter 19, “Random Chatter,” you learned how to generate a random number between some minimum value and maximum value using the JavaScript Math.random() method. Recall that you could, for instance, generate a random number between 1 and 4 using this expression:

rnumber = Math.round(Math.random() * (4-1))+1

This works quite nicely for your purposes here, because you need to randomly pick two things for each card "drawn" from the deck:

Because there are four suits, you can easily write a function to pick a suit by randomly generating a number between 1 and 4:

function pickSuit()
{
suit = Math.round(Math.random() * (4-1))+1;
if(suit == 1)
return "Spades";
if(suit == 2)
return "Clubs";
if(suit == 3)
return "Diamonds";
return "Hearts";
}

You really need to check suit only to see if it's 1, 2, or 3. If it isn't one of those, it must be 4 and you don't have to use an if statement to make sure. This is an "old programmer's trick" that saves a few keystrokes and a couple of steps when the program is running. (If you want to drop a buzzword at your next party, this is called program optimization.)

The next step is to pick the card from the suit. There are 13 cards in each suit: Ace, 2 through 10, Jack, Queen, and King. You need your card picker to return two things: the value of the card, which is a number from 2 to 11 (remember, Aces are 11) and the name of the card, which is either the card number or the word "Jack," for instance. This causes a bit of a dilemma: "Jack", "Queen", "King", and "Ace" are all strings, but "2", "3", "5", and so on are numbers. You need both.

Because you need two distinct things from your card picker, make two distinct functions: one to return the value and one to return the name. The value function simply takes the number of your card (a number from 1 to 13) and returns a value from 2 to 11:

function cardValue(card)
{
if(card == 1)
return 11;
if(card > 10)
return 10;
return card;
}

If the card is 1 (an Ace), you say it's worth 11. If the card is a face card, its number will be 11, 12, or 13, so you say it's worth a value of 10. Otherwise, it's worth the number that's passed in.

Figuring out the card name is similar:

function cardName(card)
{
if(card == 1)
return "Ace";
if(card == 11)
return "Jack";
if(card == 12)
return "Queen";
if(card == 13)
return "King";
return new String(card);
}

Each face card has a different name, so you can't just say "If it's greater than 10...." Therefore, you need a separate if statement for each face card. Finally, the number cards are simply returned, but you convert the value to a string (using the “new String” expression).

Here’s how you construct your PickACard() function:

function PickACard(strWho)
{
card = Math.round(Math.random() * (13-1))+1;
suit = pickSuit();
ShowCard(strWho, cardName(card), suit);
return cardValue(card);
}

Return the card's value from PickACard() so you can use this function to add to the totals in the dealer's and user's hands. Notice, though, the function call to ShowCard().You haven’t created this function yet, so let’s discuss it.

Graphic Gaming

Remember from Chapter 15, “Image-ine That!,” that you can dynamically replace graphic images on a page using the Image object and the images[] array of JavaScript 1.1. This applies perfectly to the BlackJack games—or any card game for that matter. Here’s the basic theory behind adding card images to the page:

Conjuring the Code 

To accomplish the above, you’ll need to create the following items in the JavaScript program:

Let’s code all of the above! First, however, you should create a small function named init(), which will initialize the counters.

function init()
{
dealidx=0;
useridx=0;
gameon=true
}

In this function, you zero the two counters (dealidx and useridx), which keep track of which number card in the hand is being drawn. In addition, you set the variable gameon to true. This variable will be used in other functions to control game flow, preventing the user from hitting “Hit” or “Stand” if a game is not currently in session. 

Now you’re ready to code the ClearCards() function.

function ClearCards ()
{
for (i=0; i<12; i++)
{ document.images[i].src="cards/blank.gif" }
}

This is also a simple function. It sets up a for loop that counts from 0 to 11 (the blank placeholder cards placed on the page) and sets each image’s source file to the blank card.

Finally, you create the most complex of these functions: ShowCard().

function ShowCard (who, name, suit)
{
//Construct filename for the card image
if (name.length > 2)
{ name1=name.substring(0,1) }
else { name1=name } ;
name1=name1.toLowerCase()
name2 = suit.substring(0,1);
name2 = name2.toLowerCase();
filename = "cards/"+ name1 + name2 + ".gif";
//Load filename into correct image slot
if (who=="Dealer")
{ document.images[0+dealidx].src = filename; dealidx++ }
else { document.images[6+useridx].src = filename; useridx++ }
}

This function takes three parameters: who has drawn the card (either “dealer” or “user”), the name of the card (2 through 10 for number cards or “Jack”, “King”, “Queen”, “Ace” for name cards), and the suit of the card (“Hearts”, “Diamonds”, “Clubs”, or “Spades”). After the card is drawn, these parameters are sent to ShowCard() by whichever function calls it.

In the first half of ShowCard(), the filename for the card image is constructed. The first if statement determines whether the card name is longer than 2 characters, in which case it strips the first letter from the name and converts it to lowercase. This results in a number between 2 and 10 or j, k, q, or a. Next, the first letter of the suit is stripped and converted to lowercase. Finally, the two parts are concatenated into a string conforming to the card image filename, as in cards/5h.gif (the 5 of hearts).

In the second half of ShowCards(), an if statement determines whether this card was drawn by the dealer or user. In either case, it then uses the JavaScript 1.1 images[] array to change the source file of an image on the page. To determine which image, it adds the current index (dealidx or useridx) to the proper offset. Because the first six blank cards on the page represent the dealer’s hand, his offset is zero. The sixth placeholder card on the page is the first card in the user’s hand, so the useridx is added to an offset of 6. After that, the correct image file is loaded into the correct placeholder on the page, and the index counter is incremented by one.

Dealer Takes a Card

The user takes a card only when he clicks the Hit me button. However, after the user clicks Stand, the dealer must repeatedly take cards until the house's hand goes over 16. This means you have to set up a function that repeats the pickacard() process until 17 is reached or exceeded. If you think this sounds like a job for a while loop, you're right.

Recall the basic form of the while loop: "While {something} is 'true', keep doing something else." In this case, you want to keep picking cards while the total in the dealer's hand is less than 17.

function Dealer()
{
if (gameon==false)
{message ("You must click 'New Hand' first!")}
else
{
while(form.dealer.value < 17)
{
form.dealer.value = eval(form.dealer.value) + PickACard("dealer");
}
}
}

Simple, isn't it? Note that the entire function is encased within the positive clause of an if statement. This prevents the function from executing if a game has not yet begun (signaled by the gameon variable). If gameon is false, the user is sent a message stating that he should begin the game by clicking the New Hand button. 

Speaking of sending messages, note the call to a function named message(). This is a simple function (as you’ll see in the final illustration of the program) that merely alters the value of a form button at the top of the browser. This button is used as a “message board” to inform the user.

The function for when the user picks a card is equally straightforward:

function User()
if (gameon==false)
{message ("You must click 'New Hand' first!")}
else
{
form.you.value = eval(form.you.value) + PickACard("user");
if(form.you.value > 21)
{
gameon=false; 
message("You busted! Game over, man. Click 'New Hand' to play again."); 
}
}
}

And, after the players have taken all the cards they want, figuring out who won is a snap:

function LookAtHands(form)
{
if (gameon==true)
{
if(form.dealer.value > 21)
{ gameon=false;
message("House busts! You win! Click 'New Hand' to play again.");
}
else if(form.you.value > form.dealer.value)
{
gameon=false; 
message("You win! Click 'New Hand' to play again.");
}
else
if(form.dealer.value == form.you.value)
{
gameon=false;
message("Push! Good match -- Click 'New Hand' to play again.");
}
else
gameon=false;
message("House wins! Click 'New Hand' to try again.");
}
}
}

To kick off a new game, you call the NewHand() function, which resets the score and board (image indexes, game scores, and card images) and then deals the first card to the dealer and the user.

function NewHand(form)
ClearCards();
message ("New game started -- Good Luck!");
form.dealer.value = 0;
form.you.value = 0;
form.dealer.value = eval(form.dealer.value) + PickACard("dealer");
form.you.value = eval(form.you.value) + PickACard("user");

Using the eval() function guarantees that the information in form.you.value is treated as a number, not a string. (JavaScript has a tendency to look at things as strings, whether they are strings or not.)

JavaScript Is Very Fond of Strings

JavaScript will convert any form fields that are nothing but numbers into their string equivalents. Therefore, if you want to change the value of a number in a field, you should first use the eval() function to make sure that the field value is treated as a number, not a string. For example, if you have a form field called number and it holds the value 23, you add 5 to it like this:

form.number.value = form.number.value + 5;

The resulting number has the value "235", as though you were tacking strings together. However, you can use eval() to keep this from happening:

form.number.value = eval(form.number.value) + 5;

In this case, the result will be "28".

Now that you’ve learned about the important functions in the logic of this game, it's time to "spread the felt." Let’s set up the gaming table.

Deal Me In

The game page needs two text form fields (for the house's and user's totals) and three buttons: Hit, Stand, and New Hand. Each button will have its own onClick event handler to process taking a card (for a hit) or having the dealer take cards (if the user chooses to stand). From the functions you've compiled so far, you can see that the following statements are true:

You create the game buttons with form fields that call the appropriate functions, such as:

<input type="button" value="Hit me!" onclick="User(this.form)">
<input type="button" value="Stand" onclick="Dealer(this.form);LookAtHands(this.form);">
<input type="button" value="New Hand" onclick="init();NewHand(this.form)">

These form fields are placed within a table on the page to improve the look of the page.

Knowing that, you’re ready for the complete script for the BlackJack game. Watch for the following things, which appear beside all described functions: the message form field at the top of the page, the table containing the scores and game buttons, and finally a table containing the 12 placeholder images for the cards.

<html>
<head>
<title>BlackJack</title>
</head>
<body onload="gameon=false;document.forms[1].reset()">
<script language="JavaScript">
<!-- Hide from non-JavaScript browsers
function init()
{
dealidx=0;
useridx=0;
gameon=true
}
function pickSuit()
{
suit = Math.round(Math.random() * (4-1))+1;
if(suit == 1)
return "Spades";
if(suit == 2)
return "Clubs";
if(suit == 3)
return "Diamonds";
return "Hearts";
}
function cardName(card)
{
if(card == 1)
return "Ace";
if(card == 11)
return "Jack";
if(card == 12)
return "Queen";
if(card == 13)
return "King";
return new String(card);
}
function cardValue(card)
{
if(card == 1)
return 11;
if(card > 10)
return 10;
return card;
}
function message (msg)
{
document.forms[0].message.value = msg
}
function ClearCards ()
{
for (i=0; i<12; i++)
{ document.images[i].src="cards/blank.gif" }
}
function ShowCard (who, name, suit)
{
//Construct filename for the card image
if (name.length > 2)
{ name1=name.substring(0,1) }
else { name1=name } ;
name1=name1.toLowerCase()
name2 = suit.substring(0,1);
name2 = name2.toLowerCase();
filename = "cards/"+ name1 + name2 + ".gif";
//Load filename into correct image slot
if (who=="dealer")
{ document.images[0+dealidx].src = filename; dealidx++ }
else { document.images[6+useridx].src = filename; useridx++ }
}
function PickACard(strWho)
{
card = Math.round(Math.random() * (13-1))+1;
suit = pickSuit();
ShowCard(strWho, cardName(card), suit);
return cardValue(card);
}
function NewHand(form)
ClearCards();
message ("New game started -- Good Luck!");
form.dealer.value = 0;
form.you.value = 0;
form.dealer.value = eval(form.dealer.value) + PickACard("dealer");
form.you.value = eval(form.you.value) + PickACard("user");
}
function Dealer(form)
{
if (gameon==false)
{message ("You must click 'New Hand' first!")}
else
{
while(form.dealer.value < 17)
{
form.dealer.value = eval(form.dealer.value) + PickACard("dealer");
}
}
}
function User(form) 
if (gameon==false)
{message ("You must click 'New Hand' first!")}
else
{
form.you.value = eval(form.you.value) + PickACard("user");
if(form.you.value > 21)
{
gameon=false; 
message("You busted! Game over, man. Click 'New Hand' to play again."); 
}
}
}
function LookAtHands(form)
{
if (gameon==true)
{
if(form.dealer.value > 21)
{ gameon=false;
message("House busts! You win! Click 'New Hand' to play again.");
}
else if(form.you.value > form.dealer.value)
{
gameon=false; 
message("You win! Click 'New Hand' to play again.");
}
else
if(form.dealer.value == form.you.value)
{
gameon=false;
message("Push! Good match -- Click 'New Hand' to play again.");
}
else
gameon=false;
message("House wins! Click 'New Hand' to try again.");
}
}
}
//-->
</script>
<h2><center>BlackJack</h2></center>
<center><form>
<h5><input type=button name="message" value="To begin gambling, click 'New Hand'. (Dealer stands on 17)">
</h5>
</form></center>
<hr>
<form>
<div align="center"><center><table border="3" width=40%>
<tr>
<td>Dealer has</td>
<td><center><input type="text" size="5" name="dealer"></center></td>
</tr>
<tr>
<td>You have</td>
<td><center><input type="text" size="5" name="you"></center></td>
</tr>
<caption align="bottom">
<input type="button" value="Hit me!" onclick="User(this.form)">
<input type="button" value="Stand" onclick="Dealer(this.form);LookAtHands(this.form);">
<input type="button" value="New Hand" onclick="init();NewHand(this.form)">
</caption>
</table>
</center></div>
</form>
<table border="0" width="100%">
<tr>
<td>Dealer's Hand</td>
<td><img src="cards/blank.gif" width="73" height="97">
<img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97">
<img src="cards/blank.gif" width="73" height="97">
</td>
</tr>
<tr>
<td>Your Hand</td>
<td><img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97"> 
<img src="cards/blank.gif" width="73" height="97">
<img src="cards/blank.gif" width="73" height="97">
</td>
</tr>
</table>
</body>
</html>

When viewed from a browser, your BlackJack game looks like the following figure.

Gambling with JavaScript (it’s cheaper than Vegas).

Of course, this game will work best if you actually have the 52 card images to load. You can certainly make your own, but remember to change the dimensions coded into the HTML program to fit your images (the images used in this example were 97 pixels high and 73 pixels wide). However, if you don’t want to attempt that on your own, visit the Web site for this book to download the card images used in this example.

The Least You Need to Know

In this example, you learned how to create a game with JavaScript. Using the tools you've collected from previous chapters, you built a simple BlackJack player. You also saw how using the while statement makes it possible to continue working inside a function until some condition is met. To make sure that the totals you were building in the form were correct, eval() makes sure that field values are treated as numbers instead of strings. And with JavaScript 1.1’s images[] array, you dynamically displayed images for the cards in each hand.

Previous Chapter Next Chapter


Beginning of ChapterTable of ContentsBook Home PageQue Home Page


For comments or technical support for our books and software, select Talk to Us.
To order books, call us at 800-716-0044 or 317-228-4366.

© 1997, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon & Schuster Company.