Creating A Multiplayer Poker Game For HTML5 Using Phaser, Part 4

Hey guys,

It’s been a minute since I’ve posted an update on the multiplayer poker game I’m working on, but it’s still on. While client work has kept me preoccupied for the most part, a lot of progress – and learning has been underway! 😎

Yeah, it’s been quiet around here… a little too quiet…

Ahem, without further ado, have a look at this video, which shows the WIP running in two different browser windows.

An interesting situation happened in this video. OF COURSE, the game would present it while I’m recording, haha! πŸ˜…

First off, there were four Straights. But that’s not even the most interesting part.

When more than one player wins, they all split the pot. Here, the pot was split four ways. It just happens to result in a fraction – each player winning (30 / 4 = 7.5) – which shouldn’t happen, because the lowest denominator I’m designing for is 1 chip – not partial chip. So I’ll need to correct that…

πŸ€”

Hmm… well, I could leave it, but that might give the impressions that you can do something with a partial chip value – which you can’t. Or, I could distribute the chips like this:

  • (2) players win 8
  • (2) players win 7

I might want to look this one up to see how real poker games handle this situation!

What are your thoughts on how this should be handled?

Primary Tools Used For The Project

The front end is powered by Phaser 3, the layouts and assets management by Phaser Editor 2D v3, and the back by Socket.IO v4.

Other technologies the project uses are:

The current state of the game’s progress

The poker rules logic is mostly done, so I’ve been spending a lot of time on the back-end logic.

In between rounds, you’ll be able to:

  • Join a game if you were watching as a spectator (if there are any open slots – max players is six).
  • Watch a game if you were playing (there is no limit to the number of spectators, but there is currently nothing that shows how many people are watching. I might make an πŸ‘ icon with a number next to it, and stick it in a corner or something.
  • Leave the game room and return to the lobby.

What I’m currently working on is handling those untimely situations of a player leaving a game mid-game, either because their connection died, or they closed their browser tab.

Leaving a game mid-game messes up the state, because the server and all the players need to be updated on who just left, so I’ll need to improvise here.

My original strategy was to treat it as the player folding, then when the round ends, automatically remove said player from the game. But this caused a problem, because if a player’s connection is lost, their socket is also gone. So, I decided to abruptly remove them from the game, forfeiting their hand.

Leaving mid-game also causes problems if it also happens to be the turn of the player who left. The decision here was to simply move to the next player’s turn.

Every game needs to have a dealer (D), a small blinds (SB), a big blinds (BB), and a host.

Note: The host is not part of poker – that’s a design for this particular game. By default, the host is the person who originally create the game room. The host is also responsible for starting each game when all the players are ready. The host is indicated by the ⭐ icon. When playing offline (yes, there is a single-player mode as well), there is no host.

So, if the D, SB, or BB leaves, then the next person (to their left) automatically assumes that role. It’s also perfectly normal for one player to have more than one role.

There’s also a lobby where players will go once they’ve connected to the server. Here you’ll be able to chat with other players, host your own game, or join an exiting game. Both hosting and joining are demonstrated in the video.

You can learn more about the project here:

Finally, if you’d like to get in when the first playable beta is ready before the game is released, sign up on my mailing lest here. You’ll also receive updates on other projects as I work on them.

The form you have selected does not exist.

I can’t wait to have this one finished. Definitely a challenge worth completing!

And I don’t care what anyone says. Coding a multi-player game is a LOT harder than it’s single-player counterpart. 🀨

– C. out.

Creating A Multiplayer Poker Game For HTML5 Using Phaser, Part 3

Continuing the series of building a multiplayer poker game, some progress has been made on the game. Development is coming along great using Phaser Editor v3!

A Bit More Technical Setup

I’m using several technologies, as mentioned in the first article of this series, and as I’m moving forward, I’m continuing to make adjustments to my development environment.

Since the game will perform as both a single-player and multiplayer game, I’m working on the single-player aspect first.

The game will be split into two primary projects: one for the client-side, and the other for the server-side.

Both will use common logic for running the game, and to avoid having to duplicate code across both projects. I made a secondary project, which is acts as a “common” repo for sharing code that will be used by both projects.

Since the common folder is outside of the client project root folder, I made come adjustments to VSCode to allow it to “see” this external folder and add it to the workspace.

The common folder is in the same folder as the client project (and server project). In order for Webpack to add the common files and folders to the build process, I made some adjustments to the webpack.config.js file. The alias inside of the resolve also makes the import statements look cleaner in VSCode.

module: {
  ...
  rules: [
    ...
    {
      include: [
        ...
        path.resolve(__dirname, '../common/src'),
      ],
      ...
    }
  ]
};
...
resolve: {
  ...
  alias: {
    common: path.resolve(__dirname, '../common/'),
  }
};

Learning Poker

I’m learning the rules of poker as I build this game, and the journey has been quite interesting. πŸ™‚

The order in which the players take their turns was fairly straightforward. Play starts with player left of the dealer, small blind (SB). The player left of the SB is the big blind (BB). Play goes clockwise around the poker table.

The object of poker is to win all the money in the pot. You do this by having the best poker hand at the end of the game. Your hand must be built from any combination of two cards that you’ll eventually be dealt (the “hole” cards), plus the five community cards that will eventually be dealt. As players make their turns, they can place bets by adding money to the pot. The player who wins the game (by having the best poker hand) at the end of the game wins all the money in the pot.

The game is split up into rounds and each round doesn’t end until every player has contributed an equal amount of money to the pot.

Note: I’ll be using 1/2 no-limit (NL) rules.

To start the game off, the SB posts a forced bet bet of 1bb, and the BB posts another forced bet of 2bb, twice the SB’s.

After both blinds make their bets, each player is dealt two face-down cards. (You can see your own cards, of course πŸ˜‰)

Image taken from Batman The Animated Series, Episode: “Almost Got ‘Im”.

When it’s the next player’s turn, if they wish to stay in the game, they must “call” by matching the BB’s bet of 2bb.

When play returns the the SB, they need only post 1bb, since they posted the initial 1bb star started the game. After this, all players still in the game (who haven’t quit, or folded) have posted an equal amount of 2bb.

After all players have placed equal bets the betting round comes to a close, and the first three of five community cards, the flop, are dealt, face-up in the center of the table.

The round begins again with the player nearest the left of the dealer. This would be the SB if They haven’t folded. Each players has the following moves:

  • Check: Pass to the next player without making a bet.
  • Bet: Increase the current bet by posting additional money into the pot. If a player does this, all other players must either match this bet, fold, or re-raise. More on re-raise later.
  • Fold: Forfeit their place in the current game. Their hand is surrendered face-down. Note that they can return to play the next game.
  • Call: If a previous raise has been made, the player can call by matching the previous bet.
  • All-In: If a player does not have enough money to match the current bet, and they wish to stay in the game, they must go “all in”, betting all their remaining chips. Note that if the player does not win this game, they will lose all their money and be out of the game!

Once the flop round is complete, the fourth community card is dealt, called the turn. Betting begins again with the player left of the dealer. And the round ends once all players have posted equal amounts to the pot.

Note: It is possible to no players to make any bets during a betting round by simple calling each time. But that would make for a slow and boring game, IMO. Maybe that’s the rookie talkin’, haha! πŸ˜†

Once the turn round is complete, the fifth and final community card is dealt, called the river.

(Image owned by Activision.)

Couldn’t resist.

Ahem! Anyway, betting begins again with the player left of the dealer. And the round ends once all players have posted equal amounts to the pot.

After this round ends, the showdown begins!

During the showdown, all players still in the game reveal their hands, and the player with the best hand wins the pot. If you’re curious, you can learn more about the poker hands here.

Re-Raising

Back to the concept of re-raising. This is when a player raises the bet, then another player raises again after them. Depending on the type of poker being played, the rules for determining the minimum (and maximum) amounts you can raise will vary. Since I’m coding a game using the rules by No-Limit, the maximum you can bet is all your available chips. However, the minimum you can bet can be a little trick to figure out, because it’s simply not just “double what the big blinds bet”.

I’m still working on this part, but I think I’ve got it (if not close πŸ˜…). Of course, if you’re an expert on calculating the minimum amount for re-raising in No-Limit 1/2 poker, please comment and correct any of my assertions. That would be greatly appreciated. πŸ‘πŸΎ

When re-raising against previous raise, the minimum amount must be at least the amount raised by the previous player. Take this example below.

Pot size is 6bb.
Player 1 bets 4bb.
Player 2 raises to 10bb.
What would be the minimum raise for Player 3?

First, let’s have a look at the wording used. Player 2 raised to 10bb. However, Player 2 actually increased the bet amount by 10 – 4 = 6bb. So, the minimum Player 3 will need to raise by is 6bb.

This would be a total raise size of:
10 (previous player’s total raise to) + 6 (minimum raise by) = 16bb.

Back To The Game Dev

As I close this article, here’s a screen shot of the current progress. I’m currently working on the raise (and re-raise!) implementation, as well as a few other functions, such as folding and going all-out.

After that, I’ll move on to the showdown functions and determine the winner among the various conditions.

That’s all for now. I’ll continue to keep you updated as I make progress.

Finally, you can sign up using the form below to receive updates to playable builds (or at least interactable builds) as I make noteworthy progress.

Talk to you later,

– C. out.

Creating A Multiplayer Poker Game For HTML5 Using Phaser, Part 2

Continuing the series of building a multiplayer poker game, I’ve decided to make a big turn in how the game will be built.

I’ve decided to give Arian’s latest version, Phaser Editor v3, a try.

Why? All my previous projects were deep in v1 or v2. However, I haven’t gotten very far into this Poker project, so it was easy to switch it from v2 to v3 sooner rather than later. Plus, after comparing its functionality with that of previous versions, it made sense to at least give it a try. And so far, it’s working out well!

Now, the biggest attraction, at least for me, was the fact that prefabs were back, which we haven’t seen since v1. Using factories in v2, while they took a bit of work to get them the way that I wanted, allowed me to learn a lot about how custom factories work in Phaser.

At the time of this article, v3 doesn’t yet supply an animations editor, or texture atlas editor. But that’s ok!

For animations, you can use a tool called Animator. You can learn more about animations in v3 here. I haven’t needed to use animations for my Poker game (at least not so far), so I can’t vouch for this tool. However, it is an option.

For texture atlases, you can use Texture Packer, which I’ve enjoyed using quite a bit over the years. To learn more about atlas editor usage in v3, as well as other options, check here.

Since this version now works in your browser (which also significantly reduces its download size), you start it up using your command prompt. So I made a simple batch file which starts the editor with the -desktop option since I work with a local server. My projects workspace is also in a location different than the default workspace, so I use the -ws option. For a full list of options, see this page.

As I’m learning the rules for Poker, the interactive tutorial on this page has been very helpful. Also, playing this game has also been useful. (No, I haven’t been screwing around, getting sucked into playing! Nope. Not me.)

I got a good chuckle from this scene from The Avengers.

That’s all for now. I’ll continue to keep you updated as I make progress.

Finally, sign up using the form below to receive updates to playable builds (or at least interactable builds) as I make noteworthy progress.

Talk to you later,

– C. out.

Creating A Multiplayer Poker Game For HTML5 Using Phaser, Part 1

Getting started with building a multiplayer Poker game, I’m first setting up the development environment.

After setting up webpack, ESLint, Babel, and Typescript (getting them to all work together and the way I want was a challenge), I’ve started on building a the game’s main display using Phaser Editor.

Building The Game Layout

I decided to go with allowing up to six players, and the layout will look like this:

Just a very simple layout here. Nothing fancy. #ImNotAGameDesigner

Phaser Editor supports Typescript, which is awesome. However, the code the Phaser Editor compiler generates doesn’t make ESLint happy.

As you can see here, we have some lines of code that ESLint is nit-picking us about:

There are sections in the code where you can put whatever you want, and it won’t be overwritten by the Phaser Editor compiler. In this case, I’d surround the generated code to temporarily disable ESLint.
/* eslint-disable */
and
/* eslint-enable */

NOTE: Since you cannot directly control the generated code, disabling ESLint is fine in these situations.

Since I’m using modules, at the bottom of Scene files generated by Phaser Editor, you can export the Scene class, by doing so at the bottom of the file. Code placed here will not be affected by Phaser Editor (the editor will mark the file with comments, telling you where you can write your own code). At the bottom, you can write something like this:

/* END OF COMPILED CODE */

// You can write more code here
export default [NameOfYourSceneClass];
Setting Up A Factory For The Card Images

Still building the layout, I want the card images to be set up as their own factory in Phaser. When I recently converted the Flash game Blasteroids to HTML5, I used JavaScript. However, for this project, I’m using TypeScript and the code is being linted by ESLint.

First let’s create a class that will display the card image. I won’t go into full details for this class, but the setup could look like this:

export default class CardImage extends Phaser.GameObjects.Image {
  constructor(scene: Phaser.Scene, x: number, y: number, texture: string,
    frame?: string | number) {
    super(scene, x, y, texture, frame);
    ...
  }
}

NOTE: You can then create properties to store the suit and value of the card, as well as whether or not the card is face up or face down. Note that if you change the face up/down property, you’d need to update the card’s image as well.

Next, let’s create a factory file. The name of my factory would be cardImage, and the looks something like this:

import CardImage from './CardImage';

Phaser.GameObjects.GameObjectFactory.register('cardImage',
  function foo(this: Phaser.GameObjects.GameObjectFactory,
    x: number, y: number, texture: string, frame?: string | integer): CardImage {
    const scene = this.scene;

    const sprite = new CardImage(scene, x, y, texture, frame);

    scene.sys.displayList.add(sprite);
    // scene.sys.updateList.add(sprite);

    return sprite;
  });

NOTE: The CardImage class extends Phaser.GameObjects.Image. If your class extends Phaser.GameObjects.Sprite, you’d also need to uncomment line 11 as well.

When setting card images in the scene to use cardImage as the factory, then saving the scene file, and viewing the source code, you’ll see there are some issues:

NOTE: I’ve spent a day or so trying to figure out how to resolve this issue. The scene class was originally a TypeScript, and I was originally using the TypeScript version of scenes in Phaser Editor. Unfortunately, I’ve had trouble getting namespaces and modules to work together. My research eventually led me to trying Declaration Merging, and looking at declaration files (.d.ts). And while setting up factories with TypeScript came closest (actually, this resource did work) to helping me solve my issue, I was still unable to resolve error ts(2339) by VS Code in the above image.

Long story short: I’m using JavaScript for scenes that use factories to avoid these issues.

After changing Phaser Editor to use JavaScript instead of TypeScript for this scene, VS Code no longer reports issues, and the code still compiles and runs with no issues! 😎

Now, I can continue to build the game’s main display, and code the functionality for dealing and flipping cards, presenting chips, and building the buttons, call, check, etc. There are still the game logic for the rules of Poker itself, as well as the multiplayer aspect of the game, which will be the game’s main draw (and an excellent experience for me in developing a turn-based, real-time multiplayer game).

That’s it for now!

If you’d like to get on my e-mail list and receive updates are the progress continues, sign up using the form below.

Talk to you later,

– C. out.

Starting On Multiplayer Poker Game For HTML5 Using Phaser

What’s up, guys. 🀜🏾

Now that the Flash game Blasteroids has been converted to HTML5, the next game I’m working on is a multiplayer Poker game.

Now, this won’t be a conversion, but a game originally built using Phaser 3, JavaScript, and Phaser Editor.

Building this game will be a bit of a challenge for me for these two reasons:

One, this will be my first attempt at developing a complete multiplayer, turn-based game.

An MP game is much more challenging to build than a single-player one, believe you-me. I once tried making a real-time MP in the past using PlayerIO, but… let’s just say that didn’t pan out so well.

And two, I’m learning a bit about the JavaScript ecosystem. I took previous course on Zenva called MMORPG Academy. The course showed you how to make a simple multiplayer MMO game using Phaser, Express and SocketIO. It also introduced me to the following tools, which I will also be using for this game.

Technology Used

Node.js

Node.js is a server-side platform built on Google Chrome’s V8 JavaScript Engine. It can be used for developing server-side and networking applications. I’ll use it as part of the back-end for this game.

npm

npm is a very large software repository. It’s a package manager for JavaScript, and you can use it to download and install many third-party packages into your projects. It comes with Node.js, and it can be used to install most of the tools listed below.

Express

This is a web framework that allows your web application to handle requests that are sent to a specific URL. Both Node.js and Express will be used together to create the web server.

Socket.IO

This is what will allow communication between the the player client app and the server web app.

webpack

Among many other things, webpack can bundle all your modular JavaScript code and packages together into a single file that can be understood by your web browser.

Babel

It’s much more convenient to write JS code in the latest/modern version. However, older versions of web browsers can’t understand these versions. This is where Babel comes in. It can transpile your modern code to earlier versions that can be understood those older browsers, increasing the available of your web apps to more people.

ESLint

This tool examines your code for syntactical errors, or other issues where the code may not adhere to a certain standard or set of styling rules. Essentially, “linting”, helps you write cleaner code.

Some other tools not discussed in the Zenva course that I will be using in Poker are:

TypeScript

I much prefer Typescript over JavaScript. Typescript allows you extra features, and for me, that means static typing and support for interfaces. Of course, I’m sure I’ll learn more as I dive deeper into Typescript.

Note: My plan is to write the code using TypeScript (which both Phaser and Phaser Editor also support), use Babel (with Typescript support) to transpile the code, and bundle it all together in a single JavaScript file using webpack. This is for the client-side portion of the game. The back-end server-side code will not be run in a web-browser, so it will not use this process.

Poker Game Multiplayer Development Plan

First thing’s first: the game’s design, and layout will need to be created. While I’m not a game designer (my service currently only includes programming), this version of Texas Holdem Poker I’ll be making will have a relatively simple enough mechanic. Nothing fancy here. And the game is mainly to add to my portfolio.

I’ll need to find some assets (graphics and sounds). I’ll use the same assets I used from my Thunderjack! game.

Because this is a multi-player game using a client-server model, where the server has the authority and maintains the game’s state.

The client apps (what the players will use) act as visual agents, showing the state of the game server. They also accept input (keyboard, mouse, touch, etc.) from each player, send them to the server, then the server with validate that input. The validation is to make sure the state stays clean, and to prevent cheating.

Because this will be a turn-based game and not a real-time game, it doesn’t need to rely heavily on advanced concepts like client-side prediction or entity interpolation and prediction.

Next, there are the rules of Poker. Because the server maintains the stage, all the game logic will be written on the server. And of course, I’ll need to get the rules down as well. πŸ™ƒ

And finally, I want the game to be able to use bots that can fill in if no other real players are available. So building an AI will be a challenge all its own, though I’m sure I can research this for help.

Where and How will I Host the Game Server?

The final component to this game is determining where and how I’ll host the game server, so you can play this game online with other real people. The client-side app will be hosted here on my site here, but it’s the game server that needs to be hosted elsewhere.

The multi-player game that the Zenva course helps you to create uses Heroku to host the game project, so I’ll use that again. The course covers just enough for you to set up a simple game, as any more, I believe, would go beyond the scope of the project, but there’s enough there to get started.

As I work on the game, I’ll post noteworthy updates in future articles. And you can play the work-in-progress versions of the game as I make playable updates, before the game is released to the public!

Sign up using this form to get on my mailing list:


Whew! That’s it for now, and this will be quite a challenge. I’m sure this will take me a while to complete. And I hope you stick with me on this journey! I got this! πŸ’ͺ🏾

– C. out.

Converting a Flash Game To HTML5

Hey guys,

I’m working on an interesting project now. A repeat client has requested that I convert a cards game I previously made for them from Flash to HTML5.

As you may be aware, Adobe is discontinuing their Flash player browser plugin by the end of 2020.

There really aren’t any true “conversion” tools out there. In other words, say you have a Flash project that consists of a few Flash FLA files with library assets in them, and maybe some exported to external classes, as well as additional external asses loaded at run-time, as well as external AS3 files. And your build process (what you use to generate your final SWF file) uses either Flash CSx/Animate or say, FlashDevelop (good tool, by the way – I highly recommend it if you’re using Adobe AIR to build mobile games).

There is no conversion tool that can just convert all that into a HTML + JavaScript files with all the assets extracted from FLA files, and present you with an HTML index file that you can simply run and be on your merry away.

That. Would. Be. Awesome.

If you want to convert a Flash game to HTML5, you pretty much have to learn JavaScript if you don’t already know it, and rebuild it from scratch).

Depending on the simplicity of your game, you may get some help from Adobe’s Flash Animate, but I’ve never tried it, and due to the complexity of the games I make, it’s likely to not work.

There are several other tools out there, your best bet may be to simple rebuild the code, but you can still use the assets.

However, if you have the original source code – the ActionScript 3 .AS files, sounds, and Flash .FLA files (which I do have because I wrote the original Flash project in the first place) – you already have everything you need to do this. Meaning, you don’t need to go through the trouble of decompiling SWF files and other dubious tasks.

Also, fortunately, the Flash game I’m converting doesn’t do any blitting of bitmaps. All graphics are either bitmaps imported into the FLA, or vector art saves as library symbols. The library symbols can be converted to bitmaps so they can then be used in the HTML5 version.

The biggest challenge for assets and layout is no longer having a super-convenient WSYISYG (what you see is what you get) editor like Flash CS/Animate. This allows you to place and size the objects in a scene as they should look when the game is running. Without this vital tool, you have a few options:

1). Hard-code all the sizes and positions of everything, and references to what everything looks like. (OUCH! This is not the old days of game development. I’m pretty sure we can come up with something quicker and easier, right? πŸ€” )

2). Use an editor, like Phaser Editor. A visual editor to build your layout, position and size objects on the scene. This editor has a built-in code Canvas Compiler that will translate your layout into JavaScript code, placing and sizing all your objects for you. You can then focus on building the layout. Now…

For the most part, converting AS3 to JavaScript files is fairly simple. Although it’s a laborious task of recoding, ActionScript 3 and JavaScript share many similarities, both having roots in the ECMAScript standards for example. Sure, you can’t just copy AS3 code, paste it into a JS file and call it a day, but if you know both languages well, the conversion process isn’t all that painful.

Here a quick example of an original source file written in AS3 for Flash, and below it, the JavaScript counterpart.

public final class EZ_PlayerData {
  private var mvo_dailyScores:Vector.<BJ_DailyScoresData>;
  private var mvo_cardsData:Vector.<EZ_PlayerCardsData>;
  private var mn_betValue:Number;
  private var mn_credits:Number;
  private var mu_selectedHandIndex:uint;
  private var mn_tournamentCredits:Number;
  private var mb_isTournamentEnabled:Boolean;
  
  ////////////////////////////////////////////////////////////////////
  // ctor
  public function EZ_PlayerData(u_maxHands:uint, u_maxCardsPerHand:uint) {
    super();
    
    mn_betValue = mn_credits = mn_tournamentCredits = 0;
    mvo_dailyScores = new Vector.<BJ_DailyScoresData>();
    
    mvo_cardsData = new Vector.<EZ_PlayerCardsData>(u_maxHands);
    for (var u_index:uint = 0; u_index < u_maxHands; u_index++) {
      mvo_cardsData[u_index] = new EZ_PlayerCardsData(u_index, u_maxCardsPerHand);
    }
  }
  
  ////////////////////////////////////////////////////////////////////
  // public functions
  
  //==================================================================
  // addDailyScore
  //==================================================================
  public function addDailyScore(o_score:BJ_DailyScoresData):void {
    if (!o_score) {
      return;
    }
    
    mvo_dailyScores.push(o_score);
    mvo_dailyScores.sort(onSortDailyScores);
  }
  
  //==================================================================
  // betValue (get)
  //==================================================================
  public function get betValue():Number {
    return(mn_betValue);
  }
  
  //==================================================================
  // betValue (set)
  //==================================================================
  public function set betValue(n_value:Number):void {
    if (!isNaN(n_value)) {
      mn_betValue = n_value;
    }
  }
  
  //==================================================================
  // cardsData (get)
  //==================================================================
  public function get cardsData():Vector.<EZ_PlayerCardsData> {
    return(mvo_cardsData);
  }
  
  //==================================================================
  // credits (get)
  //==================================================================
  public function get credits():Number {
    return(mn_credits);
  }
  
  //==================================================================
  // credits (set)
  //==================================================================
  public function set credits(n_value:Number):void {
    if (!isNaN(n_value)) {
      mn_credits = n_value;
    }
  }
  
  //==================================================================
  // getDailyScores
  //==================================================================
  public function getDailyScores():Vector.<BJ_DailyScoresData> {
    return(mvo_dailyScores);
  }
  
  //==================================================================
  // isTournamentEnabled (get)
  //==================================================================
  public function get isTournamentEnabled():Boolean {
    return(mb_isTournamentEnabled);
  }
  
  //==================================================================
  // isTournamentEnabled (set)
  //==================================================================
  public function set isTournamentEnabled(b_value:Boolean):void {
    mb_isTournamentEnabled = b_value;
  }
  
  //==================================================================
  // removeLastDailyScore
  //==================================================================
  public function removeLastDailyScore():BJ_DailyScoresData {
    return(mvo_dailyScores.pop());
  }
  
  //==================================================================
  // selectedHandIndex (get)
  //==================================================================
  public function get selectedHandIndex():uint {
    return(mu_selectedHandIndex);
  }
  
  //==================================================================
  // selectedHandIndex (set)
  //==================================================================
  public function set selectedHandIndex(u_value:uint):void {
    mu_selectedHandIndex = u_value;
  }
  
  //==================================================================
  // tournamentCredits (get)
  //==================================================================
  public function get tournamentCredits():Number {
    return(mn_tournamentCredits);
  }
  
  //==================================================================
  // tournamentCredits (set)
  //==================================================================
  public function set tournamentCredits(n_value:Number):void {
    if (!isNaN(n_value)) {
      mn_tournamentCredits = n_value;
    }
  }
  
  ////////////////////////////////////////////////////////////////////
  // private functions
  
  //==================================================================
  // onSortDailyScores
  //==================================================================
  private function onSortDailyScores(o_score1:BJ_DailyScoresData, o_score2:BJ_DailyScoresData):Number {
    if (o_score1.score >= o_score2.score) {
      return( -1);
    }
    
    return(1);
  }
}

And here is the corresponding JavaScript code:

(function(global) {
  global.Multiplay = global.Multiplay || {};
  Multiplay.PlayerData = Multiplay.PlayerData || {};
  
  //===========================================================================================
  //"public"
  
  //-------------------------------------------------------------------------------------------
  //ctor
  //-------------------------------------------------------------------------------------------
  Multiplay.PlayerData = function(u_maxHands, u_maxCardsPerHand) {
    this.dailyScores = [];
    this.betValue = 0;
    this.credits = 0;
    this.selectedHandIndex = 0;
    this.tournamentCredits = 0;
    this.isTournamentEnabled = false;
    
    this.cardsData = [];
    for (var u_index = 0; u_index < u_maxHands; u_index++) {
      this.cardsData.push(new Multiplay.PlayerCardsData(u_index, u_maxCardsPerHand));
    }
  };
  
  //-------------------------------------------------------------------------------------------
  //addDailyScore
  //-------------------------------------------------------------------------------------------
  Multiplay.PlayerData.prototype.addDailyScore = function(dailyScoreData) {
    if (!dailyScoreData) {
      return;
    }
    
    this.dailyScores.push(dailyScoreData);
    this.dailyScores.sort(this._onSortDailyScores);
  };
  
  //-------------------------------------------------------------------------------------------
  //removeLastDailyScore
  //-------------------------------------------------------------------------------------------
  Multiplay.PlayerData.prototype.removeLastDailyScore = function() {
    return(this.dailyScores.pop());
  };
  
  //===========================================================================================
  //"private"
  
  //-------------------------------------------------------------------------------------------
  //_onSortDailyScores
  //-------------------------------------------------------------------------------------------
  Multiplay.PlayerData.prototype._onSortDailyScores = function(score1, score2) {
    if (score1.score >= score2.score) {
      return( -1);
    }
    
    return(1);
  };
})(window);

AS3 takes the usual object oriented programming approach with classes, by defining an actual ‘class’, its constructor, and added properties and methods onto that class.

JavaScript uses protoypes, and a function of the same name as the “class” acts like a constructor. You then add functions onto the prototype which act like the class’s methods.

Due to the absence of public getter/setter properties, the JavaScript file turned out to be way shorter than the original ActionScript. However, you have to be careful when accessing members of the Multiplay.PlayerData prototype, that you don’t accidentally modify a member intended to be treated as private. In JavaScript, I precede such functions and members with an underscore ( _ ).

Throughout the conversion process, I highly recommend that you constantly test the code in small updates to ensure that the code is working as expected (much like you would when building any game, but especially here). I convert AS3 classes as few as possible at a time, then re-test for errors.

How Phaser Editor Helps

Other than the aforementioned benefits of using a WSYIWYG editor, Phaser Editor also allows you to write custom code in the same files that it creates with its canvas compiler.

In most cases, I prefer to keep the UI code separate from logic/business/implementation code, adapting some concepts from Model View Controller design pattern. This makes it easier for me to convert classes from AS3 to JS, since the UI and logic code will be implemented different in the two scripting languages.

That said, in the files generated by the Phaser Editor canvas compiler, I can add an “initialization” method call the editor’s User Code section, and from there, set up all the UI objects that were created on the canvas.

This one line is the only one I write in the Create tab of the User Code dialog. I prefer to keep code in here minimal and focus on write what the _init does in the user code section of the canvas JS file. Phaser Editor will reserve areas of the JS file as user code for you, and the code there will not be overwritten the next time you save the canvas layout (which will cause the canvas compiler to update the corresponding JS file, overwriting any unsaved changes – make sure you always save your JS file first before going saving new changes in the editor’s canvas!)

In the “user code here” section of the JS file (marked by Phaser Editor in a commented at the bottom of the file), I defined an empty _init function. This is where you can custom setup any UI objects, and we’ll be revisiting this later.

GameView.prototype._init = function () {
};
Converting a Button

Next, say I want to create (or “re-create”, as we’re rebuilding the original Flash layout πŸ˜‰ ) a button on the canvas named btnBet1 (from the casino cards game I’m converting for my client). Because I also have the field property set to true, the canvas compiler will create a property on the JavaScript file of the corresponding canvas called fBtnBet1. I also have the callback property set to a method on this instance of the canvas object this._onBet1Pressed. In my user code, I can define the _onBet1Pressed function. You’ll also want to add the button frames for over, out, down, and up so your button can respond to the various button states.

The canvas file is actually a Phaser state, and the canvas compiler will automatically generate the following code in the state’s create method:

var _btnBet1 = this.add.button(931.0, 753.0, 'some_atlas', this._onBet1Pressed, this, 'bet_up_1_over', 'bet_up_1_norm', 'bet_up_1_over', 'bet_up_1_over', _buttons);
...
this.fBtnBet1 = _btnBet1;

In the user code section of the JS file, I can create the button pressed callback function like this:

GameView.prototype._onBet1Pressed = function () {
};

It’s similar to a MouseEvent handler from ActionScript 3 when setting up a button to respond to mouse clicks.

Keeping the UI code and implementation code separate, I won’t include what the button actually does inside this method. Instead, I’ll have it dispatch a notification, via a Phase Signal. I treat the signal is treated as a public property of the canvas object, and I define the signal in the _init method created earlier:

GameView.prototype._init = function () {
  this.onBetButtonPressed = new Phaser.Signal();
};

When the button is pressed, I want that signal to fire a notification to be handled by the implementation code. Modifying the _onBet1Pressed function, it now looks like this:

GameView.prototype._onBet1Pressed = function () {
  this.onBetButtonPressed.dispatch();
};

There is no logic code in this canvas JS file, since it’s for UI only. The implementation is handled by the code that processes the game logic. First, it assigns a function to be handled by the signal when it dispatches a notification:

var gameView;
...
gameView.onBetButtonPressed.add(function() {
  //do whatever when the button is pressed
});

Instead of inlining the function, you can also pass in a function reference:

gameView.onBetButtonPressed.add(onBetBtnPressed);
...
function onBetBtnPressed() {
  //do whatever when the button is pressed
}

That’s pretty much how all the buttons will be converted.

Converting Fonts

Fonts is a big one. In Flash, you can embed fonts directly into the SWF file to make sure that the fonts will show up, even if the fonts aren’t installed on the end users’ machines. However, you can’t do this with HTML5. Instead, you have to either use web fonts something like this, or you can use bitmap fonts. Phaser Editor can not show all the fonts installed on your system. A quick excerpt from this page about fonts in Phaser Editor:

Phaser Editor uses JavaFX to render the scenes but Phaser uses the browser for the same purpose. This means that in some cases the text object is not rendered in design-time like Phaser renders it at run-time.

Note that Phaser Editor can use the fonts installed in the OS, but the majority of them are not available in all platforms, so we recommend to use safe fonts or load the font files in the CSS of your game.

I decided to go with using Bitmap fonts. Phaser Editor also supports bitmap fonts. This means, I have to create a texture atlas and corresponding XML for all the fonts I want to use. There are a few tools out there for this task:

For this project, I went with the Generator. While it’s not as powerful as Littera in terms of customizations, because it supports font formats other than only True Type. And this game does use some True Type fonts, and I want to be as faithful to the original implementation as possible.

A quick note on using the Generator. When it creates your text image atlas, you’ll also get a .FNT data file. This is the XML file that Phaser (and Phaser Editor) uses when setting up bitmap fonts. You can simply rename the extension from FNT to XML, and you should be good.

That’s the jist of what I’ve done, and so far, I’m not running into any major hurdles. It’s just a detailed process, and you should convert and test in small increments. I may write future updates to this if I encounter anything that warrants an article. I’m thinking timeline animations, but because I didn’t use any in this game, I don’t expect any issues there.

That’s all for now guys. Thanks, and I’ll talk to you next time. Take care!

– C. out.

Thunderjack! HTML5 Game Update: A Few More Adjustments

Hey, what’s up!

Ok, I can’t believe I uploaded the game without addressing these few functions! Ha! I guess I was so excited (:
Anyway, here’s what’s “new”:

  • You can no longer start a round without placing a bet (doh!)
  • Dealer’s second card is hidden until it’s the dealer’s turn (I had his hand fully revealed while testing and forgot to hide its second card)
  • Added self-promotion on intro screen (of course!)

I’ve also been exploring minifiying JavaScript files and combining them into one file, and found UglifyJS.

UglifyJS can be run as a command-line tool, and it can accept a list of all the files you want to combine. I tried combining the Phaser JS file, but was having problems. So I ended up combining only my own source files, as well as the files generated by the Phaser Editor canvas compiler.

On the page that hosts the game, it no longer loads a bunch of JS files (currently 44 files), but just two files: the Phaser source file, and a minified Thunderjack! source file.

You can play the game here.

That’s all for now guys. Take care.

– C. out.

Thunderjack! HTML5 Game Update: Added Logic for Blitz

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:

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

We’ll update that so that to include players who have not Blitzed are eligible to play against the dealer:

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

Finally, in the beginTurnPlayerHit function, after the player has Blitzed, the next player’s turn begins, with a call to the setNextTurnPlayer.

That does it for adding Blitz functionality in the game! This function required mostly a few modifications to the existing code.

Thanks for reading this article. We’ll talk to you next time. Take care.

– C. out.

Thunderjack! HTML5 Game Update: Recycling the Deck

Hey there,

Now that you’ve got most of the core gameplay completed, this article is about managing the deck of cards.

Each round of play draws cards from the deck. Just before each new round is started, all cards involved in play are moved to a discard pile. Cards in this pile cannot be used again until the deck is recycled.

Eventually, the deck will run out of cards. This is not good, especially considering it would happen in the middle of a game, while cards are being dealt. The process of recycling the deck starts after all the in-play cards have been discarded.

In the video, for the sake of brevity, the deck will recycled after one round. But in a real game, you’d want more cards to be played before the deck recycles. The discard pile is formed just to the left of the dealer’s hand. At the end of the round, after all the cards are discarded, they are then moved back into the deck. Afterwards, the deck is shuffled, and the next round begins.

But before you get into recycling the deck, let’s first go over discarding the cards.

Discarding the cards

At the end of each round, the game discards all players’ and dealer’s cards by moving them from their hands into the discard pile.

Just like the deck was defined as a DeckDataprototype, the discard pile can use the same prototype:

discardDeckData = new Thunderjack.DeckData();

The discard pile can be treated just like the deck, because cards will be added to and removed from it.

Note: The deck functions as a stack (LIFO – last in, first out), meaning that the cards added to the deck are removed in the reverse order in which they were added. The last card to be added (last in) is the first one to be removed (first out).

As mentioned, at the end of each round, all the cards in play are moved to the discard pile. The DeckData prototype currently does not have a way to add cards to it as needed, so here’s a function to add to the prototype to do that:

pushCardData
Thunderjack.DeckData.prototype.pushCardData = function(cardData) {
  this._cardsData.push(cardData);
};

When moving discarding all the cards, some pseudo code looks like this:

for each player in the game {
  discardPlayerCards (player)
}

discardPlayerCards (dealer) //don't forget the dealer
...
discardPlayerCards (player) {
  for each cardData in player's hand {
    remove cardData from hand
    add cardData to discardDeckData //using discardDeckData.pushCardData()
  }
}

Recycling the deck

Recycling is made of three steps:

1). Decide if the deck should be recycled. If so, proceed to step 2. Otherwise, begin the next round.
2). Move all cards from the discard pile back into the deck.
3). Shuffle the deck.

Once all the in-play cards have been discarded, we call this function:

handleDiscardCardsComplete
GamePlay.prototype._handleDiscardCardsComplete = function() {
  if (this._shouldRecycleDeck()) {
    this._recycleDeck();
  }
  
  this._beginRound();
};

Let’s start with the first step:

1). Decide if the deck should be recycled. If so, proceed to step 2. Otherwise, beGin the next round.

After all the cards have been discarded, you check the deck to see if it has few enough cards to be recycled. We’ll pick a hard number (you can make it a percentage of maximum number of cards in the deck).

The handleDiscardCardsComplete function should be called after all the cards have been discarded. It will first decide if the deck should be recycled. It so, it goes ahead wit hit. Otherwise, the next round begins.

shouldRecycleDeck
GamePlay.prototype._shouldRecycleDeck = function() {
  if (this._discardDeckData.getLength() === 0) {
    //shouldn't recycle deck if no cards have been discarded
    return(false);
  }
  
  var RECYCLE_DECK_THRESHOLD = 15;
  return(this._deckData.getLength() < RECYCLE_DECK_THRESHOLD);
};

This function determines if the deck should be recycled. The threshold is 15 cards. If the deck contains less than 15 cards, it should be recycled.

Note: Having a threshold of 15 cards is quite low, and can be risky, especially if you are playing three hands. Each hand can hold six cards max. That said, the max number of cards used in a game is 7 x 6 = 42 cards (three player hands with six cards each (3 x 6) + three split hands with six cards each (3 x 6) + one dealer hand of six cards (1 x 6). So, you have: 3 x 6 + 3 x 6 + 6 = 42.

It is unlikely that a single game would use this many cards, but there is the chance that the number of cards used in the game would completely empty the deck. You can reduce this from happening by increasing the threshold. A way to guarantee that it will never happen is to use more than one deck, and increase the threshold to 43 (not a typo: you want the deck to be recycled if there are less than 43; or you could change the comparison operator from < to <= and use 42 as the threshold).

Now you know if the deck should be recycled or not. (Let’s assume so, we you can move on to step 2! πŸ˜› )

2). Move all cards from the discard pile back into the deck.

Going back to the handleDiscardCardsComplete, we’re assuming that shouldRecycleDeck returned true. So we’re now start the recycle process with this function:

recycleDeck
GamePlay.prototype._recycleDeck = function() {
  //move all cards from the discard pile to the deck
  var cardData = this._discardDeckData.popCardData();
  while (cardData !== null) {
    this._deckData.pushCardData(cardData);
    cardData = this._discardDeckData.popCardData();
  }
};

This function starts by “popping” (removing) the last card from the discard pile and storing it in the cardData variable.

Next it starts a while loop. As long as there are cards in the discard pile, the loop will do the following:

  1. Add the card data to the deck
  2. Pop the next card from the discard pile.

After the last card has been removed from the discard pile and popCardData is called when there are no cards left, cardData will be set to null.

Remember that the functions being called on the deckData and discardDeckData have been defined on the DeckData prototype.

3). Shuffle the deck.

Finally, after all the cards have been moved back into the deck, it is shuffled by calling the shuffle function.

You can do this by adding one more line at the bottom of your recycleDeck function:

GamePlay.prototype._recycleDeck = function() {
  ...
  this._deckData.shuffle();
};

Recycling the deck is now complete, and the game begins with the next round.

If you have any questions for me about anything I’ve discussed so far, feel free to ask in the comments below.

Also, if you’re looking to hire a coder to build your next card game, get in touch with me by e-mailing me at cartrell@gameplaycoder.com.

That’s it for this post, guys. Thanks, and talk to you later.

– C. out.

Thunderjack! HTML5 Game Update: Splitting and Surrendering

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.

We’ve already covered the first three moves, hitting, standing and doubling down.

  • Hit
  • Stand
  • Double
  • Split
  • Surrender

Splitting

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):

  1. Right-click on the main game canvas, then select Add, then Button.
  2. From the Add Sprite dialog, select the image you want to use as the main button sprite, then press OK.
  3. This will add a button to your canvas. You can drag it to position it where ever you want.
  4. 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.
  5. Set a varName for the button. I decided to use the game_button_split.
  6. Set the callback to this._onSplitButtonPressed, and set the callbackContext to this.
  7. 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:

onSplitButtonPressed
GamePlay.prototype._onSplitButtonPressed = function() {
  this._beginTurnPlayerSplit();
};
beginTurnPlayerSplit
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:

Thunderjack.PlayerHandIds.LOWER_RIGHT = "lowerRight";
Thunderjack.PlayerHandIds.LOWER_MIDDLE = "lowerMiddle";
Thunderjack.PlayerHandIds.LOWER_LEFT = "lowerLeft";

Thunderjack.PlayerHandIds.UPPER_RIGHT = "upperRight";
Thunderjack.PlayerHandIds.UPPER_MIDDLE = "upperMiddle";
Thunderjack.PlayerHandIds.UPPER_LEFT = "upperLeft";

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:

Thunderjack.PlayerHandIds.LOWER_RIGHT ==> Thunderjack.PlayerHandIds.UPPER_RIGHT
Thunderjack.PlayerHandIds.LOWER_MIDDLE ==> Thunderjack.PlayerHandIds.UPPER_MIDDLE
Thunderjack.PlayerHandIds.LOWER_LEFT ==> Thunderjack.PlayerHandIds.UPPER_LEFT

An example of how the turnPlayerIds is modified is below.

Say, you have two players (left and right) and the dealer in the game. The turnPlayerIds would look like:

[
  Thunderjack.PlayerHandIds.LOWER_LEFT,
  Thunderjack.PlayerHandIds.LOWER_RIGHT,
  Thunderjack.PlayerHandIds.DEALER
]

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:

setSplitStatusForPlayers
GamePlay.prototype._setSplitStatusForPlayers = function(splitPlayerId) {
  var turnPlayerHandData = this._getTurnPlayerHandData();
  turnPlayerHandData.hasSplit = true;
  
  var splitPlayerHandData = getPlayerHand(splitPlayerId);
  splitPlayerHandData.hasSplit = true;
  splitPlayerHandData.betValue = turnPlayerHandData.betValue;
};

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:

PlayerHandData
PlayerHandData = function(s_handId) {
  HandData.call(this, s_handId);
  this.betValue = 0;
  this.hasThunderjack = false;
  this.isPush = false;
  this.hasBlitz = false;
  this.hasDoubled = false;
  this.hasSurrendered = false;
  this.hasSplit = false;
};

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.

showGameButtonsForPlayer
GamePlay.prototype._showGameButtonsForPlayer = function(playerHandData) {
  this.fGame_button_hit.visible = true;
  this.fGame_button_stand.visible = true;
  this.fGame_button_double.visible = this._isDoubleEligible(playerHandData);
  this.fGame_button_split.visible = this._isSplitEligible(playerHandData);
};

The isOpeningHand function has already been covered here.

canAffordSplit
GamePlay.prototype._canAffordSplit = function(playerHandData) {
  return(this._playerData.credits >= playerHandData.betValue);
};

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:

  1. Set the varName for the button to game_button_surrender.
  2. Set the callback to this._onSurrenderButtonPressed, and set the callbackContext to this.
  3. 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:

onSurrenderButtonPressed
GamePlay.prototype._onSurrenderButtonPressed = function() {
  this._beginTurnPlayerSurrender();
};
beginTurnPlayerSurrender
GamePlay.prototype._beginTurnPlayerSurrender = function() {
  var playerHandData = this._getTurnPlayerHandData();
  this._presentPlayerSurrender(playerHandData);
  this._setNextTurnPlayer();
};

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:

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

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.

– C. out.