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.
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.
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.
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.
|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.
|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.
|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|
How do we identify a specific hand?
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.
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.
|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.
Creates a new, empty list in which to keep Hands.
Returns the initial Hand. This is used by the pre-split parts of the Blackjack game, where the player only has a single Hand.
Returns an iterator over the List of Hands this player is currently holding.
|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.
|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.
|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
- Create a new Ante bet for this hand.
- 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.
|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.
|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.
Displays the current state of the player, and the various hands.
Card must provide the Game some information required to offer insurance bets.
The AceCard subclass, however, must respond with True to this method.
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.
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.
Constructs a new BlackjackGame, using a given Shoe for dealing Cards and a BlackjackTable for recording Bets that are associated with specific Hands.
A single game of Blackjack. This steps through the following sequence of operations.
Reset the dealer’s hand and deal two cards.
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.
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.
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.
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.
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.
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.
Offers even money or insurance for a single game of blackjack. This steps through the following sequence of operations.
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.
Call BlackjackPlayer.insurance(). If insurance declined, this method is done.
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.
|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.
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.
If the points are over 21, the hand is bust, and is immediately resolved as a loser. The game is over.
Displays the current state of the game, including the player, and the various hands.
There are eight deliverables for this exercise.