There is one more type of hand the player can win: getting a Blitz. So we’re going to code that functionality into the game in this article.
If you’re familiar with Blackjack, you might also know what a Charlie is. If not, don’t worry about it. I gotcha. ๐๐ฟ
A Charlie is when a hand has acquired so many cards – usually five, six, or seven – without busting. When a player gets a Charlie, they are paid immediately, just like when getting a Blackjack or a Thunderjack.Also, the player does not play against the dealer. The next player’s turn immediately begins afterwards.
Thunderjack! will adopt a six-card Charlie, and I’m calling it a Blitz. Why? Because Blitz just sounds cooler. ๐
Ha! But no, taking the definition of the word (currently defined by Merrima-Webster, which is being “a fast intensive nonmilitary campaign or attack”.
So, if you’re taking hit after hit after hit, and still haven’t busted, I’d say that’s a pretty “intensive attack” – on attempting to beat the dealer. Would you agree? ๐
Determining if the player has a Blitz can be checked each time the player takes a hit and has not busted. You can modify the the beginTurnPlayerHit function which was defined in the article that introduced the hit functionality. The updated function is shown below:
beginTurnPlayerHit
GamePlay.prototype._beginTurnPlayerHit = function() {
//get the playerData of the current turn player. we'll be using this as a paramater to the other functions
var turnPlayer = this._getTurnPlayer();
this._dealCardToPlayer(turnPlayer);
this.updateScores();
if (this.didPlayerBust(turnPlayer)) {
this._beginPlayerBust(turnPlayer);
this._setNextTurnPlayer();
} else {
if (this._doesPlayerHaveBlitz(turnPlayer)) {
this._beginPlayerBlitz(turnPlayer);
this._setNextTurnPlayer();
}
}
};
doesPlayerHaveBlitz
GamePlay.prototype._doesPlayerHaveBlitz = function(playerData) {
var playerHand = playerData.getHandData();
var NUM_CARDS_FOR_BLITZ = 6;
return(playerHand.getNumCards() === NUM_CARDS_FOR_BLITZ &&
playerHand.score <= Thunderjack.DesignData.BLACKJACK_POINTS);
};
This function checks that the player’s hand has exactly six cards, and that the score does not exceed 21 (the number of Blackjack points).
Note: Note: You can change NUM_CARDS_FOR_BLITZ to a different value if you like. If you do, make sure your player hand user interface can handle that number of cards. With the current setup of the player_prefab canvas defined in Phaser Editor.
beginPlayerBlitz
GamePlay.prototype._beginPlayerBlitz = function(playerData) {
playerData.hasBlitz = true;
//player is paid immediately here as well
};
A player who gets a Blitz does not play against the dealer. To start this off, the hasBlitz property is set to true in the beginPlayerBlitz function. Next, we’ll need this to update the isPlayingVsDealer function.
The isPlayingVsDealer function determines if a player is playing against the dealer. Our last implementation of the function looked like this:
Happy New Year, guys! โญ I wish you a very fun, and happy 2019, complete with experiences that’ll challenge you and allow you to grow and become a stronger version of yourself.
In this article, we’ll be handling the final two moves you can perform in the game: splitting and surrendering.
If you have a pair in your opening hand, you have the option to split the cards into two hands. Each new will automatically be dealt another card. This gives you an additional hand to try and beat the dealer.
Like doubling down, you must place an addition bet equal to the original wager of the hand in order to split.
There are a few things to be aware of with splitting:
Split hands cannot win via a Thunderjack or Blackjack
Split hands may double down
Split hands may not surrender (will be covered in surrender functionality)
If splitting a pair of aces, both hands must stand after being dealt another card (can’t have more than two cards)
We’ll need a button that will allow the player to splir when pressed. Using the same steps as in previous articles, create the button (using Phaser Editor):
Right-click on the main game canvas, then select Add, then Button.
From the Add Sprite dialog, select the image you want to use as the main button sprite, then press OK.
This will add a button to your canvas. You can drag it to position it where ever you want.
In the button’s properties, set the field property to true. This will create a reference to the button in the code that the canvas compiler generates, allowing you to reference it as well.
Set a varName for the button. I decided to use the game_button_split.
Set the callback to this._onSplitButtonPressed, and set the callbackContext to this.
Set the four “Frame” properties to the appropriate sprite images, so that the button appears responsive when the it is moused over, pressed, and released.
When the canvas is saved, it’ll add the code that creates and positions the button. We also want to ensure there is the callback function, onSplitButtonPressed. We’ll write it like:
GamePlay.prototype._beginTurnPlayerSplit = function() {
var splitPlayerId = this._insertUpperPlayerIdIntoTurnPlayerIds();
this._setSplitStatusForPlayers(splitPlayerId);
this._paySplitCostForSplitPlayer(splitPlayerId);
var turnPlayerId = this._getTurnPlayerId();
this._moveCardFromPlayerToPlayer(turnPlayerId, splitPlayerId);
this._dealCardTo(turnPlayerId);
this._dealCardTo(splitPlayerId);
};
The beginTurnPlayerSplit function is where the actual splitting functionality takes place.
First, we’re adding a new player into the game: the player that is created from the split. This is what insertUpperPlayerIdIntoTurnPlayerIds does, as well as return the player ID of the new player.
Player IDs
Each player is assigned a unique “player ID”. We use these to reference the PlayerHandData for the player we want. From there, we can access the cards in the player’s hand, as well as other information about that player.
So far, we’ve only used the “lower” IDs, not the “upper” ones. Here are all six player hand IDs:
A split is always initiated from one of the three “lower” hands. The new had that is created is its “upper” counterpart.
We split the original hand into two by moving its top card to the new hand. In the beginTurnPlayerSplit function, the turnPlayerId represents the original player’s (the player’s whose turn it is), and splitPlayerId presents the new hand.
We want to insert the new player ID into the array of turn player IDs, (this._turnPlayerIds) after the current turn player’s ID. The insertUpperPlayerIdIntoTurnPlayerIds function does this, and the pseudo code for it looks like:
insertUpperPlayerIdIntoTurnPlayerIds
insertUpperPlayerIdIntoTurnPlayerIds() {
get the splitPlayerId, which is the corresponding "upper" ID of the turn player
get the array index of the turn player ID within the turnPlayerIDs array
insert the splitPlayerId into the turnPlayerIDs array after the turn player ID
};
We can use the JavaScript function, Array.prototype.splice to insert the split player ID directly after the current turn player ID, so that when the turn player is finished, the next turn will be the split player. After the split player’s turn, game play normally resumes.
When getting the splitPlayerId, which is the corresponding “upper” ID of the turn player, the mapping is:
It’s currently the “lower left” player’s turn. They split their hand, creating the “upper left” hand. The turnPlayerIds would then look like:
[
Thunderjack.PlayerHandIds.LOWER_LEFT,
Thunderjack.PlayerHandIds.UPPER_LEFT, //<-- new player ID inserted via splice
Thunderjack.PlayerHandIds.LOWER_RIGHT,
Thunderjack.PlayerHandIds.DEALER
]
You can see that Thunderjack.PlayerHandIds.UPPER_LEFT was inserted directly after the current turn player’s ID, Thunderjack.PlayerHandIds.LOWER_LEFT. And when the “lower left” player’s turn is done, the next turn is the “upper left”. After that, play resumes with “lower right” (same is it would, had the “lower left” player never split).
Now that we have created the new split player, we’re going to set the split status of both players. We’ll set up the rule that player hands that have been involved in a split during the round cannot split again. The setSplitStatusForPlayers will accomplish this.
In order to set the split status, the function could look like:
There is a new property on the PlayerHandData object that we’ve not seen yet, hasSplit. We’ll add that to the constructor function so it now looks like:
One of the rules of splitting is that we don’t want to split a hand that was already involved in a split. That means, for the current turn player, and the new hand that was created, you want to set their hasSplit property to true.
After splitting, when resuming the current player’s turn, or on the split player’s turn, that player can’t split, even if they have a pair. And of course, splitting isn’t free; it costs an additional wager equal to that placed on the hand that is splitting.
Note: Optionally, if you want to make it so that both players must automatically stand if aces are split, you can check if the turn player’s hand is a pair of aces. Then, when the split functionality is down, you can stand twice (once for the turn player, and again for the split hand).
Now that we have all the functionality for splitting done, we can update our showGameButtonsForPlayer function to include eligibility for splitting for the current turn player. If the player is allowed to split, we’ll show the split button. Otherwise, we’ll hide it.
We also want to make sure that the opening two cards are a pair, meaning, they have the same value. For face cards, they both must be the same face. For example, you can split two jacks or two queens, but not a jack and a queen.
areCardsPair
GamePlay.prototype._areCardsPair = function(playerHandData) {
if (!playerHandData || playerHandData.getNumCards() != 2) {
return(false);
}
var cardData0 = playerHandData.getCardAt(0);
var cardData1 = playerHandData.getCardAt(1);
return(cardData0 && cardData1 && cardData0.value == cardData1.value);
};
That’s it for splitting! That was quite a bit to go through. If you have any questions on anything, please ask in the comments section below. Now, let’s move onto the final player move: surrendering.
Surrender
You can do this if your opening hand does not look good against the dealer’s revealed card, and you’re not feeling confident. If a player surrenders, they:
forfeit half their bet (they’re immediately paid)
automatically end their turn
will not play against the dealer
Also, a split hand may not surrender.
First, we create the surrender button. Follow the same steps as creating the split button, except:
Set the varName for the button to game_button_surrender.
Set the callback to this._onSurrenderButtonPressed, and set the callbackContext to this.
Set the four “Frame” properties to the appropriate sprite images, so that the button appears responsive when the it is moused over, pressed, and released.
Write the code for the onSurrenderButtonPressed, so it looks like:
Here, we present the player as surrendering. This will include forfeiting half the bet, updating the player hand status, and moving onto the next turn player.
presentPlayerSurrender
GamePlay.prototype._presentPlayerSurrender = function(playerHandData) {
var n_betValue = playerHandData.betValue;
var WIN_RATIO_SURRENDER = -1 / 2;
var n_amountWon = this._calcWinAmount(n_betValue, WIN_RATIO_SURRENDER);
playerHandData.hasSurrendered = true;
this._playerData.credits += n_amountWon;
};
The presentPlayerSurrender function is very similar to the presentPlayerWon function discussed here. There is also another use of the calcWinAmount function. Except this time, we’re passing a different win ratio, WIN_RATIO_SURRENDER, defined as -1 / 2. When this ratio is passed into calcWinAmount, it will return only half of the original bet value.
We also set the hasSurrendered property of the PlayerHandData object to true.
Remember that a player has surrendered won’t play against the dealer. We previously defined a function isPlayingVsDealer, that determines if a player plays against a dealer. We’ll update that function to exclude players who’ve surrendered from playing against the dealer:
That’s it for the surrender functionality! It’s one of the shorter moves to implement, and most of the code that builds it has already been discussed in earlier articles.
We’re now finished with all five moves hit, stand, double down, split, and surrender… woot! ๐๐ฟ
Now that most of the core gameplay is complete, we can finish off the game. Here’s what’s to come in the near future:
Recycling the deck (so the deck never runs out of cards)
Adding sound effects (yeah!)
Adding the Thunderjack effect (HELL YEAH!! ๐ )
That’s it for this post, guys. Thanks, and talk to you later.
Now that betting and dealing cards is in place, we cover all five moves that the player can perform: hit, stand, double, split, and surrender. We’ll be able to activate these moves by using those five colorful buttons you’ve seen at the bottom.
Starting with this article, the next few will cover adding all five of these moves that the player can perform: hit, stand, double, split, and surrender. This particular article will cover hitting.
Hit
Stand
Double
Split
Surrender
Hitting is the same in Thunderjack! as it is in Blackjack. The object of the game is not to get 21 points; not to get closest to 21 without going over; but to beat the dealer. If you want to bolster your score, you can take a hit. You will be dealt another card, and your score will be updated. If you did not win the hand on the opening deal of the first two cards, or you already don’t have a high score, you can take a hit to get your score closer to 21 points.
Here, you have nine points. It is safe to take a hit. You were dealt a 10, giving you 19 points. A fairly strong hand! โ
This comes at a risk, because if your score goes over 21 points, you bust, losing the hand.
You’ve got 14 points here. Taking a hit is a bit riskyโฆ Dammit! You drew a king, giving you 24 points and busting! ๐
If you’re new to this series of the blog of building this Thunderjack! game, we’re using Phaser Editor, an IDE for HTML5 games creation using Phaser to build the game layout and user interface.
First, create a button that will allow the player to hit when pressed. Using the Editor:
Right-click on the main game canvas, then select Add, then Button.
From the Add Sprite dialog, select the image you want to use as the main button sprite, then press OK.
This will add a button to your canvas. You can drag it to position it where you want.
In the buttonโs properties, set the field property to true. This will create a reference to the button in the code that the canvas compiler generates, allowing you to reference it as well (which we’ll do later).
Set a varName for the button. I decided to use the game_button_hit.
Set the callback to this._onHitButtonPressed, and set the callbackContext to this.
Set the four “Frame” properties to the appropriate sprite images, so that the button appears responsive when the it is moused over, pressed, and released.
When the canvas is saved, itโll add the code that creates and positions the button, as well as specify the callback function onHitButtonPressed. That function:
When the hit button is pressed, it calls another function, beginTurnPlayerHit. Well, why didn’t we just put the code that implements the hit functionality here? Because, we’ll be using this functionality for both, the players, and the dealer. We’ll cover this in a later article.
beginTurnPlayerHit
GamePlay.prototype._beginTurnPlayerHit = function() {
//get the playerData of the current turn player. we'll be using this as a paramater to the other functions
var turnPlayer = this._getTurnPlayer();
this._dealCardToPlayer(turnPlayer);
this.updateScores();
if (this.didPlayerBust(turnPlayer)) {
this._beginPlayerBust(turnPlayer);
this._setNextTurnPlayer();
}
};
What this code does is:
deals a card to the current turn player
updates the points of the players points to include the value of the new card dealt
if the player has busted
updates player status that they have busted
sets the next player’s turn
Some of these functions have already been introduced:
dealCardToPlayer – This function is the meat of the hit move. This deals another card to the turn player from the deck.
updateScores– After the player has received the new card, the scores need to be updated.
This function will set the isBust property on the Thunderjack.HandData class to true. This property is used when determining if this player plays against the dealer.
setNextTurnPlayer
GamePlay.prototype._setNextTurnPlayer = function() {
var i_nextTurnPlayerIndex = this._getNextTurnPlayerIndex(this._turnPlayerIndex + 1);
if (!this._setTurnPlayer(i_nextTurnPlayerIndex)) {
this._endRound();
}
};
This function sets the turn to the next available player. The getNextTurnPlayerIndex has already been introduced, and it will return the index of the next available player, or -1 if all the players (including the dealer) have already played their turn.
The setTurnPlayer function was hinted at when we wrote some pseudo code for the setFirstTurnPlayer function. Here is the implementation for it:
setTurnPlayer
GamePlay.prototype._setTurnPlayer = function(i_turnPlayerIndex) {
this._turnPlayerIndex = i_turnPlayerIndex;
var s_playerId = this._getTurnPlayerId();
if (!s_playerId) {
//no turn player to set
return(false);
}
if (this._isDealerId(s_playerId)) {
this._beginDealerTurn();
} else {
this._beginPlayerTurn(s_playerId);
}
return(true);
};
This function first gets the player ID of the player to whom we’re setting the turn. Then we want to attempt to set the turn to that player.
Turn Player Setup
When calling setTurnPlayer, we pass in the turn player index (not the ID) of the current turn player. We discussed turnPlayerIds in an earlier article. This is an array of all the player IDs, in the order in which they will take their turns. The turn player index is the array index of the current turn player within that array.
If we’ve placed bets on all three hands, for example, we’d set up the the turnPlayerIds array to look like this:
There is another variable we need to track, let’s call it this._turnPlayerIndex, and it’s initialized to ‘0’. When setNextTurnPlayer is called, it will get the next index value by adding 1 onto the current turnPlayerIndex value, and passes this value into setTurnPlayer. It’s this setTurnPlayer function that actually updates the this._turnPlayerIndex variable.
If the player ID represents the dealer, we’ll resume play for the dealer’s turn (which is controlled by the game code AI – we’ll get into this in a later article). If the player ID represents a player, play resumes with that player to make a move (hit, stand, etc.).
We’ll also get into resuming play on the player’s turn in the later article as well. This will detail which move buttons to show, depending on the player’s hand.
One more note about that this function is that it returns true if the turn was successfully set, and false if the turn could not be set. This return value is used by the setNextTurnPlayer function. If setNextTurnPlayer was unable to set the next turn player, it ends the game. (If this._turnPlayerIndex has been incremented, such that it is out of the upper range of the array limit of this._turnPlayerIds, then setTurnPlayer will return false.)
This video shows three players demonstrating the hit functionality. Players that bust will not play against the dealer.
That’s it for the hit move. We’ve also covered some very important functionality with setting player turns, as other moves will use these functions as well.
Next, we’ll cover ability to stand. Thanks for reading, and take care.
We’re continuing working on the bet placing functionality. Looking at our four-step process, we’re now on step two, setting up the bet chip buttons:
Setup the hand buttons
Setup the bet chip buttons
Setup the bet button
Setup the clear button
We recently finished the hand buttons. You can view part 1 and part 2 of this function.
The bet chip buttons work like this: When a bet chip is pressed, it applies its bet value to all the selected hands.
For example, say we:
Select hand #2
Press the 10 bet chip. This will add 10 credits onto hand #2
Select hand #1 (both hands #1 and #2 are now selected)
Press the 50 bet chip. This will add 50 credits onto hand #1, and hand #2. The total credits of hand #2 is now 10 + 50 = 60.
Building the bet chip buttons and functionality put into four parts, explained below.
First, let’s add the chip buttons to the canvas.
Right-click on the canvas, then select Addโฆ, then Button.
From the Add Sprite dialog, select the image you want to use as the main button sprite, then press OK.
This will add a button to your canvas. You can drag it to position it where you want. In the button’s properties, set the field property to true . This will create a reference to the button in the code that the canvas compiler generates, allowing you to reference it as well (which we’ll do later).
Set a name for the bet chip button. I decided to use the name that matches the color of the bet chip.
Set the callback to this._onBetChipButtonPressed, and set the callbackContext to this.
Set the four “Frame” properties to the appropriate sprite images, so that the button appears responsive when the it is moused over, pressed, and released.
Follow these steps for the other bet chip buttons.
Second, once you have all the bet chip buttons made, we’ll turn them into a single prefab. You can select all the chip buttons, convert them to a group, then create a group prefab from the group. When creating my prefab, I named it bet_chips_prefab. The editor then created bet_chips_prefab.canvas, and the bet_chips_prefab.js source file. My bet chips prefab canvas looks like this:
Once you have the prefab added to your main canvas, set a varName for the prefab (I used bet_chips), and set the field property to true. In the main canvas, the canvas compiler will create a reference to this prefab as this.fBet_chips (which we’ll use later).
Third, we need to add some custom code to the bet_chips_prefab JS class file. We want to be able to fire a notification each time a chip button is pressed. A Phaser signal will be added as a property of this class to be referenced from other functions. The following code is added under the -- user code here -- comment:
This code is meant to be called once from the outside – from the code that sets up the UI. Supply the callback function that is called each time the signal fires, and the context in which that callback is used.
This function is the callback function we assigned to each button as their callback property in the editor. Each time a bet chip button is pressed, this function is called. It then dispatches the signal, which in turn, fires the buttonClickCallback function that was passed into the init function.
We need to identify which button was pressed. So, the buttonClickCallback function accepts one parameter of type string, which is the name of the button. This is the same as the name property used when creating each button.
This finishes the setup of the bet chip buttons. The fourth and final step is initializing the bet_chips_prefab and handling what happens when a bet chip button is pressed.
When setting up your main canvas, you can initialize the bet chips prefab class. In the user code (under the -- user code here -- comment), add this:
This is the init function we created earlier on the bet_chips_prefab class earlier. The onBetChipPressed function, which we’ll create next, will be called by the bet_chips_prefab signal each time a bet chip button is pressed.
function onBetChipPressed(s_betKey) {
var selectedPlayerIds = this.handButtons.getSelectedHands();
for (var s_playerId in selectedPlayerIds) {
this.addToPlayerBet(s_playerId, s_betKey);
}
}
The s_betKey is the name of the bet chip button that was pressed. This is the parameter we pass onto the buttonClickSignal of the bet_chips_prefab._onBetChipButtonPressed when the signal is dispatched.
There is a new function on the handButtons object called getSelectedHands. From this previous article, we defined a HandButtons class that manages the three hand buttons for selecting which player hands that we want to apply bets to.
The getSelectedHands function will return an array of string IDs of all the player hand buttons that have been selected. Let’s add the getSelectedHands to the HandButtons class, which looks like this:
HandButtons.prototype.getSelectedHands = function() {
var ids = [ ];
for (var s_id in this._handButtonsById) {
var button = this._handButtonsById[s_id];
if (button.getIsOn()) {
ids.push(s_id);
}
}
return(ids);
};
Back to the onBetChipPressed function, we now know which player hands have been selected at the time the bet chip button was pressed. We can now add the bet value of the selected bet chip to these selected hands with this function:
There is a BetChipValues object which we’ll define as a key/values pairs, where we’ll map the bet chip ID (which we’ll define as the name of the bet chip) to its bet value:
There is also this.playerData.addBet method. This function adds the value of the bet to each selected hand. The playerData is an object that we’ll create from a PlayerData prototype:
Inside the user code of the game play canvas (in this project, we’re using GamePlay, you can add playerData as a property of that prefab class like this: this._playerData = new PlayerData();
We are using values from the PlayerHandIds object we set up earlier, as well as the PlayerHandData, which stores data about each individual hand.
With the this.playerData.addBet(s_playerId, i_betValue); function, we want to first retrieve the PlayerHandData that matches the player ID, then add the specified bet amount to its betValue property.
You can eventually go on to update the UI with bet chips. This is what the updateTotalBet() function is reserved for. Your bet interface can include elements, such as:
the player’s credits
the player’s total bet value
the bet amount for each hand
I’ll leave that as an exercise to you, but if there is enough demand for it, I’ll post an article on it.
The final result here does include the updated UI. In the next update, we’ll set up the bet and clear buttons.
In the last article, we discussed the first part of step one of allowing the player to place bets. We broke the process into four steps, which I’ll reiterate here:
Setup the hand buttons (we are finishing up this one in this article)
Setup the bet chip buttons
Setup the bet button
Setup the clear button
We left off with being able to toggle the hand button 1 on and off. So that it does this:
Next, we’ll create toggle buttons to represent hand buttons 2 and 3.
For hand button 2, first in the Project Explorer of Phaser Editor, copy and paste the file hand_button_1_prefab.canvas, renaming it to hand_button_2_prefab.canvas. Then copy and paste hand_button_1_prefab.js, renaming it to hand_button_2_prefab.js.
Next, open the hand_button_2_prefab.canvas file in the editor. Click on the Configuration tab at the bottom. Under the Source section, look for a className property. Change it’s value to hand_button_2_prefab, then Save the file.
What this does is allow the proper class name (which is hand_button_2_prefab) in the corresponding hand_button_2_prefab.js file to be to be written when the canvas compiler automatically generates the code.
Next, click the Generate Code icon in the top toolbar of the editor. It looks like a gear. When you are changing configurations and update the canvas file, it may not automatically update the corresponding JavaScript file. Clicking this button forces it to do so.
Remember, however, our user code is not touched by the canvas compiler, so we’ll need to update part manually. If you look inside hand_button_2_prefab.js, it has updated generated code to use hand_button_2_prefab, but hand_button_1_prefab in our _onButtonPressed user code. So, you’ll want to update all occurrences of hand_button_1_prefab with hand_button_2_prefab in your user code. Then save the JavaScript file.
For hand button 3, follow the same steps for hand button 2 above, except you’d use the class name, hand_button_2_prefab.
Once you have all three hand button toggle button prefabs set up, you can add them to your game’s main canvas. Here are two quick ways to add a prefab to your canvas.
The first is:
Right-click on the canvas
Mouse over Prefab from the context menu
Choose Addโฆ from the secondary menu
Select one of the hand button prefabs from the list, and click OK.
Alternatively, you can use a very similar method:
Right-click on the canvas
Mouse over Addโฆ from the context menu
Choose Prefab from the secondary menu
Select one of the hand button prefabs from the list, and click OK.
Once you’ve done that, the prefab should appear on your canvas. You can then drag it to position it wherever you like.
Managing the Hand Buttons
Now that we have all the hand buttons created, we need a way to manage them.
As mentioned in the last article, we place a bet by selecting one or more hands, then pressing a bet chip button to place bets on those hands. But what happens if none of the hand buttons are selected when you press one of the bet chip buttons?
Well, we could provide some kind of “error” feedback, or simply not place the bet. But this might create a slight frustration for your end users. In my opinion, it would be better to prevent all three buttons from being turned off. So this implies two things:
One of the buttons must always be on by default (it can be any button you want, but for our purpose, let’s have hand button 2 on by default; or
If only one hand button is on, disable that button, so that it cannot be turned off. If another button is then turned on, that button can then be re-enabled.
Yes, this is little more work on our part, but it creates a smoother user experience, in my opinion, of course.
Having a “manager” class that works with all the hand buttons would be very helpful here, so let’s create that class:
HandButtons = function(gameCanvas) {
//this is the game canvas that you pass in when you construct your `HandButtons` object
this.gameCanvas = gameCanvas;
//here's another key-value object that references all three hand buttons by their id
//key: string (button id)
//data: HandButton
this._handButtonsById = { };
this._init();
};
There is an _init function that sets up all three hand buttons, and it looks like this:
HandButtons.prototype._init = function() {
//setup the three hand buttons
this._initHandButton(Thunderjack.PlayerHandIds.LOWER_LEFT, this.gameCanvas.fHand_button_1);
this._initHandButton(Thunderjack.PlayerHandIds.LOWER_MIDDLE, this.gameCanvas.fHand_button_2, true);
this._initHandButton(Thunderjack.PlayerHandIds.LOWER_RIGHT, this.gameCanvas.fHand_button_3);
//enable/disable buttons as necessary
this._updateButtons();
};
Each hand button is setup in an _initHandButton function, as in:
We’re creating a HandButton object from the class we created here. The toggle button will will be turned on if b_isOn is true.
The click callback, which is a Phaser Signal, is assigned the method, this._onHandButtonPressed, which we’ll look at later in this article. Finally, the button is added to the collection of buttons, indexed by the player hand ID.
There’s another function being called in the _init function, and that’s _updateButtons:
HandButtons.prototype._updateButtons = function() {
var button = this._getLoneOnButton();
if (button !== null) {
button.setEnabled(false);
} else {
this._enableAllButtons();
}
};
Earlier in this article, we discussed disabling a button if it’s the only one on. After the buttons are first set up, and every time the state of the buttons changes, we want to call _updateButtons. In this function, there is a_getLoneOnButton function. I won’t go into details on it, but returns a button if that button is the only one selected. Otherwise, it function returns null.
Finally, from this setup of the buttons, there is the click callback function, _onHandButtonPressed:
You’ll see that _onHandButtonPressed accepts a single parameter, a string ID. This is the button ID, the same as the property that is being dispatched in the HandButton.prototype._onButtonCallback function we defined in the earlier article. Each time a toggle button is pressed (either turned on or off), we want to update all the buttons. If only one button is left on, that button will be disabled. This prevents the player from turning off all the buttons, which would create a problem when we set up the bet chip buttons in a later article.
That completes adding the functionality of the hand buttons. We can now select which hand (or hands) on which we want to place bets. Also, we made sure that at least one of these hand buttons is on at all times.
In the next article, we’ll set up the bet chip buttons, so we can place bets on our selected hands. Thanks, and talk to you soon.
In this article, we’re going to look at how you place your bets before starting a round of Thunderjack!.
You can play from one to three hands against the dealer, and each hand has its own individual bet.
We’ll accomplish this through the following steps:
Setup the hand buttons
Setup the bet chip buttons
Setup the bet button
Setup the clear button
There’s quite a bit to these four steps, so the first step will be covered in two articles.
1. Setup the hand buttons
First, we’ll start with the three hand buttons. Each of these buttons represents a player hand:
button 1 is for the left player;
button 2 is for the middle player; and
button 3 is for the right player
These are toggle buttons, meaning, like check boxes, you can press them to turn them on, off, and back. This means we will need two images for each button, one to represent each state.
Phaser does not have native support for a toggle button, nor does the Phaser Editor, so we’re going to create our own. We’ll do this by creating two buttons, one for the on state, and one for the off state, and switching their visibility depending whether or not the state is on or off.
Let’s start with button 1.
Phaser Editor manages your buttons (and all other UI you create with it) in a JavaScript file, by using its Canvas, which is a visual, WYSIWYG (what-you-see-is-what-you-get) game level/object editor.
On the Canvas of, you can create a prefab and include in it two buttons. Both buttons should be the same size and shape, and in the same position (at 0,0 in the prefab). For the button that will represent the on state, set its varName to on. Likewise, for the off button, set its varName to off.
Save the canvas UI as hand_button_1_prefab.js.
Important!
Every time you make changes to the UI elements and save the file, the Canvas compiles the UI and updates the JavaScript file corresponding to that UI. This file is the prefab class that represents all the UI on the canvas. Thus, you can only directly modify the file in certain areas, otherwise, your changes will be lost the next time Phaser Editor updates it. The editor adds comments to the code, indicating the areas you can safely modify without your edits being lost.
You may not directly edit code in between these comment lines. If you do, your changes will be overwritten by the canvas compiler the next time you save your .canvas file, which causes it to update the code.
However, you can so indirectly by using the User Code dialog.
There are two ways you can access this (both from the Canvas UI):
Right click on the Canvas UI and select Edit User Code from the menu; or
Use the keyboard shortcut Ctrl + Shift + U.
There are two text boxes in the User Code dialog where you can enter text. I recommend adding your own comment at the beginning of each of those, so that you can easily locate where your code was inserted by the Canvas when it rebuilds the JS file.
When you set a varName to a UI object, like we did by naming our buttons “on” and “off”, the canvas compiler will generate attach those objects as properties of the prefab class:
// fields
this.fOn = _on;
this.fOff = _off;
You can reference them in your user code with this.fOn and this.fOff.
This works for any valid varName. For example, if you assigned a varName of myTextBox to a UI object on the canvas. The compiler will assign it to this.fMyTextBox.
You may find yourself needing to add user code for things like custom initialization or responding to button callbacks.
If you’d like more information on the Canvas Editor, please see here. For more on User Code, see here.
We also want to be notified when either the on or off button is pressed, so that we can switch buttons. We do this by adding a callback function to each button.
To do this:
Click on a button on the Canvas.
The properties for the button will appear in the properties panel. Look for “Button”.
In the “callback” field, add this._onButtonPressed (You can name the button whatever you want, but be sure to precede it with this.).
In the “callbackContext” field, add this.
Follow the above steps for the other button as well.
In user code, add the following button click callback function:
You’ll notice a this.clickCallback that we haven’t seen before. We’re going to use this as a Phaser.Signal. The signal will allow us to be notified from another location (which we’ll setup later) that a hand button has been clicked. Let’s create a reference to the click callback and set it to null in the middle section of the user code:
this.clickCallback = null;
Why we’re setting up to be notified from another location
I’m a firm believer that the UI code should be separated from the logic code as much as possible. Sure, we could put all the code in this hand_button_1_prefab class, but it would a jumble of the UI code (which what the Canvas compiler uses), and our logic code. I’d rather put the logic code into another file, and leave the prefab files to handle the UI and user code (which should also be kept to a minimum as much as possible, in my opinion).
You can create a new class, for example, HandButton.js, and place it in the WebContent/js folder of your project. The constructor might look something like this:
HandButton = function(s_handButtonId, handButtonPrefab) {
this._id = s_handButtonId;
this._handButtonPrefab = handButtonPrefab;
//here's the same clickCallback we setup in the user code of the hand_button_1_prefab canvas.
this._handButtonPrefab.clickCallback = new Phaser.Signal();
this._handButtonPrefab.clickCallback.add(this._onButtonCallback, this);
this._isOn = false;
this.callbackSignal = new Phaser.Signal();
};
The other two hand buttons will eventually use this HandButton class, so this is not exclusive to hand button 1.
You’ll see a this._onButtonCallback referenced when setting up the signal. That’s the function that the signal will call when it’s dispatched in the button prefab user code. (See this.clickCallback.dispatch(this); in the hand_button_1_prefab.prototype._onButtonPressed function written previously.)
What this does is toggle the _isOn property between true (on) and false (off) each time the hand toggle button is clicked. We also have yet another signal, this.callbackSignal, which passes notification onto the main logic of the game that the button has been pressed. Another reason that we setup the first this._handButtonPrefab.clickCallback is so that we can provide the UI function of toggling between the button states. We’ll handle this callbackSignal at a later time.
Next, we’ll handle the functionality that updates the current state of the toggle button.
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:
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.
The one player with a Thunderjack is payed out immediately.
That player also does not play against the dealer.
If the dealer gets a Blackjack, the player is still paid the Thunderjack payout. The result is not a push.
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:
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:
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.
We have more updates available for the HTML5 version of Thunderjack! using Phaser.
This time, we’re covering what happens when one or more players or the dealer gets a Blackjack.
As you may be aware, when a player gets a Blackjack on the opening hand, they are immediately paid, and do not play against the dealer. When the game first starts players who do have Blackjack will have their turns skipped, and the game starts with the first non-Blackjack player. If all players get a Blackjack, then the game ends.
If the dealer gets a Blackjack, then the game ends immediately. If any players also have a Blackjack, their result is a Push (the player gets their original bet back). All non-Blackjack players lose their bets.
Let’s get started on the logic for this might look like.
After all the cards are dealt, here is the basic rundown of the logic:
Calculate the scores of all players and dealer
Determine which hands, if any, have Blackjacks
If the dealer has a blackjack:
For each player that has a Blackjack, result is a push
End game
If the dealer does not have a Blackjack:
For each player that has a Blackjack, is paid immediately, and does not play against the dealer
Game starts with the first non-Blackjack player taking their turn.
We’ll tackle those one at a time.
1) Calculate the scores of all players and dealer
Every time one or more player’s or dealer’s hands change cards, we need to calculate how many points they have. As I hinted in this post, we have a Thunderjack.HandData class
Because PlayerHandData inherits from HandData, it here we’re interested in the isPush property. The dealer can never push, which is why this property is nor defined in HandData.
We got into dealing the cards in this post. After the cards are dealt to a player, their cardsData property will contain the array of Thunderjack.CardData objects. The class looks like:
Now let’s wrote some code that figures out the players’ hand scores based on their cards. For the most part, it’s just adding up the values, but things are a little tricky with the ace, since it can be one or eleven.
CalcScore
Thunderjack.CalcScore.ACE_POINTS_REMAINING = 10;
Thunderjack.CalcScore.Eval = function (cardsData) {
var points = 0; var containsAnAce = false;
for (var index = 0; index < cardsData.length; index++) {
var cardData = cardsData[index];
cardPoints = Thunderjack.CardPoints[cardData.value];
if (cardData .value === Thunderjack.CardValues.ACE) {
containsAnAce = true;
}
points += cardPoints;
}
if (containsAnAce) {
var proposedValue = points + Thunderjack.CalcScore.ACE_POINTS_REMAINING;
if (proposedValue <= Thunderjack.DesignData.BLACKJACK_POINTS) {
points = proposedValue;
}
}
return(points);
};
We’d call the Thunderjack.CalcScore.Eval function and send it the cardsData property of the hand we’re interested in.
We get the points of an individual card based on its value, then look it up in the Thunderjack.CardPoints object. For aces, we always add 1 point (for now), and we note that we’ve encountered at least one ace with the containsAnAce boolean.
If the hand contains an ace, we know that we can add the remaining 10 points of the high value of the ace to the points, as long as the results doesn’t exceed 21 (Thunderjack.DesignData.BLACKJACK_POINTS).
We eventually stored the returned result in the player’s hand. After all the cards have been dealt, we have these functions:
updateScores
function updateScores() {
for each player in the game {
player.handData.score = Thunderjack.CalcScore.Eval(player.handData.cardsData);
}
dealer.handData.score = Thunderjack.CalcScore.Eval(dealer.handData.cardsData);
}
Now the have the scores of all the players and dealer, we can evaluate their opening hands.
2) Determine which hands, if any, have Blackjacks. Here’s a quick pseudocode of the logic.
Let’s start with determining if has a hand has a blackjack.
hasBlackjack
function hasBlackjack(player) {
return (player.handData.score === Thunderjack.DesignData.BLACKJACK_POINTS);
}
As long as hasBlackjack is called after the scores have been update, the results will be accurate.
3) If the dealer has a blackjack: 3a) For each player that has a Blackjack, result is a push 3b) End game
We have a function specific to when the dealer has a Blackjack:
handleDealerBlackjack
function handleDealerBlackjack() {
for each player in the game {
if hasBlackjack(player) {
player.handData.isPush = true;
}
}
endRound();
}
Note that when the player has a Blackjack, we did not set the hasBlackjack property to true. Th is because the results is a push, even though the player got a Blackjack on their opening hand, they’re not being paid a Blackjack.
4 If the dealer does not have a Blackjack: 4a) For each player that has a Blackjack, is paid immediately, and does not play against the dealer 4b) Game starts with the first non-Blackjack player taking their turn
However, if the dealer does not have a Blackjack, we have code that handles below:
checkIfPlayersHaveBlackjacks
function checkIfPlayersHaveBlackjacks() {
var bjHands = []
for each player in the game {
if hasBlackjack(player) {
player.handData.hasBlackjack = true
add player to bjHands array
}
}
return bjHands
}
setFirstTurnPlayer
function setFirstTurnPlayer() {
var turnPlayerIndex = getNextTurnPlayerIndex(0);
if (turnPlayerIndex !== -1) {
setTurnPlayer(turnPlayerIndex);
} else {
endRound();
}
}
We’ve a some new function with setFirstTurnPlayer. There is a specific order in which the players take their turns. I’ve defined it as:
Right player
Middle player
Left player
Dealer
These are stored in an array called turnPlayerIds, and it will store the values we defined in Thunderjack.PlayerHandIds. These IDs were introduced in this article.
(The “upper” hands are used for splitting, which we’ll go over in a later article.)
The first turn player will be the one who is eligible to play against the dealer. This is the player who did not get a Blackjack on their opening hand.
getNextTurnPlayerIndex
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 playerHandsData variable is an object that can store the player hands, indexed by their player IDs.
The isPlayingVsDealer function is what we can use to determine if a player will play vs the dealer. It looks like:
isPlayingVsDealer
function isPlayingVsDealer(handData) {
return(!
(playerHandData.hasBlackjack ||
playerHandData.isPush));
};
If a player does not have a Blackjack or a push, they will play against the dealer. If there is no player to play against the dealer (getNextTurnPlayerIndex would return -1), then the game would end.
That’s it for this post. We covered quite a bit in these few steps, but most of the opening hand logic is done. In the next article, we’ll add my special feature to this game – winning a hand via Thunderjack! Stay tuned, and take care.
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.
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.
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:
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:
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!
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 lastCardData 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!
Hey there, and welcome back for another update on the HTML5 Phaser version of Thunderjack!.
The project is starting to get interesting, because most of the UI is done, and I have begun working on the game mechanics and logic.
Updates include:
Hit and Stand functions are complete.
The dealer now plays (dealer AI) if there are any eligible players remaining.
At the end of the round, winners are determined by comparing hands.
A new round can now be played.
Let’s go over each of these in a little more detail.
1. Hit and Stand functions are complete
When taking a hit, the bust functionality is now complete. If a player’s score exceeds 21, the player will bust, and the Bust indicator will appear. The game then moves to the next available player.
Likewise, the player can stand. If they do so, the game then moves to the next available player.
A few notes on available players.
Thunderjack! Will allow you to place bets on one to three players. Since splitting is supported in this game, it is possible for you to split the hands to play a total of six hands in a round.
I assigned a โplayer IDโ to each of the six players. This ID is a string that is used when referencing any of the player data, such as:
cards in hand
score
status (bust, blackjack, Thunderjack, etc.)
bet amount
The player ID is also used to reference the graphical UI for each player. Things like:
The ID, PlayerHandIds.DEALER, is very similar to the six players, except that it does not use betting information, and it cannot split or double-down.
The ID, PlayerHandIds.DECK, is used when moving cards back into the deck (for reshuffling)
The ID, PlayerHandIds.DISCARD_DECK, is used at the end of each round when all players place all the cards in the hand into the discarded deck.
2. The dealer now plays (dealer AI) if there are any eligible players remaining
The logic for the dealer is simple. If if has less than seventeen points, it will take a Hit. Otherwise, it will stay. Also, the dealer does not double-down or surrender.
The dealer will play its hand only if there are any eligible players remaining to play against it.
An eligible player is a player who:
did not bust
did not get a blackjack, Thunderjack, or blitz
did not surrender
If there are no eligible players, the round ends, and the dealer does not play.
3. At the end of the round, eligible players now play against the dealer
When all players and the dealer have completed their turns, the hands of eligible players are compared against the dealer’s to determine winners.
4. A new round can now be played
After the round has ended, you can play a new round with the same bets, or you can set new bets on your hands. When you start a new game, all cards in the players’ and dealer’s hands are moved to the discard deck, then players are dealt new hands from the deck.
Have a look at this video to see the current progress:
Following Updates
As you can see, the game is coming along nicely. You may have noticed a few other details I didn’t mention, such as showing how much player’s won, or the credits in the upper-left corner of the screen are now being deducted when starting a game. Or, maybe you noticed that the credits didn’t increase when players win!
Ha! No, I’m not trying to put the shit on anybody; I just haven’t gotten to that part yet.
But let me go ahead and state some of the updates to come in the game:
Credits increase when players win.
Finish showing all the result indicators. Bust and Win show, but the others, like Blackjack or Push, show. Which brings me to the next to-do items.
Complete logic for Blackjack for player. If a player gets a Blackjack, that player’s turn immediately ends, he/she receives an immediate payout, and it becomes the next eligible player’s turn. A player who gets a blackjack becomes ineligible to play against the dealer.
Complete logic Blackjack for dealer. If the dealer gets a Blackjack, the game immediately ends and and player hands are compared against the dealer. Any player who has a blackjack results in a push. Players who get a Thunderjack! Will actually beat the dealer (this will be implement a bit later on). Otherwise, all other players don’t win the round.
If a player’s hand results in a push, they get their original bet back.
As cards are added to or removed from the deck, visually update it’s deck accordingly.
As cards are added to or removed from the discard pile, visually update it’s deck accordingly.
If too many cards have been used from the deck, before dealing the next wave of cards to the player and dealer, all discarded cards will be returned to the deck, the deck will be shuffled.
If you like where this is going, please leave me a comment saying what you like. Of course, if you don’t like it, that’s cool, too. (:
If you want be to build a Blackjack-style type of game for you (or a breakout style game like this or that), then please contact me here, contact me directly at cartrell@gameplaycoder.com, or look me up on LinkedIn.
That’s all I have for you for now. Thanks, and stay tuned for the next update.