Craps Table Class¶
In Roulette, the table was a passive repository for Bet instances.
In Craps, however, the table and game must collaborate to accept or
reject bets based on the state of the game. This validation includes
rules based on the total amount bet as well as rules for the individual bets.
In Throw Class, we roughed out a stub version of the CrapsGame class
to test the Throw class. In this section, we will
extend this stub with additional features required by the table.
In Craps Table Analysis we’ll look at two issues related to how the game works. In Movable Bets we’ll look at how the Pass Line bet works. In Game State and Allowed Bets we’ll look at the bet validation issue.
We’ll dig into three separate design decisions:
After looking closely at the choices, in Handling Working Bets we’ll create a final approach to handling the bets on the table.
This will lead to detailed design presentations in CrapsGame Stub and Craps Table Design. We’ll enumerate the deliverables for this chapter in Craps Table Deliverables.
Craps Table Analysis¶
The Table class is a container for the Bet instances. The
money placed on Bet instances on the Table is
“at risk”. There are several outcomes:
A bet can win an amount based on the odds,
A bet can lose the amount placed by the player,
The Don’t Come and Don’t Pass bets may be returned, an event called a “push”, and
The Buy and Lay bets include a commission (or vigorish) to place the bet; the commission is lost money; the balance of the bet, however, may win or lose.
The responsibility for this new event called
a push is something we can allocate to CrapsGame, the commission price belongs to Bet.
Movable Bets¶
Some Bet instances (specifically Pass, Don’t Pass, Come and Don’t
Come) may have their Outcome reference changed. The use case works like this:
The bet is created by the Player with one
Outcome, for example, “Pass”. TheTableaccepts thisBetinstance.That bet may be resolved as an immediate winner or loser. The Game and Throw will determine if the Bet is a winner as placed.
More commonly, the
Betmay be changed to a newOutcome, possibly with different odds.In a casino, the chips initially placed on Come Line and Don’t Come bets are relocated to a point number box to show this change. In the case of Pass Line and Don’t Pass bets, the “On” marker is placed on the table to show an implicit movement of all of those line bets.
The change is the
responsibility of the CrapsGame object; however, the Table instance
must provide an iterator over the line bets that the CrapsGame object
will move.
Game State and Allowed Bets¶
Each change to the game state changes the allowed bets as well as the active bets. When the point is off, most of the bets on the table are not allowed, and some others are inactive, or not “working”.
When a point is established, all bets are allowed, and all bets are active. We’ll examine the rules in detail, below.
The Table class must be
able to reject bets which are inappropriate for the current CrapsGame object’s
state.
Design Decision – Table vs. Game Responsibility¶
We’ve identified three responsibilities that are part of handling Craps bets:
moving
Betinstances,inactivating outcomes based on game state, and
accepting or rejecting bets based on
CrapsGamestate.
Clearly, these require
additional collaboration between the CrapsGame and Table classes.
We will have to add methods to the CrapsGame class that will allow
or deny some bets, as well as methods that will active or deactive some bets.
We have to choose where in the class hierarchy we will retrofit this additional collaboration.
Problem.
Should we put these new responsibilities at a high-enough level that we’ll add table and game
collaboriation to the Table class used for Roulette?
Forces.
If we do add
this for Roulette, we could simply return True from the
method that validates the allowed bets, since all bets are allowed in
Roulette.
However, our overall application design does not depend on all
subclasses of CrapsGame and Table being
polymorphic; we will never mix and match different combinations of Craps
Table and Roulette Game.
Solution.
Because we don’t need polymorphism between Craps and Roulette, we can
create a subclass of Table with a more complex interface
and leave Roulette untouched. Perhaps we’ll call it CrapsTable.
Design Decision – Allowable Outcomes¶
After deciding to create a CrapsTable subclass, we have
several consequent decisions. First, we turn the interesting question of
how best to allocate responsibility for keeping the list of Outcome instances
which change with the game state.
Problem.
Which class determines the valid and invalid Outcome instances?
Forces. We can see two places to place this responsibility.
CrapsTable. This class could have methods to return the lists ofOutcomeinstances that are allowed or not allowed.CrapsGamecan make a call to get the list ofOutcomeinstances and make the changes. Making each change would involve theCrapsTablea second time to mark the individualOutcomeinstances. This information is then used by theCrapsTableto validate individualBetinstances.CrapsGame. This class could invoke a method ofCrapsTablethat changes a singleOutcome’s state to makt it inactive. This information is then used by theCrapsTableto validate individualBetinstances.A feature of this choice is to have the
validBet()method ofCrapsTabledepend onCrapsGameto determine which bets are allowed or denied. In this case,CrapsGamehas the responsibility to respond to requests from eitherCrapsTableorPlayerregarding a specificOutcomeinstances.
Solution
We need to place a validation method in the CrapsTable; but the Table simply
delegates the details to the CrapsGame. This allows the Player to
deal directly with the Table. But it centralizes the actual decision-making
on the Game.
This leaves the table as a fairly passive repository for bets. The bulk of the decision-making for validity is delegated to the game.
Consequences.
The game must move Outcome instances for certain kinds of bets.
Additionally, the CrapsTable’s isValid()
method will use the CrapsGame to both check the validity of
individual bets as well as the entire set of bets created by a player.
The first check allows or denies individual bets, something CrapsTable
must do in collaboration with CrapsGame. For the second
check, the CrapsTable assures that the total of the bets is
within the table limits; something for which only the table has the
information required.
Design Decision – Domain of Allowed Bets¶
The rule for allowed and non-allowed bets is relatively
simple. When the game state has no point (also known as the come out
roll), only Pass Line and Don’t Pass bets are allowed, all other bets
are not allowed. When the point is on, all bets are allowed. We’ll have
to add an isAllowed() to CrapsGame, which CrapsTable
will use when the player attempts to place a bet.
We have two ways to implement this:
A “validation” function that determines if bets are allowed.
A “what’s possible” function that returns an enumeration of legal bets.
The idea of one-by-one validation might make sense in a situation where the player transactions are quite complex. For casino games, the player’s alternatives are narrowly constrained.
It makes considerable sense for the CrapsGame to supply the domain of
allowed bets to the Table and Player. In this way, a Player
can simply extract the interesting bets from the available domain of possible
bets.
Handling Working Bets¶
The rule for working and non-working bets adds a layer of complexity to the game state.
On the come out roll, all odds bets placed behind any
of the six Come Point numbers are not working. This rule only applies to
odds behind Come Point bets; odds behind Don’t Come bets are always
working. We’ll have to add an isWorking() to CrapsGame,
which CrapsTable will use when iterating through working bets.
The sequence of events that can lead to this condition is as follows.
The player places a Come Line bet, the dice roll is 4, 5, 6, 8, 9 or 10, and establishes a point. The bet is moved to one of the six come points.
The player creates an additional odds bet placed behind this come point bet.
A dice roll makes the main game point is a winner. changing the game state so the next roll is a come out roll. In this state, any additional odds behind a come point bet will be a non-working bets on the subsequent come-out roll.
As with allowed bets, we have a domain of working bets for a given game state. We can implement this as a function that responds with state information. We can also implement this as a collection of bets what are working in a given game state.
The code could look like this:
Working Bets Method
if theTable.is_working(some_bet):
if theTable.winner(some_bet):
player.win(some_bet)
else:
player.lose(some_bet)
Or, it could look like this:
Working Bets Collection
if some_bet in theTable.working_bets():
if some_bet in theTable.winning_bets():
player.win(some_bet)
else:
player.lose(some_bet)
The distinction is minor.
Also note that the examples don’t include push outcomes. We’ll look at the details of hanlding that in the CrapsGame Class section.
What’s important is that we can handle these subtle cases gracefully.
This elegant processing of complex rules is one of the important reasons why
object-oriented programming can be more successful than procedural
programming. In this case, we can isolate this state-specific processing
to the CrapsGame class. We can also provide the interface to the CrapsTable
class making this responsibility explicit and easy to use.
CrapsGame Stub¶
The CrapsGame class is a preliminary design for the game of Craps. In
addition to features required by the Throw class, this version
includes features required by the CrapsTable class.
Methods¶
-
CrapsGame.isAllowed(self, outcome: Outcome) → bool¶ - Parameters
outcome (
Outcome) – AnOutcomethat may be allowed or not allowed, depending on the game state.
Determines if the
Outcomeis allowed in the current state of the game. When thepointis zero, it is the come out roll, and only Pass, Don’t Pass, Come and Don’t Come bets are allowed. Otherwise, all bets are allowed.
-
CrapsGame.isWorking(self, outcome: Outcome) → bool¶ - Parameters
outcome (
Outcome) – AnOutcomethat may be allowed or not allowed, depending on the game state.
Determines if the
Outcomeis working in the current state of the game. When thepointis zero, it is the come out roll, odds bets placed behind any of the six come point numbers are not working.
Craps Table Design¶
The CrapsTable is a subclass of the Table class with an
association with a CrapsGame object. As a subclass of the Table class,
it contains all the Bet instances created by the Player instance.
It also has a betting limit, and the sum of all of a player’s bets
must be less than or equal to this limit. We assume a single Player instance
in the simulation.
Fields¶
Constructors¶
Methods¶
-
CrapsTable.isValid(self, bet: Bet) → bool¶ - Parameters
bet (
Bet) – The bet to validate.
Validates this bet by checking with the
CrapsGameto see if the bet is valid; it returnsTrueif the bet is valid,Falseotherwise.
-
CrapsTable.allValid(self) → bool¶ This uses the superclass to see if the sum of all bets is less than or equal to the table limit. If the individual bet outcomes are also valid, return
True. Otherwise, returnFalse.
Craps Table Deliverables¶
There are three deliverables for this exercise.
A revision of the stub
CrapsGameclass to add methods for validating bets in different game states. In the stub, the point value of 0 means that only the “Pass Line” and “Don’t Pass Line” bets are valid, where a point value of non-zero means all bets are valid.The
CrapsTablesubclass.A class which performs a unit test of the
CrapsTableclass. The unit test should create a couple instances ofBet, and establish that theseBetinstances are managed by the table correctly.For testing purposes, it is easiest to have the test method simply set the the
pointvariable in theCrapsGameinstance to force a change in the game state. While public instance variables are considered by some to be a bad policy, they facilitate the creation of unit test classes.