Blackjack
Play the game or view the source code.The Blackjack Hand
Object
The Hand
object is designed to represent an individual
blackjack hand and the cards in it. It provides methods for actions like adding
a card to the hand, removing a card and totaling the its score.
When the game is first initialized, one of these objects is created for each possible hand. This includes the dealer's hand and an array of hands for the player (in case of splits). The constructor function is shown below.
Hand.prototype.leftIncr = 2.5; // For positioning cards. Hand.prototype.topIncr = 0.2; Hand.prototype.rollEvery = 5; function Hand(id) { this.cards = new Array(); // Get page elements based on id. this.fieldNode = document.getElementById(id); this.cardsNode = document.getElementById(id + "Cards"); this.scoreTextNode = document.getElementById(id + "Score").firstChild; if (id != "dealer") { this.betTextNode = document.getElementById(id + "Bet").firstChild; this.resultTextNode = document.getElementById(id + "Result").firstChild; } this.reset = handReset; this.addCard = handAddCard; this.removeCard = handRemoveCard; this.getScore = handGetScore; this.clearCards = handClearCards; // Initialize as an empty hand. this.reset(); }
To create a hand, an ID string is passed to the function so it can find the elements reserved for that particular hand's playing area on the page. These references are saved for later use.
This only needs to be done once. Other properties of the Hand
object are only relevant to an individual round. These are defined and set in
the reset()
method which is used to initialize a hand for
play.
function handReset() { // Remove any cards and initialize properties. this.clearCards(); this.cards = new Array(); this.blackjack = false; this.split = false; this.doubled = false; this.surrender = false; this.left = 0; this.top = 0; this.scoreTextNode.nodeValue = "\u00a0"; if (this.betTextNode) { this.betTextNode.parentNode.className = "textBox dollars"; this.betTextNode.nodeValue = "\u00a0"; } if (this.resultTextNode) this.resultTextNode.nodeValue = "\u00a0"; }
First it removes any cards used in the previous round. Then it initializes several flags and values that will be used during the course of play. Finally, it resets any text displays in the hand's playing area. The flags used during game play will be discussed shortly but first let's look at how the cards are displayed.
Displaying Cards
To add a card to a hand, the addCard()
method is used. It adds
the given Card
object to the hand's cards
array and
creates a DOM node for displaying it. This node is dynamically added to
cardArea
class DIV assigned to the hand (stored as the property
cardsNode
) so it will appear on the page. Note that it allows an
option to display the card face down.
function handAddCard(card, down) { var n; var node; // Add the given card to the hand. n = this.cards.length; this.cards[n] = card; // Create a card node for display, set as face down if requested. node = this.cards[n].createNode(); if (down) node.firstChild.style.visibility = "hidden"; // Add the card display to the associated card area on the page. node.style.left = this.left + "em"; node.style.top = this.top + "em"; this.cardsNode.appendChild(node); this.left += this.leftIncr; if (this.cards.length % this.rollEvery == 0) this.top = 0; else this.top += this.topIncr; }
Likewise, the removeCard()
method will remove the last
Card
object in the cards
array and remove the last
card node from the cardsNode
DIV so that it no longer appears on
the page.
function handRemoveCard() { var card; // Remove the last card in the array and save it. card = null; if (this.cards.length > 0) { card = this.cards.pop(); // Remove the card node from the display and reset position. this.cardsNode.removeChild(this.cardsNode.lastChild); this.left -= this.leftIncr; this.top -= this.topIncr; } // Return the card. return card; }
The card that was removed is returned by the function. This makes it useful for handling splits, as we'll see later.
Finally, there is the clearCards()
method which provides a
quick way to remove all the card nodes from a playing area.
function handClearCards() { // Remove the card nodes in the associated card area. while (this.cardsNode.lastChild) this.cardsNode.removeChild(this.cardsNode.lastChild); }
This method is actually used only indirectly via the reset()
method to initialize the hand for play.
Scoring
Since the object of the game is to score points by totaling the value of
each card in a hand, a method is defined to calculate this total. While it
seems simple enough to just add up each card's value, the fact that aces can
be counted as one or eleven complicates the task a bit. The
getScore()
method is defined to handle this.
function handGetScore() { var i, total; total = 0; // Total card values counting Aces as one. for (i = 0; i < this.cards.length; i++) if (this.cards[i].rank == "A") total++; else { if (this.cards[i].rank == "J" || this.cards[i].rank == "Q" || this.cards[i].rank == "K") total += 10; else total += parseInt(this.cards[i].rank, 10); } // Change as many ace values to 11 as possible. for (i = 0; i < this.cards.length; i++) if (this.cards[i].rank == "A" && total <= 11) total += 10; return total; }
Basically, it first totals each card value, counting any ace as one point. Then it goes through the cards again looking only at aces. If it finds one and it can be counted as eleven without pushing the total over 21, it does so. This way, the method returns the highest score possible, without going over 21, for the hand.