Blackjack Game Class

After reviewing the procedure provided in A Walkthrough, we’ll define the basic game class for Blackjack. This will require a stub class for a Blackjack Player. We’ll also revisit the fundamental relationship between Game, Hand and Player. We’ll invert our viewpoint from the Player containing a number of Hands to the Hands sharing a common Player.

Blackjack Game Overview

The sequence of operations in the game of Blackjack is quite complex. We can describe the game in either of two modes: as a sequential procedure or as a series of state changes.

  • A sequential description means that the state is identified by the step that is next in the sequence.
  • The state change description is what we used for Craps, see Craps Game. Each state definition included a set of methods that represented conditions that could change state; and each Throw object invoked one of those methods, in effect, announcing a condition that caused a state change.

Additionally, we need to look at the various collaborations of the Game. We also need to address the question of handling the dealer’s rules.

Maintaining State. The sequential description of state, where the current state is defined by the step that is next, is the default description of state in most programming languages. While it seems obvious beyond repeating, it is important to note that each statement in a method changes the state of the application; either by changing state of the object that contains the method, or invoking methods of other, collaborating objects. In the case of an active class, this description of state as next statement is adequate. In the case of a passive class, this description of state doesn’t work out well because passive classes have their state changed by collaborating objects. For passive objects, instance variables and state objects are a useful way to track state changes.

In the case of Roulette, the cycle of betting and the game procedure were a simple sequence of actions. In the case of Craps, however, the game was only loosely tied to each the cycle of betting and throwing the dice, making the game state a passive part of the cycle of play. In the case of Blackjack, the cycle of betting and game procedure are more like Roulette.

Most of a game of Blackjack is simply sequential in nature: the initial offers of even money, insurance and splitting the hands are optional steps that happen in a defined order. When filling the player’s Hands, there are some minor sub-states and state changes. Finally, when all of the player’s Hands are bust or standing pat, the dealer fills their hand. Finally, the hands are resolved with no more player intervention.

Most of the game appears to be a sequence of offers from the Game to the BlackjackPlayer; these are offers to place bets, or accept cards, or a combination of the two, for each of the player’s Hands.

Blackjack Collaboration

In Craps and Roulette, the Player was the primary collaborator with the Game. In Blackjack, however, focus shifts from the Player to the Hand. This changes the responsibilities of a BlackjackPlayer: the Hand can delegate certain offers to the BlackjackPlayer for a response. The BlackjackPlayer can become a plug-in strategy to the Hand, providing responses to offers of insurance, even money, splitting, doubling-down, hitting and standing pat. The BlackjackPlayer‘s response will change the state of the Hand. Some state changes involve getting a card, and others involve placing a bet, and some involve a combination of the two.

We’ll use the procedure definition in A Walkthrough. Following this procedure, we see the following methods that a Hand and a BlackjackPlayer will need to respond to the various offers from the BlackjackGame. The first portion of the game involves the BlackjackPlayer, the second portion invovles one or more Hand s. The collaboration is so intensive, we have created a kind of swimlane table, showing the operations each object must perform. This will allow us to expand Hand and BlackjackTable as well as define the interface for BlackjackPlayer.

Blackjack Overall Collaboration
BlackjackGame Hand BlackjackPlayer BlackjackTable
calls player’s placeBet   creates empty Hand, creates initial Ante Bet accepts the ante bet, associate with the hand
gets the initial hand; deal 2 cards to hand add cards returns the initial hand  
deal 2 cards to dealer’s hand add cards    
gets up card from dealer’s hand; if this card requires insurance, do the insurance procedure return up card    
iterate through all hands; is the given hand splittable? return true if two cards of the same rank returns a list iterator  
if the hand is splittable, offer a split bet get the player’s response; return it to the game to split: create a split bet, and an empty hand; return the new hand accept the split bet for the new hand
if splitting, move card and deal cards; loop back, looking for split offers take a card out; add a card    
iterate through all hands; if the hand is less than 21, do the fill-hand procedure   returns a list iterator  
while the dealer’s point value is 16 or less, deal another card return point value of the hand; add a card    
if the dealer busts, iterate through all hands resolving the ante as a winner return point value of the hand returns a list iterator resolve bet as winner and remove
if the dealer does not bust, iterate through all hands comparing against the dealer’s points, determining win, loss or push return point value of the hand returns a list iterator resolve bet and remove

There are a few common variation in this procedure for play. We’ll set them aside for now, but will visit them in Variant Game Rules.

Insurance. The insurance procedure involves additional interaction between Game and the the Player‘s initial Hand. The following is done only if the dealer is showing an ace.

Blackjack Insurance Collaboration
BlackjackGame Hand BlackjackPlayer BlackjackTable
if player’s hand is blackjack: offer even money return true if 2 cards, soft 21 to accept, return true  
if player accepted even money offer: change bet, resolve; end of game     update bet; resolve and remove bet
offer insurance   to accept, create new bet; return true accept insurance bet
if player accepted insurance offer: check dealer’s hand; if blackjack, insurance wins, ante loses, game over; otherwise insurance loses return point value   resolve and remove bet

Filling the Hands. The procedure for filling each Hand involves additional interaction between Game and the the Player‘s initial Hand. An Iterator used for perform the following procedure for each individual player Hand.

Blackjack Fill-Hand Collaboration
BlackjackGame Hand BlackjackPlayer BlackjackTable
if player’s hand is blackjack: resolve ante bet return true if 2 cards, soft 21   resolve and remove bet
while points less than 21, offer play options of double or hit; rejecting both offers is a stand. return point value; pass offers to player to double, increase the bet for this hand and return true; to hit, return true update bet
if over 21, the hand is a bust return point value   resolve the ante as a loss

There is some variation in this procedure for filling Hands. The most common variation only allows a double-down when the Hand has two cards.

Hand-specific Decisions. Some of the offers are directly to the BlackjackPlayer, while others require informing the BlackjackPlayer which of the player’s Hands is being referenced.

How do we identify a specific hand?

While the difference is minor, it seems slightly more sensible for the BlackjackGame to make offers directly to the BlackjackPlayer, including a reference to the relevant Hand.

Dealer Rules

In a sense, the dealer is a special player. They have a fixed set of rules for hitting and standing. They are not actually offered an insurance bet, nor can they split or double down.

However, the dealer does participate in the hand-filling phase of the game, deciding to hit or stand pat.

The dealer’s rules are quite simple. Should the Dealer be a special subclass of BlackjackPlayer; one that implements only the dealer’s rules?

Or should the Dealer be a feature of the Game. In this case, the Game would maintain the dealer’s Hand and execute the card-filling algorithm.

Using an subclass of BlackjackPlayer is an example of Very Large Hammer design pattern. We only want a few features of the BlackjackPlayer class.

Refactoring. To avoid over-engineering these, we could refactor player into two components. An object that handles hand-filling, and an object that handles betting strategies.

The dealer would only use the hand-filling component of a player.

Mutability. To avoid over-engineering this, we can look at features that are likely to change. The dealer hand-filling rules seem well-established throughout the industry.

Further, a change to the hand-filling rules of the dealer would change the nature of the game enough that we would be hard-pressed to call in Blackjack. A different hand-filling rule would constitute a new kind of game.

We’re confident, then, that the dealer’s hand can be a feature of the BlackjackGame class.

BlackjackPlayer Class

class BlackjackPlayer

BlackjackPlayer is a subclass of Player that responds to the various queries and interactions with the game of Blackjack.

Fields

BlackjackPlayer.hand
Some kind of List which contains the initial Hand and any split hands that may be created.

Constructors

BlackjackPlayer.__init__(self, table)
Parameter:table (BlackjackTable) – The table on which bets are placed
Uses the superclass to construct a basic Player. Uses the newGame() to create an empty List fot the hands.

Methods

BlackjackPlayer.newGame(self)
Creates a new, empty list in which to keep Hands.
BlackjackPlayer.placeBets(self)

Creates an empty Hand and adds it to the List of Hands.

Creates a new ante Bet. Updates the Table with this Bet on the initial Hand.

BlackjackPlayer.getFirstHand(self)
Returns the initial Hand. This is used by the pre-split parts of the Blackjack game, where the player only has a single Hand.
BlackjackPlayer.__iter__(self) → iter
Returns an iterator over the List of Hands this player is currently holding.
BlackjackPlayer.evenMoney(self, hand) → bool
Parameter:hand (Hand) – the hand which is offered even money
Returns true if this Player accepts the even money offer. The superclass always rejects this offer.
BlackjackPlayer.insurance(self, hand) → bool
Parameter:hand (Hand) – the hand which is offered insurance
Returns true if this Player accepts the insurance offer. In addition to returning true, the Player must also create the Insurance Bet and place it on the BlackjackTable. The superclass always rejects this offer.
BlackjackPlayer.split(self, hand) → Hand
Parameter:hand (Hand) – the hand which is offered an opportunity to split

If the hand has two cards of the same rank, it can be split. Different players will have different rules for determine if the hand should be split ot not.

If the player’s rules determine that it wil accepting the split offer for the given Hand, hand, then the player will

  1. Create a new Ante bet for this hand.
  2. Create a new one-card Hand from the given hand and return that new hand.

If the player’s rules determine that it will not accept the split offer, then None or null is returned.

If the hand is split, adding cards to each of the resulting hands is the responsibility of the Game. Each hand will be played out independently.

BlackjackPlayer.doubleDown(self, hand) → boolean
Parameter:hand (Hand) – the hand which is offered an opportunity to double down
Returns true if this Player accepts the double offer for this Hand. The Player must also update the Bet associated with this Hand. This superclass always rejects this offer.
BlackjackPlayer.hit(self, hand) → boolean
Parameter:hand (Hand) – the hand which is offered an opportunity to hit

Returns true if this Player accepts the hit offer for this Hand. The superclass accepts this offer if the hand is 16 or less, and rejects this offer if the hand is 17 more more. This mimics the dealer’s rules.

Failing to hit and failing to double down means the player is standing pat.

BlackjackPlayer.__str__(self) → str
Displays the current state of the player, and the various hands.

Card Rework

Card must provide the Game some information required to offer insurance bets.

We’ll need to add an offerInsurance() method on the class Card. The Card superclass must respond with False. This means that the FaceCard subclass will also respond with False.

The AceCard subclass, however, must respond with True to this method.

Hand Rework

Hand should retain some additional hand-specific information. Since some game allow resplitting of split hands, it’s helpful to record whether or not a player has declined or accepted the offer of a split.

Fields

Hand.player
Holds a reference to the Player who owns this hand. Each of the various offers from the Game are delegated to the Player.
Hand.splitDeclined
Set to true if split was declined for a splittable hand. Also set to true if the hand is not splittable. The split procedure will be done when all hands return true for split declined.

Methods

Hand.splittable(self) → bool
Returns true if this hand has a size of two and both Cards have the same rank. Also sets Hand.splitDeclined to true if the hand is not splittable.
Hand.getUpCard(self) → Card
Returns the first Card from the list of cards, the up card.

BlackjackGame Class

class BlackjackGame

BlackjackGame is a subclass of Game that manages the sequence of actions that define the game of Blackjack.

Note that a single cycle of play is one complete Blackjack game from the initial ante to the final resolution of all bets. Shuffling is implied before the first game and performed as needed.

Fields

BlackjackGame.shoe
This is the dealer’s Shoe with the available pool of cards.
BlackjackGame.dealer
This is the dealer’s Hand.

Constructors

BlackjackGame.__init__(self, shoe, table)
Parameters:
  • shoe (Shoe) – The dealer’s shoe, populated with the proper number of decks
  • table (BlackjackTable) – The table on which bets are placed
Constructs a new BlackjackGame, using a given Shoe for dealing Cards and a BlackjackTable for recording Bets that are associated with specific Hands.

Methods

BlackjackGame.cycle(self)

A single game of Blackjack. This steps through the following sequence of operations.

  1. Call BlackjackPlayer.newGame() to reset the player. Call BlackjackPlayer.getFirstHand() to get the initial, empty Hand. Call Hand.add() to deal two cards into the player’s initial hand.

  2. Reset the dealer’s hand and deal two cards.

  3. Call BlackjackGame.hand.getUpCard() to get the dealer’s up card. If this card returns true for the Card.offerInsurance(), then use the insurance() method.

    Only an instance fo the subclass AceCard will return true for offerInstance(). All other Card classes will return false.

  4. Iterate through all Hands, assuring that no hand it splittable, or split has been declined for all hands. If a hand is splittable and split has not previously been declined, call the Hand‘s split() method.

    If the split() method returns a new hand, deal an additional Card to the original hand and the new split hand.

  5. Iterate through all Hands calling the fillHand() method to check for blackjack, deal cards and check for a bust. This loop will finish with the hand either busted or standing pat.

  6. While the dealer’s hand value is 16 or less, deal another card. This loop will finish with the dealer either busted or standing pat.

  7. If the dealer’s hand value is bust, resolve all ante bets as winners. The OutcomeAnte should be able to do this evaluation for a given Hand compared against the dealer’s bust.

  8. Iterate through all hands with unresolved bets, and compare the hand total against the dealer’s total. The OutcomeAnte should be able to handle comparing the player’s hand and dealer’s total to determine the correct odds.

BlackjackGame.insurance(self)

Offers even money or insurance for a single game of blackjack. This steps through the following sequence of operations.

  1. Get the player’s BlackjackPlayer.getFirstHand(). Is it blackjack?

    If the player holds blackjack, then call BlackjackPlayer.evenMoney().

    If the even money offer is accepted, then move the ante bet to even money at 1:1. Resolve the bet as a winner. The bet will be removed, and the game will be over.

  2. Call BlackjackPlayer.insurance(). If insurance declined, this method is done.

  3. If insurance was accepted by the player, then check the dealer’s hand. Is it blackjack?

    If the dealer hold blackjack, the insurance bet is resolved as a winner, and the ante is a loser; the bets are removed and the game will be over.

    If the dealer does not have blackjack, the insurance bet is resolved as a loser, and the ante remains.

    If insurance was declined by the player, nothing is done.

BlackjackGame.fillHand(self, hand)
Parameter:hand (Hand) – the hand which is being filled

Fills one of the player’s hands in a single game of Blackjack. This steps through the following sequence of operations.

  1. While points are less than 21, call BlackjackPlayer.doubleDown() to offer doubling down. If accepted, deal one card, filling is done.

    If double down is declined, call BlackjackPlayer.hit() to offer a hit. If accepted, deal one card. If both double down and hit are declined, filling is done, the player is standing pat.

  2. If the points are over 21, the hand is bust, and is immediately resolved as a loser. The game is over.

BlackjackGame.__str__(self) → str
Displays the current state of the game, including the player, and the various hands.

Blackjack Game Deliverables

There are eight deliverables for this exercise.

  • The stub BlackjackPlayer class.
  • A class which performs a unit test of the BlackjackPlayer class. Since this player will mimic the dealer, hitting a 16 and standing on a 17, the unit test can provide a variety of Hand s and confirm which offers are accepted and rejected.
  • The revised Hand class.
  • A class which performs a unit tests of the Hand class. The unit test should create several instances of Card, FaceCard and AceCard, and add these to instances of Hand, to create various point totals. Since this version of Hand interacts with a BlackjackPlayer, additional offers of split, double, and hit can be made to the Hand.
  • The revised Card class.
  • Revised unit tests to exercise the Card.offerInsurance() method.
  • The revised BlackjackGame class.
  • A class which performs a unit tests of the BlackjackGame class. The unit test will have to create a Shoe that produces cards in a known sequence, as well as BlackjackPlayer. The cycle() method, as described in the design, is too complex for unit testing, and needs to be decomposed into a number of simpler procedures.

Table Of Contents

Previous topic

Blackjack Table Class

Next topic

Simple Blackjack Player Class

This Page