Many of the examples in the book will center on simulations of a process with a number of moderately complex state changes. The card game of Blackjack involves a few rules and a few state changes during play. If you're unfamiliar with the game of Blackjack, here's an overview.
The objective of the game is to accept cards from the dealer to create a hand that has a point total that is between the dealer's total and twenty-one. The dealer's hand is only partially revealed, forcing the player to make a decision without knowing the dealer's total or the subsequent cards from the deck.
The number cards (2 to 10) have point values equal to the number. The face cards (Jack, Queen, and King) are worth 10 points. The Ace is worth either eleven points or one point. When using an ace as eleven points, the value of the hand is soft. When using an ace as one point, the value is hard.
A hand with an Ace and a seven, therefore, has a hard total of eight and a soft total of 18. This leads the player to choose to take extra cards. If the dealer is showing a face card, it's very likely the dealer is holding twenty points, and the player may not want to risk taking another card.
Each suit has four two-card combinations that total 21. These are all called Blackjack, even though only one of the four combinations involves a Jack. These combinations often provide a bonus payout, because there are only four of them available.
Most of the game is about proper choice of cards. There is, of course, a betting element. The distinction between playing and betting is made somewhat more complicated by the provision to split one hand into two hands. This is allowed when the player's two cards have the same rank. This option will be detailed in the next section on how the game is played.
The mechanics of play generally work as follows. The details can vary, but the outline is similar:
- First, the player and dealer each get two cards. The player, of course, knows the value of both of their cards. They're dealt face up in a casino.
- One of the dealer's cards is revealed to the player. It's displayed face up. The player, therefore, knows a little bit about the dealer's hand, but not everything. This is typical of more complex simulations where partial information is available and statistical modeling is required to make appropriate decisions.
- If the dealer has an Ace showing, the player is offered the opportunity to place an additional insurance bet. This is a special case, and is typical of more complex simulations where there are exceptions.
- For the balance of the game, the player can elect to receive cards, or stop receiving cards. There are four choices available:
- The player can hit, which means take another card.
- They player can or stand or stand pat with the cards dealt.
- If the player's cards match, the hand can be split. This entails an additional bet, and the two hands are played separately.
- The player can double their bet before taking one last card. This is called doubling down.
The final evaluation of the hand works as follows:
- If the player went over 21, the hand is a bust, the player loses, and the dealer's face-down card is irrelevant. This provides an advantage to the dealer.
- If the player's total is 21 or under, then the dealer takes cards according to a simple, fixed rule. The dealer must hit a hand that totals less than 18; the dealer must stand on a hand that totals 18 or more.
- If the dealer goes bust, the player wins.
- If both the dealer and player are 21 or under, the hands are compared. The higher total is the winner. In the event of a tie, the game is a push, neither a win nor a loss. If the player wins with 21, they win a larger payout, usually 1.5 times the bet.
The rules can vary quite a bit. We'll elide these details to focus on the Python code required for simulation.
In the case of blackjack, there are actually two kinds of strategies that the player must use:
- A strategy for deciding what play to make: take insurance, hit, stand, split, or double down.
- A strategy for deciding what amount to bet. A common statistical fallacy leads players to raise and lower their bets in an attempt to preserve their winnings and minimize their losses. These are interesting, stateful algorithms in spite of the underlying fallacies.
These two sets of strategies are, of course, prime examples of the Strategy design pattern.
We'll use elements of the game, such as the player, hand, and card, as examples for object modeling. We won't design the entire simulation. We'll focus on elements of this game because they have some nuance, but aren't terribly complex.
The cards are relatively simple, immutable objects. There are a variety of modeling techniques available. Cards fall into a simple class hierarchy of the number cards, face cards, and the Ace. There are simple containers, including hands of card instances, and decks of cards as well. These are stateful collections with cards being added and removed. There are a number of ways to implement this in Python and we'll look at many alternatives. We also need to look at the player as a whole. A player will have a sequence of hands, as well as a betting strategy and a Blackjack play strategy. This is a rather complex composite object.