CrapsPlayer Class

The numerous variations on CrapsPlayer, all of which reflect different betting strategies, are the heart of this application. In Roulette Game Class, we roughed out a stub class for Player, and refined it in Player Class. We will further refine this to create a definition of the CrapsPlayer class for use in simulating the complex stateful game of Craps.

In Craps Player Analysis we’ll look at the general responsibilities and collaborators of a player. Since we’ve already examined many features of the game, we can focus on the player and revising the roughed-out version we created earlier.

We’ll present the details of the design in three parts:

In Craps Player Deliverables we’ll detail the deliverables for this chapter.

Craps Player Analysis

We have built enough infrastructure that we can begin to add a variety of players and see how their betting strategies work. Each player has betting algorithm that we will evaluate by looking at the player’s stake to see how much they win, and when they stop playing because they’ve run out of time or gone broke.

As a practical matter, the house edge means players will eventually go broke. It’s mostly a question of how long they can play before they’ve run out of money.

The CrapsPlayer class has the responsibility to create bets and manage the amount of their stake. To create bets, the player must create legal bets from known Outcome instances and stay within table limits. To manage their stake, the player must deduct the price of a bet when it is created, accept winnings or pushes, report on the current value of the stake, and leave the table when they are out of money.

We have an interface that was roughed out as part of the design of the CrapsGame and CrapsTable classes. In designing the CrapsGame class, we put a placeBets() method in the CrapsPlayer class to place all bets. We expected the CrapsPlayer class to create Bet instances and use the CrapsTable.placeBet() method to save the individual Bet instances.

In an earlier exercise, we built a stub version of the CrapsPlayer class in order to test the Game class. See CrapsPlayer Class Stub. When we finish creating the final superclass, CrapsPlayer, we will also revise our CrapsPlayerStub to be more complete, and rerun our unit tests to be sure the expanded design still handles the basic test cases correctly.

Our objective is to have a new abstract CrapsPlayer class, with a concrete subclass that follows the Martingale system, using simple Pass Line bets and behind the line odds bets.

We’ll defer some of the design required to collect detailed measurements for statistical analysis. In this first release, we’ll simply place bets. Most of the Simulator class we built for Roulette should be applicable to Craps without significant modification.

Some Basic Features. Our basic CrapsPlayer class will place a Pass Line bet and a Pass Line Odds bet. This requires the player to interact with the CrapsTable or the CrapsGame class to place bets legally. On a come out roll, only the Pass Line will be legal. After that, a single Pass Line Odds bet can be placed. This leads to three betting rules:

  • Come Out Roll. Condition: No Pass Line Bet is currently placed and only the Pass Line bet is legal. Action: Place a Pass Line bet.

  • First Point Roll. Condition: No odds bets is currently placed and odds bets are legal. Action: Place a Pass Line Odds bet.

  • Other Point Roll. Condition: An odds bets is currently placed. Action: Do Nothing.

Beyond simply placing Pass Line and Pass Line Odds bets, we can use a Martingale or a Cancellation betting system to increase the bet on each loss, and decrease the amount on a win. Since we have two different bets in play – a single bet created on the come out roll, a second odds bet if possible – the simple Martingale system doesn’t work well. In some casinos, the behind the line odds bet can be double the pass line bet, or even 10 times the pass line bet, leading to some potentially complex betting strategies. For example, the CrapsPlayer could apply the Martingale system only to the odds bet, leaving the pass line bet at the table minimum. We’ll set this complexity aside for the moment, build a simple player first.

CrapsPlayer Design

The CrapsPlayer class is a subclass of an abstract Player class. It places bets in Craps. This is also an abstract class, with no actual body for the Player.placeBets() method. However, this subclass does implement the basic win() and lose() methods used by all of its subclasses.

Since this is a subclass of a common player definition, we inherit several useful features. Most of the features of Player are repeated here. The student should refactor the common code out of the CrapsPlayer class into the common superclass shared by the CrapsPlayer and RoulettePlayer classes.

Fields

CrapsPlayer.stake

The player’s current stake. Initialized to the player’s starting budget.

CrapsPlayer.roundsToGo

The number of rounds left to play. Initialized by the overall simulation control to the maximum number of rounds to play. In Roulette, this is spins. In Craps, this is the number of throws of the dice, which may be a large number of quick games or a small number of long-running games. In Craps, this is the number of cards played, which may be large number of hands or small number of multi-card hands.

CrapsPlayer.table

The CrapsTable used to place individual Bet instances.

Constructors

CrapsPlayer.__init__(self, table: CrapsTable) → None
Parameters

table (CrapsTable) – The table

Constructs the CrapsPlayer instance with a specific CrapsTable object for placing Bet instances.

Methods

CrapsPlayer.playing(self) → bool

Returns True while the player is still active. A player with a stake of zero will be inactive. Because of the indefinite duration of a craps game, a player will only become inactive after their roundsToGo is zero and they have no more active bets. This method, then, must check the CrapsTable instance to see when all the bets are fully resolved. Additionally, the player’s betting rules should stop placing new bets when the roundsToGo is zero.

CrapsPlayer.placeBets(self) → bool

Updates the CrapsTable instance with the various Bet instances.

When designing the CrapsTable class, we decided that we needed to deduct the price of the bet from the stake when the bet is created. See the Roulette Table Roulette Table Analysis for more information on the timing of this deduction, and the Craps Bet Bet Analysis for more information on the price of a bet.

CrapsPlayer.win(self, bet: Bet) → None
Parameters

bet (Bet) – that was a winner

Notification from the CrapsGame object that the Bet instance was a winner. The amount of money won is available via Bet.winAmount().

CrapsPlayer.lose(self, bet: Bet) → None
Parameters

bet (Bet) – that was a loser

Notification from the CrapsGame that the Bet instance was a loser.

CrapsPlayerPass Subclass

CrapsPlayerPass is a CrapsPlayer who places a Pass Line bet in Craps.

Methods

CrapsPlayer.placeBets(self) → bool

If no Pass Line bet is present, this will update the Table object with a bet on the Pass Line at the base bet amount.

Otherwise, this method does not place an additional bet.

Craps Martingale Subclass

class CrapsMartingale

The CrapsMartingale class is a subclass of CrapsPlayer who places bets in Craps. This player doubles their Pass Line Odds bet on every loss and resets their Pass Line Odds bet to a base amount on each win.

Fields

CrapsPlayer.lossCount

The number of losses. This is the number of times to double the pass line odds bet.

CrapsPlayer.betMultiple

The the bet multiplier, based on the number of losses. This starts at 1, and is reset to 1 on each win. It is doubled in each loss. This is always set so that betMultiple = 2^{lossCount}.

Methods

CrapsPlayer.placeBets(self) → bool

If no Pass Line bet is present, this will update the Table with a bet on the Pass Line at the base bet amount.

If no Pass Line Odds bet is present, this will update the Table object with a Pass Line Odds bet. The amount is the base amount times the betMultiple.

Otherwise, this method does not place an additional bet.

CrapsPlayer.win(self, bet: Bet) → None
Parameters

bet (Bet) – that was a winner

Uses the superclass win() method to update the stake with an amount won. This method then resets the lossCount to zero, and resets the betMultiple to 1.

CrapsPlayer.lose(self, bet: Bet) → None
Parameters

bet (Bet) – that was a loser

Increments lossCount by 1 and doubles betMultiple.

Craps Player Deliverables

There are six deliverables for this exercise.

  • The CrapsPlayer abstract superclass. Since this class doesn’t have a body for the placeBets() method, it can’t be unit tested directly.

  • A CrapsPlayerPass class that is a proper subclass of the CrapsPlayer class, but simply places bets on Pass Line until the stake is exhausted.

  • A unit test class for the CrapsPlayerPass class. This test should synthesize a fixed list of Outcome instances, Throw instances, and calls a CrapsPlayerPass instance with various sequences of craps, naturals and points to assure that the pass line bet is made appropriately.

  • The CrapsMartingale subclass of the CrapsPlayer class.

  • A unit test class for the CrapsMartingale class. This test should synthesize a fixed list of Outcome instances, Throw objects, and calls a CrapsMartingale instance with various sequences of craps, naturals and points to assure that the bet doubles appropriately on each loss, and is reset on each win.

  • The unit test class for the CrapsGame class should still work with the new CrapsPlayerPass class. Using a non-random generator for the Dice instance, this should be able to confirm correct operation of the CrapsGame class for a number of bets.

Looking Forward

Once we have the basics of a player, we can do some design cleanup and refactoring of the code. We have a large number of classes, and there are some areas of overlap and commonality that suggest possible simplifications. In the next chapter we’ll refactor some more of the application class hierarchy.