Thunderjack! HTML5 Game Update: Completed the Logic for Thunderjack Hands

Hey there.

Welcome back!
Next we’re going to add what happens when a player gets a hand of the same name as the game itself: a Thunderjack!

PFCHSHSH-KCGHOGHOGHOUGHOGHOUGHWGHWGHW… “THUNDERJACK!!” (spelling of a thunder sound effect)

A Thunderjack is a suited (both cards have the same suit) Blackjack, and the non-ace card is a Jack or higher. The following hands are examples of Thunderjack hands:

Ace Clubs + Jack Clubs
Queen of Hearts + Ace of Hearts
King of Spades + Ace of Spades

However, an Ace of Diamonds and 10 of Diamonds is not a Thunderjack, but a regular Blackjack, even though the cards are of the same suit.

There are also some additional criteria for a Thunderjack:

  1. Only one player hand can get a Thunderjack. If more than one player gets a Thunderjack, those affected hands cancel each other out and push.
  2. The one player with a Thunderjack is payed out immediately.
  3. That player also does not play against the dealer.
  4. If the dealer gets a Blackjack, the player is still paid the Thunderjack payout. The result is not a push.
  5. The dealer can not get a Thunderjack. Even if the dealer gets a suited Blackjack with the non-ace card being a Jack or higher, the result is still a Blackjack.

Just like checking for a regular Blackjack, we will check for a Thunderjack after all the players and dealer have received been dealt their first two cards. We’ll need to change our original code that involves checking the opening hands for Blackjack. If you recall from the previous article, the pseudo code looks like this:

if hasBlackjack(dealer) {
   handleDealerBlackjack();
   endRound();
 } else {
   checkIfPlayersHaveBlackjacks();
   setFirstTurnPlayer();
 }

First, we need to store a few variables. We want to determine which player, if any, has a Thunderjack. This code checks every player’s hand for a suited Blackjack where the non-ace card is a face card. The pseudo code now looks like:

var playerWithThunderjack = checkIfPlayerHasThunderjack()
var playersWithBlackjack = checkIfPlayersHaveBlackjacks()
var doesDealerHaveBlackjack = hasBlackjack(dealer)
if playerWithThunderjack {
  handleThunderjack(playerWithThunderjack)
  endRound()
} else {
  if doesDealerHaveBlackjack {
    handleDealerBlackjack()
    endRound()
  } else {
    setFirstTurnPlayer()
  }
}

All of the Thunderjack handling happens in the checkIfPlayerHasThunderjack() function. If there is more than one player with a Thunderjack, all affected players push. If only one player qualifies, we return that player for later use.

function checkIfPlayerHasThunderjack() {
  var playersWithTj = getAllPlayersWithTj();
  if playersWithTj.length > 1 {
    handleMultipleThunderjacks(playersWithTj);
    return null;
  } else if (playersWithTj.length == 0) {
    return null;
  }
  return playersWithTj[0];
}

This function determines all players, if any, that have a Thunderjack, and returns them in an array. The array will be empty if no players qualify.

function getAllPlayersWithTj() {
  var playersWithTj = []; // set up an empty array
  //do not include the dealer here - only the players, because dealer can not get a thunderjack
  for each player in the game {
    if hasThunderjack(player) {
      playersWithTj.push(player); // add this player to the array
    }
  }
  return playersWithTj;
}

If we have more than one player with a Thunderjack, they all push:

function handleMultipleThunderjacks(playersWithTj) {
  for each player in playersWithTj {
    player.handData.isPush = true;
  }
}

The criteria for getting a Thunderjack is defined in this function:

function hasThunderjack(player) {
  return hasBlackjack(player) &&
    isFlush(player) &&
    isLowCardAtLeast(player, Thunderjack.CardValues.JACK);
}

This function handles the one player that has Thunderjack:

function handleThunderjack(player) {
  player.handData.hasThunderjack = true;
}

One requirement for a Thunderjack is that both cards must be the same suit (like a flush in Poker). Since this will only be run against the player’s opening hand of two cards, was can assume indexes 0 and indexes 1 in the player.cardsData array.

function isFlush(player) {
  var firstCardData = player.cardsData[0];
  var secondCardData = player.cardsData[1];
  return firstCardData.suit == secondCardData.suit;
}

We’re using the same two cards. Let’s use a lowestCardValue parameter instead of making a function “isLowCardAtLeastAJack()”. That way, if we want to change this requirement in the future, all we have to do is change what is being passed as lowestCardValue.

function isLowCardAtLeast(player, lowestCardValue) {
  var firstCardData = player.cardsData[0];
  var secondCardData = player.cardsData[1];
  return
    isCardValueAtLeast(firstCardData, lowestCardValue) &&
    isCardValueAtLeast(secondCardData, lowestCardValue);
}

function isCardValueAtLeast(cardData, lowestCardValue) {
  var indexOfLowestCardValue = Thunderjack.CardValues.ALL.indexOf(lowestCardValue);
  var indexOfCardDataValue = Thunderjack.CardValues.ALL.indexOf(cardData.cardValue);
  return indexOfCardDataValue >= indexOfLowestCardValue;
}

In the Thunderjack.CardValues.ALL array, all the card values are defined in order of lowest rank to highest rank (Ace being the highest here). The JavaScript Array class has a method called indexOf, which will search the array for a specified value, and return it’s index, or position, within the array. So, what we do here is get the index of the cardValue of our CardData, and compare it to the index of the specified indexOfLowestCardValue.

The CardData’s index must be at least as high lowCardValue’s in order to qualify. Since we specified Thunderjack.CardValues.JACK as the lowest card value, our CardData’s card value must be at least a Jack, just like it was defined in the Thunderjack requirements.

This completes handling of players with Thunderjacks. After all opening hands have been resolved, and the game has not ended due to a player’s Thunderjack, or a dealer’s Blackjack, we want to set the first turn player.

From the previous article, we had a setFirstTurnPlayer function that sets the turn to the first player eligible to play against the dealer.

function setFirstTurnPlayer() {
  var turnPlayerIndex = getNextTurnPlayerIndex(0); 
  if (turnPlayerIndex !== -1) {
    setTurnPlayer(turnPlayerIndex);
  } else {
    endRound();
  }
}

The function that setFirstTurnPlayer uses to determine which player goes first is getNextTurnPlayerIndex. This function gets the index of the next player that is eligible to play against the dealer. Players are stored in an array, in this order: right, middle, left, dealer. When setFirstTurnPlayer runs, it calls getNextTurnPlayerIndex with startTurnPlayerIndex value set to 0. This allows us to check the first array index.

Note: Later in the game, when we’re moving onto the next player, getNextTurnPlayerIndex will have a value of whatever the current turn player index sent to it, plus one. For now, don’t worry about it. We’ll get to this in the next article.

function getNextTurnPlayerIndex(startTurnPlayerIndex) {
  var index = startTurnPlayerIndex;
  while (index < turnPlayerIds.length) {
    var playerId = turnPlayerIds[index];
    var handData = playerHandsData[playerId];
    if (isPlayingVsDealer(handData)) {
      return(index);
    }
    index++;
  }
  return(-1);
}

The getNextTurnPlayerIndex function in turn, calls isPlayingVsDealer, and this is the function we’re interested in. As shown, a player is eligible to play against the dealer if the player does not have a Blackjack, and has not pushed.

function isPlayingVsDealer(handData) {
  return(
    !(playerHandData.hasBlackjack ||
    playerHandData.isPush));
}

We want to edit this function so that it includes checking if the player does not have a Thunderjack. If so, the player is eligible.

function isPlayingVsDealer(handData) {
  return(
    !(playerHandData.hasBlackjack ||
    playerHandData.isPush ||
    playerHandData.hasThunderjack));
}

The game now allows us to properly determine if any players have a Thunderjack, and to obtain the first turn player, skipping over any players with a Thunderjack (because they are ineligible to play against the dealer).

In the next article, we’ll get into placing bets, which I touched on in an earlier post, but will cover in more detail.

Let me know if you have any questions. Thanks for staying with me, guys. Take care.

– C. out.