Blackjack Summary
Welcome back to our Deep Dive series on building the casino game Blackjack in C# and Blazor WebAssembly!
- Poker tables and supplies, blackjack tables and accessories, craps tables, roulette wheels and tables, bingo cards and machines, baccarat tables, and layout, pai gow, name your game and we have it! American Gaming Supply offers a vast variety of gaming items which includes game tables and accessories for every popular casino game you can imagine.
- Description: Blackbelt in Blackjack is another classic book from blackjack legend Arnold Snyder. This is the book in which Snyder details some of his simple, but powerful, card counting methods. Those who are new to card counting or experienced counters who want to know more will love this book.
- Summary and Further Reading. Odds and probability in blackjack is a subject with endless ramifications. The most important concepts to understand are how to calculate probability, how to understand expected value, and how to quantify the house edge. Understanding the underlying probabilities in the game makes learning basic strategy and card.
In this second part, we're going to use the modeling ideas we wrote down in the previous post to build a complete C# model for a Blackjack game.
You might want to read the previous post in this series before reading this one. Here it is:
Also, there's a sample GitHub repository that has all of the code used in this series.
All caught up? Good! Let's jump right in.
Preview of BlackJack Summary: Full of mystery and romance, Black Jack is a suspenseful, action-filled story. Written in a descriptive style, the story includes authentic historical details that bring its eighteenth-century setting to life without ever becoming dull. Touches of ironic humor, aimed mostly at self-important people, enliven the story. These pioneers proved that blackjack offered the best odds of winning if players would follow a specific set of playing rules. Their work was published in 1956 in the Journal of the American Statistical Society, and then, a year later, in the book Playing Blackjack to Win. Although the Four Horsemen were not widely known by the general public.
Basic Enumerations
Let's begin our C# modeling with the most basic type we can model: enumerations for the card's suit and value.
We will deal with the score later in this post.
Playing Cards
The next-most-simple object we can model is the individual playing cards. You may recall from the previous post that we decided that the Card
object should have the following attributes:
- A suit
- A value
- A score
Suits and values are already defined as enumerations, and by making them properties of the Card
object we can calculate a score.
Visibility
We need to consider one additional situation for this model, and that is this: the dealer has one card that is not visible to the player. So what model should that property (visibility) be a part of?
There are several ways to answer this question; the one we are going with is that we will make a new property IsVisible
on the Card object. This property will be settable outside of the object so that the Dealer can mark their cards as not visible.
IsTenCard
Lastly, and as a convenience, we can define the property IsTenCard
to identify if the given Card
instance is a Ten, Jack, Queen, or King, and thus worth ten points.
Image URLs
We are implementing the display for our cards as individual images, with names like cardClubsJack.png, cardHeartsSix.png, cardSpadesAce.png, etc.
The images look like this:
Our Card object will need a property which stores the name of the image to display for that Card.
The Deck
The next object we can build is the deck of cards.
A Collection of Cards
The deck, at its core, is very simple: it's a collection of Card
objects. But what kind of collection?
One of the properties of a real-world deck of cards is that, when drawing a card, we always draw from the top of the deck. There is a collection class in .NET that implements similar functionality: the Stack<T>
class. Stack<T>
defines a Pop()
method, which removes and returns the 'topmost' object in the collection.
Our new CardDeck
object will need a property of type Stack<Card>
which stores the individual Card
objects.
Count, Add, and Draw
We must now consider a few properties. First, we need to keep track of how many cards remain in the deck; if the number of cards gets below a certain value, we need to reshuffle the deck.
As we decided in the previous post, we need methods to add cards to the deck and to draw a card from the deck. These methods are fairly straightforward to implement:
Initialization and Shuffling
Operation Blackjack Summary
The most complex part of the CardDeck
implementation is this: when we create a new instance of CardDeck
, we need to first populate the cards with the correct number and suits, and then shuffle the cards thoroughly.
Let's start by creating a constructor for our CardDeck
object, which first creates and inserts all of the cards needed.
We now need to extend this constructor to shuffle the cards. For this, we'll be using the venerable Fisher-Yates Shuffling Algorithm I blogged about a while back:
Here's that implementation:
Our CardDeck
object is now ready for use! We can move on to the more-complex objects, starting with Person
.
The Person Object
Recall from the last post that we need a common Person
object that both Player
and Dealer
will inherit from. That Person
needs the following abilities:
- Keep a hand of cards
- Use the hand to calculate a score
- Use the hand to determine if they are busted
The Visible Score Problem
First, though, we need to solve an unsolved problem from the previous post: how do we deal with the Dealer
object's visible score (e.g. the score from only the face-up cards)?
This is one of those places where the modeler (i.e. us) can make 'executive decisions'. There are a couple of ways to model the visible score, such as making it a property of the Dealer object, a property of the Person object, or an external method. For this post, I have chosen the second option: we will make a property VisibleScore
that is part of the root Person
object, since it is Person
that holds the property for Cards
. Please note that none of these options are inherently worse than the others, they just require different implementation details.
With all that in mind, we can build a Person
object with a set of properties to represent those abilities. Here's the skeleton object:
We now need to fill in the other properties.
True Scores and Visible Scores
The most complex thing the Person
object will do is calculate that person's current score. Because we want a property for the VisibleScore
and a property Score
for the true score, we will use a private method to calculate the score.
The algorithm to calculate the score goes like this:
- PASS IN a value that determines whether we calculate the visible score only, or the true score.
- IF the sum value of all cards is less than or equal to 21, return that sum.
- IF there are no Aces in the person's hand AND the sum is greater than 21, the person has bust, so return the score.
- IF there are Aces in the person's hand...
- WHILE there are Aces left that have not been converted
- CONVERT a single Ace to being worth 1 point.
- IF the score is now less than or equal to 21, return the score.
- END WHILE
- IF the score is STILL greater than 21, the person has bust, so return the score.
The resulting code looks something like this:
We can then make two properties; one for the true score, and one for the visible score.
Checking for Blackjack and Bust
In the previous post, we decided that we wanted a special display that normally displays the Person's score, but will also show when they get a blackjack and when they bust.
To do this, we first need a property that checks to see if the Person
has a blackjack. A person has a blackjack if;
- Their score is exactly 21 AND
- They have exactly two cards AND
- One of their cards is an Ace AND
- The other card is a ten-card
Here's the property for this:
We also want a convenience property that shows if the Person
has bust:
The Score Display
Using the properties HasNaturalBlackjack
and IsBusted
that we just defined, we can now create a property to display the Person
object's score. This property uses all of the properties of Person
that we have defined so far:
Convenience Methods - Adding a Card and Clearing the Hand
There are two convenience methods we still need, though your implementation may forego them.
First we need a straightforward method to add cards to the Person's hand:
Finally, we need a method to clear the Person
's hand:
With all of that, our root Person
object is complete! Now we can move on to the Dealer
and Player
objects.
The Dealer Object
Let's start with the Dealer object.
The Dealer
needs to inherit from the root Person
object:
From there, we can add the properties unique to the Dealer
The Card Deck
Let's start with a property for the CardDeck
object:
Dealing
The Dealer
object will also need three methods:
Operation Blackjack Summary
- A method that draws a card from the deck.
- A method that draws a card from the deck to give to the player.
- A method that draws a card from the deck to give to the dealer.
These methods end up being pretty straightforward:
Has an Ace Showing
We also need one convenience property that is used for the Insurance special play: whether or not the Dealer
has an Ace showing.
With that, our Dealer
is complete, and we can move on to implementing the Player
object.
The Player Object
The Player
object, like the Dealer
, needs to inherit from Person
.
Funds
The most distinguishing characteristic of the Player
is that they have funds, i.e. the money they walked up to the table with. Let's create a property for those funds, and set an initial amount:
Bets
We also need properties to keep track of the Player
's bets: their main bet they make before each hand, and the optional Insurance bet.
We will also want a convenience property to show whether or not the Player
has made an insurance bet:
The Change Amount
After each hand, the player's funds may change based on whether they won or lost the bet. Let's write a property that keeps track of this change amount.
Has Stood
Poker Summary
We need a convenience property to show whether or not the player has decided to stand:
Payouts
Finally, we need a method for the player to adjust their Funds
property based on whether they won or lost the bet after each hand.
Now our Player
object is complete and ready to gamble his/her chips!
Ada Blackjack Summary
21 Blackjack Summary
Summary
Phew! That was a lot of work. But now our implementation of Blackjack in C# is ready to go, and we can start building the Blazor components which will make up the display area of the game.
Blackjack Rules Summary
We can now build components for:
- The Player and Dealer hands
- The Player and Dealer scores (visible and total)
- The Player's funds and how they change after each hand
- The current bet
- The status of the Player (e.g. whether or not they have stood) AND
- The result of the hand (e.g. whether the Player wins or loses)
Implementing these components and more is exactly what we will do in the next part of this series. Stick around!
Do you have a way you can improve on my design? Submit a pull request or let me know in the comments! I am always looking for input from my dear readers.
Happy Coding!