Hand and Outcome Classes¶
This chapter introduces the hand of cards used by player and dealer.
It wrestles with the problems of scoring the value
of the hand in the presence of soft and hard values for aces.
It also introduces a subclass of the Outcome
class that
can handle the more complex evaluation rules for Blackjack.
In Hand Analysis we’ll look at a number of issues related to Blackjack hands. In Payout Odds we’ll look at how the odds depend on the cards and the way the hands are resolved. In Hard and Soft Totals we’ll examine how an Ace leads to a hand having distinct hard and soft totals. This will cause us to rexamine the nature of outcomes; this is the subject of Blackjack Outcomes.
We can then design the overall Hand
class.
This is described in Hand Class Design. We’ll look at all of the
deliverables for this chapter in Hand Deliverables.
Hand Analysis¶
The hand of cards is both a container for cards, but is also one dimension of the current state of the player’s playing strategy. In addition to the player’s hand, the other dimension to the strategy is the dealer’s up card.
A player may have multiple hands. The hands are resolved independently.
For each hand, a hand’s responsibilities include collecting the cards, producing a hard and soft point total and determining the appropriate payout odds.
Collecting cards is trivial. Each hand can be modeled as a bag or multiset of
Card
instances. A hand can’t be a Set
because there can easily be duplicate
cards in a multiple deck game. In an 8-deck game, there are 8 separate instances of A♠.
Determining the payout odds for a Hand
object is somewhat more complex.
Payout Odds¶
To be compatible with other games, we’ll be creating Bet
objects which
are associated with Outcome
instances. In Roulette, there were a profusion
of Outcome
instances, each with fixed odds. In Craps, there
were fewer Outcome
instances, some of which had odds that depended on a
the current Throw
object.
The player’s Hand
instance must be associated with an Outcome
object.
The core of the game simulation is matching the Outcome
object for a Bet
instance
and the Outcome
object of a Hand
instance to determine the payout.
In the case of Roulette, each Bin
instance was a set of winning Outcome
objects.
In the case of Craps, both the Throw
object and the game had a mixtures of winning, losing and
unresolved Outcome
object. The Bet
instances were associated with
Outcome
instances. The Wheel
, Dice
. or Game
objects gave us sets of winning (and losing) Outcome
instances.
In Blackjack, there are relatively few distinct outcomes. And, it’s not clear how each
Outcome
instance associates with a Hand
object.
Survey of Outcomes.
To figure out how to associate a Hand
object and a betting Outcome
object,
we’ll start by enumerating all the individual Outcome
instances in this game.
Note that the “Ante” bet, placed before any cards are seen, has three distinct outcomes.
Insurance. This outcome pays . This is offered when the up card is an Ace. This outcome is a winner when the dealer’s hand is blackjack. (Also, the “Ante” bet will be a loser.) This outcome a loser when the dealer’s hand is not blackjack. (The Ante bet is unresolved.)
Even Money. This outcome pays . This is offered in the rare case when the player’s hand is blackjack and the dealer’s up card is an Ace. If accepted, it can be looked at as a switch of the Ante bet from the original “Ante” outcome to a different “Even Money” outcome. After this change, the “Ante” bet is then resolved as a winner.
Ante paying . This variation occurs when the player’s hand is less than or equal to 21 and the dealer’s hand goes over 21. This payout also occurs when the player’s hand is less than or equal to 21 and also greater than the dealer’s hand. All “Ante” outcome variants are a loser as soon as the player’s hand goes over 21. They are also a loser when the player’s hand is less than or equal to 21 and also less than the dealer’s hand. The odds depend on both player and dealer’s hand.
Ante paying . This variation occurs when the player’s hand is blackjack. The odds depend on the player’s hand.
Ante resolved as a push, paying . This variation occurs when the player’s hand is less than or equal to 21 and equal to the dealer’s hand. The odds depend on both player and dealer’s hand.
Problem. What kind of class is the Hand
class?
Is it a collection of Outcome
instances?
Or is it something different?
It appears that the Hand
class, as a whole, is not simply associated with an Outcome
object.
It appears that a Hand
instance must produce an Outcome
object based on the hand’s
total, the dealer’s total, and possibly the state of the game.
This is a change from the way the Dice
class and the collection of Bin
isntances work.
The Bin
instances were directly (and immutably) associated with Outcome
objects.
A Blackjack Hand
, however, must do a bit of processing
to determine which Outcome
instance it represents.
A two-card hand totalling soft 21 produces a blackjack
Outcome
object that pays .All other hands produce an
Outcome
object that pays and could be resolved as a win, a loss, or a push.
Also, changes to the state of the game depend on the values of both
hands, as well as the visible up card in the dealer’s hand. This makes
the state of the hand part of the evolving state of the game, unlike the simple
RandomEvent
instances we saw in Roulette and Craps.
Forces.
We have a few ways we can deal with the Hand
class definition.
We can make the
Hand
class a subclass of theRandomEvent
class, even though it’s clearly more complex than other events.We can make the
Hand
class unique, unrelated to other games.
Hand is an “Event”?
While a Hand
object appears to be a subclass of the RandomEvent
class,
it jars our sensibilities. A Hand
object is built up from a
number of Card
instances. Dealing a Card
object seems more event-like.
One could rationalize calling a Hand
instance
an “event” by claiming that the Shoe
clss is the random event generator.
The act of shuffling is when the events are created.
The complex event is then revealed to the player and dealer one card at a time.
It seems that we need to define a Hand
class that shares a common
interface with the RandomEvent
class, but extends the basic concept
because a hand has an evolving state until it is fully revealed.
Hand is different.
While we can object to calling a Hand
instance a single “event”, it’s difficult
to locate a compelling reason for making the Hand
class into something
radically different from the Bin
or Dice
classes.
Hand Features.
Our first design decision, then, is to define the Hand
class as a kind of RandomEvent
subclass.
We’ll need to create several Outcome
instances that can be paired with the event: Insurance, Even Money, Ante and Blackjack.
A Hand
object will produce an appropriate Outcome
object based on the hand’s structure,
the game state, and the dealer’s hand. Generally, each Hand
object will produce a simple Ante outcome
as a winner or loser. Sometimes a Hand
object will produce a Blackjack outcome.
Sometimes the Player and Blackjack Game will collaborate to add an Insurance or Even Money Outcome
object to the Hand
.
Hard and Soft Totals¶
Our second design problem is to calculate the point value of the hand. Because of aces, hands can have two different point totals. If there are no aces, the total is hard. When there is an Ace, there are two totals: the hard total uses an Ace as 1, the soft total uses an Ace as 11.
We note that only one ace will participate in this hard total vs. soft total decision-making. If two aces contribute soft values, the hand is at least 22 points. Therefore, we need to note the presence of at most one ace to use the soft value of 11, all other cards will contribute their hard values to the hand’s total value.
The presence of an Ace means finding at most one card with a card.hardValue != card.sotfValue
.
A Hand
object has a two internal point totals:
Hard. All hands have a hard total. This is the total of the hard values of all of the
Card
instances.Soft. When a hand has at least one Ace, the soft total is computed as the hard total of all cards except the Ace, plus the soft value of the Ace.
The final point total for a hand, then, has two options. When the soft total when is 21 or less, the soft total applies. WHen the soft total is over 21, the hard total applies.
This leads us to a number of algorithms within the Hand
class to locate and isolate one card with different
hard and soft values.
Blackjack Outcomes¶
As a final design decision, we need to consider creating any
subclasses of the Outcome
class to handle the variable odds for the “Ante”
bet. We don’t need a subclass for the “Insurance” or “Even Money” outcomes,
because the base Outcome
class does everything we need.
The notable complication here is that there are three different odds. If the player’s hand beats the dealer’s hand and is blackjack, the odds are . If the player’s hand beats the dealer’s hand, but is not blackjack, the odds are . If the player’s hand equals the dealer’s hand, the result is a push; something like odds where you get your money back.
It doesn’t seem like new subclasses of the Outcome
class are necessary.
We simply have some alternative Outcome
instances that can be produced by a Hand
.
The player can only create one of four basic Bet
instances.
The “Ante” bet that starts play. This is assumed to be until game conditions change this to or a push. This is the essential
Outcome
object for the player’s primaryBet
instance.The “Insurance” and “Event Money” bets. One of these may also be active after the first cards are dealt.
For the insurance
Outcome
object to be active, the dealer must be showing an Ace and the player’s hand is not 21. The player is offered and accepts by creating an insuranceBet
object.For even money
Outcome
object to be active, the dealer must be showing an Ace and the player’s hand is 21. The player is offered and accepts by creating an even moneyBet
object.These are resolved before further cards are dealt. If the dealer does not have 21, these bets are lost. If the dealer has 21, these bets win, but the Ante bet is a loss.
The “Double Down” bet. This is generally offered at any time. It can be looked at as an an additional amount added to the “ante” bet and a modification to game play. The player creates this
Bet
object, changing the course of play: only a single card can be dealt when this bet is made.
Objects.
It seems simplest to create a few common Outcome
instances: Ante, Insurance, Even Money
and Double Down.
The Table object will then have to combine a Double-Down Bet
object’s amount
into the Ante Bet
object’s amount.
Hand Class Design¶
-
class
Hand
¶ Hand
contains a collection of individualCard
instances, and determines the two point values for the hand.
Constructors¶
Methods¶
-
Hand.
hard
(self) → int¶ Returns the hard total of all cards in the hand.
-
Hand.
soft
(self) → int¶ Does the soft-total computation.
Partition the cards into two collections:
Ace. This collection has at most one Ace. For this card,
card.softValue != card.hardValue
. If there’s no card here, the value is zero. If there’s a card here, the value is the soft value of the one-and-onlyCard
instance.Non-Ace. This collection has all the cards except for the card in the Ace collection. If there is no Ace, this collection is all the cards. The value is the hard total of all cards.
Return the sum of the values for each collection, Ace plus Non-Ace.
-
Hand.
add
(self, card: Card) → None¶ - Parameters
card (
Card
) – A card to hadd
Add this card to the
Hand.cards
list.
-
Hand.
value
(self) → int¶ Computes the final total of this hand.
If there are any aces, and the soft total is 21 or less, this will be the soft total. If there are no aces, or the soft total is over 21, this will be the hard total.
-
Hand.
size
(self) → int¶ Returns the number of cards in the hand, the size of the
List
.
-
Hand.
blackjack
(self) → bool¶ Returns true if this hand has a size of two and a value of 21.
-
Hand.
busted
(self) → bool¶ Returns true if this hand a value over 21.
-
Hand.
__iter__
(self) → Iterator[Card]¶ Returns an iterator over the cards of the
List
.
-
Hand.
__str__
(self) → str¶ Displays the content of the hand as a String with all of the card names.
Hand Deliverables¶
There are six deliverables for this exercise.
The
HandTotal
class hierarchy:HandTotal
,HandHardTotal
,HandSoftTotal
.A unit test for each of these classes.
The
Hand
class.A class which performs a unit tests of the
Hand
class. The unit test should create several instances ofCard
,FaceCard
andAceCard
, and add these to instances ofHand
, to create various point totals.The
Card
andAceCard
modifications required to set the appropriate values in aHand
A set of unit tests for assembling a hand and changing the total object in use to correctly compute hard or soft totals for the hand.
Looking Forward¶
The Cards and Hands are essential elements for the game of Blackjack.
Following the pattern of previous games, the next chapter will look
at the implementation of a table to hold the player’s Bet
objects.
An interesting part of this is the a player’s hand can be split,
leading to multiple hands, each of which has multiple active bets.