From UBC Wiki

Blackjack Simulator

Authors: Benjamin, Lily, Yinchen

What is the problem?

Blackjack, or Twenty-One, is a hugely popular card game with many interesting strategies. In recent years, Blackjack has also become immensely popular online. We plan to implement a game engine which will allow the user to play the game interactively against a variable number of computer AI. To make the experience more realistic, we will develop AI to play Blackjack intelligently against their opponents through estimation methods using probability and the current game state. This will help users get better at the game and allow them to easily test new techniques. (See bottom of page for our modified Blackjack ruleset.)

What is the something extra?

To enhance usability and overall interest, we will develop a GUI for the game. It will display player information, such as one's current status in the round and amount of money, the center pot value, and a simple interface of buttons to trigger game actions. Additionally, to further enhance game experience, we will include a betting system. Betting will also be integrated into the AI's strategy.

What did we learn from doing this?

The Bottom-Line

We determined that although functional programming is useful for some aspects of virtual card game projects, the inability to define global variables and consequent awkward pattern-matching for data types lead to significant programming overhead and code repetition. Additionally, whilst researching GUI libraries to use in our project, we found that many of them seemed to be less feature rich and less intuitive to use than libraries such as Three.js or Qt (c++).

Specific Findings

A game of Blackjack requires that we keep track of many pieces of data including the current deck in play, stage of the game, and betting information. For each player, we also have to retain their current hand, money stash, round bet amount, and their status in the game (Fold, Stand, etc.). Because we chose to implement players as data types, we had to do significant pattern-matching in almost every function to reach important information stored within human and AI instances. This meant that a large amount of code was repeated to serve both player types. The game data type also had similar tradeoffs. Although Haskell allows record-style access for data types as an alternative, we found that we still weren't able to improve code repetition or remove unnecessary pattern-matching because we often needed the entire modified instance to be returned.

Another roadblock we faced was that we often needed to return multiple values. In Haskell, this meant deconstructing and reconstructing tuples many times. For both issues, we felt that global variables, pass-by-reference variables, and possibly even OOP-style classes would have been extremely beneficial. However, as a result of Haskell's powerful list features and recursive function definitions, we found Haskell to excel when implementing list operations and doing computations that were vital to the functionality of our game.

The Brick GUI library was relatively user-friendly and abstracted away many IO and overall game design elements by implementing a event-->handle-->draw game loop for us. This let us focus on writing pure functions. In this aspect, Haskell was successful. Our GUI design could certainly be extended to give a more professional look and possibly allow other game functionality, such as the ability to start a new game after the previous had ended.

Links to code etc.


Ruleset we developed:

At the start of a round, each player is dealt two cards. Their goal is have the highest score up to 21 points (scores based on traditional Blackjack card rules).

After the deal, each player has a single turn where they can ask for more cards until they:

  • bust (go over 21),
  • fold (give up on round), or
  • decide to stand (keep their current hand).

Betting is performed during one's turn. If a previous player decides to raise the bet, the next player must either match that bet, raise, or fold.

After each player has gone, if an earlier player has bet less than the current buy-in, then they must either match it or fold.

Once the round is over, the pot will be split among those with the highest score.

When the game is over, winners are determined to be those with the most money.