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
Outcomeobject that pays
.All other hands produce an
Outcomeobject 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
Handclass a subclass of theRandomEventclass, even though it’s clearly more complex than other events.We can make the
Handclass 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
Cardinstances.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
Outcomeobject for the player’s primaryBetinstance.The “Insurance” and “Event Money” bets. One of these may also be active after the first cards are dealt.
For the insurance
Outcomeobject 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 insuranceBetobject.For even money
Outcomeobject 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 moneyBetobject.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
Betobject, 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¶ Handcontains a collection of individualCardinstances, 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-onlyCardinstance.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.cardslist.
-
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
HandTotalclass hierarchy:HandTotal,HandHardTotal,HandSoftTotal.A unit test for each of these classes.
The
Handclass.A class which performs a unit tests of the
Handclass. The unit test should create several instances ofCard,FaceCardandAceCard, and add these to instances ofHand, to create various point totals.The
CardandAceCardmodifications required to set the appropriate values in aHandA 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.