-->
Bookmark and Share

Blackjack

Play the game or view the source code.

1. Starting a Round

The startRound() function first does a little housekeeping. This involves resetting the hands from the previous round, enabling and disabling form buttons and checking if a new deck is needed.

function startRound() {

  var i;

  // Reset all hands.

  dealer.reset();
  for (i = 0; i < player.length; i++) {
    player[i].reset();
    if (i > 0)
      player[i].fieldNode.style.display = "none";
  }

  // Start with a single player hand.

  curPlayerHand  = 0;
  numPlayerHands = 1;

  // Enable/disable buttons.

  document.forms["controls"].elements["deal"].disabled      = true;
  document.forms["controls"].elements["increase"].disabled  = true;
  document.forms["controls"].elements["decrease"].disabled  = true;
  DisablePlayButtons();

  // If the burn card was reached, start a new deck.

  if (deck.cardCount() < burnCard) {
    alert("New deck.");
    newDeck();
  }

  ...

The DisablePlayButtons() function simply disables the "Split," "Double," "Surrender," "Hit" and "Stand" buttons. This must be done at several other points in the code so it makes sense to define a function for it.

Next it takes the player's bet, deducting the amount from the player's credits.

  // Take the player's bet.

  player[0].bet = defaultBet;
  credits -= player[0].bet;
  updateBetDisplay(0);

The final step is to start dealing the initial cards for the player and the dealer. We could just call the .addCard() method twice on each hand. But then all the cards would appear on the page at once. It's more desirable to have a short pause before each card is dealt.

JavaScript has no function for pausing execution. Performing a busy wait, say with an empty for loop between each deal of a card, wouldn't help either. The browser will not update the display while script code is executing. A busy wait would just lock up the browser for a time, then all the cards would appear at once.

Instead, we create a function to deal one card at a time. To cause a delay between each deal of a card, the built-in setTimeout() function will be used. To start things off, the global variable dealRoundCounter is set to one and dealRound() is called.

  // Start dealing the cards.
  dealRoundCounter = 1;
  dealRound();

Dealing the Cards

Within dealRound() a card is dealt according to the value of dealRoundCounter. The first to the player, the second to the dealer, etc.

function dealRound()
{

  // Deal a card to the player or the dealer based on the counter.

  switch(dealRoundCounter)
  {
    case 1:
      player[0].addCard(getNextCard(), false);
      break;

    case 2:
      dealer.addCard(getNextCard(), true);
      break;

    case 3:
      player[0].addCard(getNextCard(), false);
      break;

    case 4:
      dealer.addCard(getNextCard(), false);
      break;

    default:

      // No more cards to deal, play the round.

      playRound();
      return;
      break;
  }

  // Update the player's score.

  if (player[0].getScore() == 21) {
    player[0].blackjack = true;
    player[0].scoreTextNode.nodeValue = "Blackjack";
  }
  else
    player[0].scoreTextNode.nodeValue = player[0].getScore();

  // Set a timer for the next call.

  dealRoundCounter++;
  setTimeout(dealRound, dealTimeDelay);
}

Once a card is dealt, the player's score is updated, the counter is incremented, setTimeout() is called to fire another call to dealRound (after a delay determined by dealTimeDelay) and the function exits.

By ending code execution, the browser is free to update the page, showing the new card. When the timer expires, dealRound will execute again to deal the next card. After all the card have been dealt, the playRound() function is called and we exit without setting a timer.

Evaluating the Initial Hands

The playRound() function examines the initial cards dealt to the player and the dealer to determine what action to take.

First, if the dealer's face up card is an Ace, we need to offer the player insurance. As insurance is a side bet, the outcome doesn't affect the rest of the game play so a call is made to doInsurance() to handle this (see below) before moving on.

function playRound() {

  // If dealer's up card is an ace, offer insurance.

  if (dealer.cards[1].rank == "A")
    offerInsurance();

If either the player or the dealer has a blackjack, the round is over. The function checks and will call endRound() to finish the round and exit (step 5).

  // Check for dealer blackjack.

  if (dealer.getScore() == 21) {
    dealer.blackjack = true;
    dealer.scoreTextNode.nodeValue = "Blackjack";
  }

  // If player or dealer has blackjack, end the round.

  if (player[0].blackjack || dealer.blackjack) {
    endRound();
    return;
  }

If neither has blackjack, we move on and enable the play buttons: "Split" (if the player was dealt a pair), "Double," "Surrender," "Hit" and "Stand." The player's hand is highlighted and the function exits, leaving the game at step 2, where the next action will depend on which button the player presses.

  // Enable/disable buttons.

  if (canSplit())
    document.forms["controls"].elements["split"].disabled = false;
  document.forms["controls"].elements["double"].disabled    = false;
  document.forms["controls"].elements["surrender"].disabled = false;
  document.forms["controls"].elements["hit"].disabled       = false;
  document.forms["controls"].elements["stand"].disabled     = false;

  // Highlight the player's hand.

  addClassName(player[0].fieldNode, "activeField");
}

Insurance

The doInsurance() function will prompt the player using the built-in confirm() function, so execution will halt until the player responds to the dialog box.

function offerInsurance() {

  var amount;

  // Offer insurance bet to player. This is a side bet so it's resolved
  // here.

  if (confirm("Do you want to buy insurance?")) {

    // Take half of player's current bet from credits.

    amount = player[0].bet / 2;
    credits -= amount;

    // If the dealer has blackjack, show the down card and pay player at
    // 2 to 1.

    if (dealer.getScore() == 21)
    {
      dealer.cardsNode.firstChild.firstChild.style.visibility = "";
      credits += player[0].bet;
      alert("Dealer has Blackjack, you win "
        + formatDollar(player[0].bet));
    }
    else
      alert("Dealer does not have Blackjack, you lose "
        + formatDollar(amount));

    // Update credits.

    updateBetDisplay(0);
  }
}

If the player selects "OK" to buy insurance, it adjusts the player's credits and checks the dealer's hand for a blackjack. If the dealer has blackjack, it updates the player's credits again to pay off the bet. In any case, it returns execution to the calling function, playRound().