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”. TheTable
accepts thisBet
instance.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
Bet
may 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
Bet
instances,inactivating outcomes based on game state, and
accepting or rejecting bets based on
CrapsGame
state.
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 ofOutcome
instances that are allowed or not allowed.CrapsGame
can make a call to get the list ofOutcome
instances and make the changes. Making each change would involve theCrapsTable
a second time to mark the individualOutcome
instances. This information is then used by theCrapsTable
to validate individualBet
instances.CrapsGame
. This class could invoke a method ofCrapsTable
that changes a singleOutcome
’s state to makt it inactive. This information is then used by theCrapsTable
to validate individualBet
instances.A feature of this choice is to have the
validBet()
method ofCrapsTable
depend onCrapsGame
to determine which bets are allowed or denied. In this case,CrapsGame
has the responsibility to respond to requests from eitherCrapsTable
orPlayer
regarding a specificOutcome
instances.
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
) – AnOutcome
that may be allowed or not allowed, depending on the game state.
Determines if the
Outcome
is allowed in the current state of the game. When thepoint
is 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
) – AnOutcome
that may be allowed or not allowed, depending on the game state.
Determines if the
Outcome
is working in the current state of the game. When thepoint
is 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
CrapsGame
to see if the bet is valid; it returnsTrue
if the bet is valid,False
otherwise.
-
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
CrapsGame
class 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
CrapsTable
subclass.A class which performs a unit test of the
CrapsTable
class. The unit test should create a couple instances ofBet
, and establish that theseBet
instances are managed by the table correctly.For testing purposes, it is easiest to have the test method simply set the the
point
variable in theCrapsGame
instance 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.