Thunderjack! HTML5 Game Update: Cheating? Yes But No

Hey guys. We’re into getting into some of the updates mentioned from my past post – mainly checking if an opening hand has a Blackjack, and eventually other specific combinations of cards in the hand. And there’s just one little problem… it can take all day to get the cards we need in order to test.

So, we’ll need a way to control the order in which cards are drawn. Otherwise, we may waiting a long time before we’re dealt the cards we want. So, we’ll have to cheat and pull a fast one here.

But before we get into that, let’s take a look at how the game sets up the cards and the deck.

Card Suits And Values

As you know, there are four suits and we have values from 2 to Ace. Let’s start with the suits first.

In Thunderjack!, we have a class called CardValues that acts like an enumeration, similar to what you might see in C# or Java. Except, we’ll be storing strings instead of integral values.

Thunderjack.CardSuits.HEARTS = "h";
Thunderjack.CardSuits.DIAMONDS = "d";
Thunderjack.CardSuits.CLUBS = "c";
Thunderjack.CardSuits.SPADES = "s";
Thunderjack.CardSuits.ALL = [
  Thunderjack.CardSuits.HEARTS,
  Thunderjack.CardSuits.DIAMONDS,
  Thunderjack.CardSuits.CLUBS,
  Thunderjack.CardSuits.SPADES
];

The ALL array is useful in iterating through all of the suits, which we’ll do later.

When storing the card values, you may think we’d use a number. However, we’ll going to use both string and number. The strings for suits and values will be used when creating unique IDs. We’ll get into these later, but for now, let’s have a look at the values.

We also have another ALL array for iterating through all of the values.

The CardPoints object stores the values, indexed by the string values defined above.

Thunderjack.CardValues.TWO = "2";
Thunderjack.CardValues.THREE = "3";
Thunderjack.CardValues.FOUR = "4";
Thunderjack.CardValues.FIVE = "5";
Thunderjack.CardValues.SIX = "6";
Thunderjack.CardValues.SEVEN = "7";
Thunderjack.CardValues.EIGHT = "8";
Thunderjack.CardValues.NINE = "9";
Thunderjack.CardValues.TEN = "10";
Thunderjack.CardValues.JACK = "j";
Thunderjack.CardValues.QUEEN = "q";
Thunderjack.CardValues.KING = "k";
Thunderjack.CardValues.ACE = "a";
Thunderjack.CardValues.ALL = [
  Thunderjack.CardValues.TWO,
  Thunderjack.CardValues.THREE,
  Thunderjack.CardValues.FOUR,
  Thunderjack.CardValues.FIVE,
  Thunderjack.CardValues.SIX,
  Thunderjack.CardValues.SEVEN,
  Thunderjack.CardValues.EIGHT,
  Thunderjack.CardValues.NINE,
  Thunderjack.CardValues.TEN,
  Thunderjack.CardValues.JACK,
  Thunderjack.CardValues.QUEEN,
  Thunderjack.CardValues.KING,
  Thunderjack.CardValues.ACE
];
Thunderjack.CardPoints = { };
Thunderjack.CardPoints[Thunderjack.CardValues.TWO] = 2;
Thunderjack.CardPoints[Thunderjack.CardValues.THREE] = 3;
Thunderjack.CardPoints[Thunderjack.CardValues.FOUR] = 4;
Thunderjack.CardPoints[Thunderjack.CardValues.FIVE] = 5;
Thunderjack.CardPoints[Thunderjack.CardValues.SIX] = 6;
Thunderjack.CardPoints[Thunderjack.CardValues.SEVEN] = 7;
Thunderjack.CardPoints[Thunderjack.CardValues.EIGHT] = 8;
Thunderjack.CardPoints[Thunderjack.CardValues.NINE] = 9;
Thunderjack.CardPoints[Thunderjack.CardValues.TEN] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.JACK] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.QUEEN] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.KING] = 10;
Thunderjack.CardPoints[Thunderjack.CardValues.ACE] = 1;

You may also notice that Ace only stores the value of 1 not 11. We’ll be using the 11 at another time, when we calculate how many points are in a player’s hand than contains an Ace. We won’t be using the CardPoints in this post, but we’ll need it soon.

Card Data Structure

Now that we have suites and values defined, we want a card data object that can store these. In Blackjack, more than one deck is commonly used, and Thunderjack! is no exception. This introduces the problem of having multiple cards with the same suit and value, and we want to be able to uniquely identify every card in each deck.

This is where the card ID that I mentioned earlier comes into play. We’ll be storing that in our data object, as well as the zero-based array index that identifies which deck the card represented by the card data object comes from.

This is what our card data object class looks like:

Thunderjack.CardData = function(s_cardValue, s_cardSuit, i_deckIndex) {
  this.value = s_cardValue;
  this.suit = s_cardSuit;
  this.deckIndex = i_deckIndex;
  this.id = this.value + "_" + this.suit + "_" + this.deckIndex;
};

As you can see, we’re forming our card ID as a string using the following format:
[card value] _ [card suit] _ [deck index]

For example, the seven of clubs from the third deck would look like this:
"7_c_2"

Deck Structure

Every card is represented by a card data object, and a deck is simply an array of 52 of these card data objects. We have a simple class for “dealing” and shuffling cards, as well as a few other utility functions.

Starting out, the deck data class looks like this:

//===========================================================================================
//"public"
//=========================================================================================== 

//===========================================================================================
//ctor
//===========================================================================================
Thunderjack.DeckData = function(i_numDecks) {
  //data: Thunderjack.CardData
  this._cardsData = [];
  this._init(i_numDecks);
};

//-------------------------------------------------------------------------------------------
//getLength
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype.getLength = function() {
  return(this._cardsData.length);
};

//-------------------------------------------------------------------------------------------
//popCardData
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype.popCardData = function() {
  return(this.getLength() > 0 ? this._cardsData.pop() : null);
};

//-------------------------------------------------------------------------------------------
//shuffle
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype.shuffle = function() {
  Phaser.ArrayUtils.shuffle(this._cardsData);
};

//===========================================================================================
//"private"
//===========================================================================================

//-------------------------------------------------------------------------------------------
//_init
//-------------------------------------------------------------------------------------------
Thunderjack.DeckData.prototype._init = function(i_numDecks) {
  var i_decksCount;
  if (typeof i_numDecks === "number") {
    i_decksCount = i_numDecks;
  } else {
    return;
  }

  for (var i_deckIndex = 0; i_deckIndex < i_decksCount; i_deckIndex++) {
    for (var i_valueIndex = 0; i_valueIndex < Thunderjack.CardValues.ALL.length; i_valueIndex++) {
      var s_value = Thunderjack.CardValues.ALL[i_valueIndex];
      for (var i_suitIndex = 0; i_suitIndex < Thunderjack.CardSuits.ALL.length; i_suitIndex++) {
        var s_suit = Thunderjack.CardSuits.ALL[i_suitIndex];
        this._cardsData.push(new Thunderjack.CardData(s_value, s_suit, i_deckIndex));
      }
    }
  }
};

You can see in the _init method is where we create the CardData objects for each card in every deck. This is where we use the ALL arrays from CardSuits and CardValues.

Note that, instead of using those ALL properties, we could have also iterated through them using a for…in statement, but the arrays are useful in other parts of the game, which we won’t get into right now.

The method we’re interested in when dealing cards is DeckData.popCardData(). This will remove the last CardData object from the _cards array and return it to us. This is the basic structure of the DeckData class. There are other methods in this class as well, and they’ll be added as we need them.

You’ll also notice a shuffle method. Phaser already provides for us an ArrayUtils class, and so happens to have a shuffle method that randomizes the order of items in an array. We don’t have to write one ourselves. Remember, coders are lazy. (:

Get off yo ass and write some code!
Get off yo ass and write some code!
Dealing Cards to Each Player

With the deck and cards set up, we can now begin to deal cards to the players and dealer.
Some psuedocode looks like this:

function dealCardsToPlayers() {
  while (numberOfCardsDealtToEachPlayer < 2) {
    for each player in the game {
      dealCardToPlayer (player);
    }
    
    dealCardToPlayer (dealer);
    numberOfCardsDealtToEachPlayer++;
  }
}

When a card is dealt in dealCardToPlayer, it looks something like this:

function dealCardToPlayer (playerData) {
  var cardData = deckData.popCardData();
  playerData.cardsData.push(cardData);
}
Controlling the Order in Which Cards are Drawn (To Cheat!)

Now, if we’re trying to test out certain combinations of cards in a player’s hand, such as a Blackjack on the opening hand, we can be waiting a long ass time before we get what we’re looking for. This is where che~~~ ahem, controlling the order of the cards comes in.

The plan is like this:

  • Setup an array of card ID strings that will specify the order in which we want the cards to be drawn.
  • Shuffle the deck (if necessary)
  • After the cards have been shuffled, set the order of the cards in the deck according to the array of card Ids in step one.

Let’s setup each of these steps.

1). Setup an array of card ID strings that will specify the order in which we want the cards to be drawn.

We can set the array up that will hold the card IDs we want to use in the following:

debugCardIds = [
  "4_h_0",  //first card that will be dealt to the player
  "a_d_0",  //first card that will be dealt to the dealer
  "5_c_0",  //second card that will be dealt to the player
  "10_s_0"  // second card that will be dealt to the dealer
];

Say we have only one player and the dealer. From the card dealing snipped above, all players are dealt a card, then the dealer. Then this process continues until each player and the dealer has two cards.

2). Shuffle the deck (if necessary)

If we’re just starting the game, we’ll want to shuffle the deck. We can do that using the shuffle method of the DeckData class mentioned earlier.

deckData.shuffle();

3). After the cards have been shuffled, set the order of the cards in the deck according to the array of card Ids in step one.

function aceUpSleeve() {
  var s_cardId = _debugCardIds.shift();
  var i_swapIndex = _deckData.getLength();
  while (s_cardId) {
    _deckData.swap(s_cardId, --i_swapIndex);
    s_cardId = _debugCardIds.shift();
  }
}

What we’re doing here is using the shift method of JavaScript’s Array class to retrieve and remove the first card ID.

When dealing cards, remember that we retrieve and remove the last CardData object from the deck. So when setting up the deck in the order we want, we swap out the last card data object in the deck with debug card ID. Then, we work our way backwards, towards the beginning of the deck, until our debugCardsId array is empty.

The i_swapIndex variable represents the array index within the DeckData where we will place the CardData represented by our debug card ID. This is where a new method for DeckData, swap(), comes in:

Thunderjack.DeckData.prototype.swap = function(s_cardId, i_indexToSwapAt) {
  if (i_indexToSwapAt < 0 || i_indexToSwapAt >= this.getLength()) {
    return;
  }

  var i_indexOf = this.indexOfId(s_cardId);
  if (i_indexOf > -1) {
    var cardData = this._cardsData[i_indexToSwapAt];
    this._cardsData[i_indexToSwapAt] = this._cardsData[i_indexOf];
    this._cardsData[i_indexOf] = cardData;
  }
};

What this method does is first, locate the index within the _cardsData array of the CardData represented by our card debug ID, Then, it will swap the elements of the _cardsData array; the card data found at our requested swap index with the card data at the located index.

So now, our dealCardsToPlayers() method looks like this:

function dealCardsToPlayers() {
  //do this first – assume cards have been shuffled already in some other function
  aceUpSleeve();

  while (numberOfCardsDealtToEachPlayer < 2) {
    for each player in the game {
      dealCardToPlayer (player);
    }
    
    dealCardToPlayer (dealer);
    numberOfCardsDealtToEachPlayer++;
  }
}

Also, if we don’t want to control the order of dealing cards, we can set debugCardIds to an empty array.

The problem with this is, we have to keep editing the code when we want to control the order. Later on in development, I’ll build a user interface that allows us to manipulate the debug card Ids. Adding a debug/diagnostic interface like this is not something you’d put in a release version of your game, but they are invaluable when debugging, testing, and demoing your game.

Yes, they are a little bit of a detour, but it’s worth it. If you’re testing the game with someone else, they’ll be able to easily control the order of the deck without having to change the code. For my clients, they wouldn’t want to anyway – they just want something that’s easy for them to use without having to poke around in the code. That’s my job. πŸ˜‰

Finally, you can watch this video of the *cough*cheat’n’*cough* in action. You may want to view the video at full-screen so you can see the edits I’m making in code. Also, after the opening cards are drawn each time, feel free to pause the video to check that the order of the cards drawn does indeed match with the _debugCardIds array (they do):

So that’s it for now. This was a long one, and thanks for reading all the way to the end. I can’t wait until this game is finished for you all to play!

– C. out.