Roulette Table Class

This section provides the design for the Table to hold the bets. In the section Roulette Table Analysis we’ll look at the table as a whole.

One of the table’s resposibilities seems to be to validate bets. In InvalidBet Exception Design we’ll look at how we can design an appropriate exception.

In Roulette Table Design we’ll look at the details of creating the table class. Then, in Roulette Table Deliverables we’ll enumerate the deliverables for this chapter.

Roulette Table Analysis

We’ll look at several topics in detail as part of the analysis of the table.

The Table has the responsibility to keep the Bets created by the Player. Additionally, the house imposes table limits on the minimum amount that must be bet and the maximum that can be bet. Clearly, the Table has all the information required to evaluation these conditions.

Note

Betting Constraints

Casinos prevent the Martingale betting system from working by imposing a table limit on each game. To cover the cost of operating the table game, the casino also imposes a minimum bet. Typically, the maximum is a multiplier of the minimum bet, often in the range of 10 to 50; a table with a $5 minimum might have a $200 limit, a $10 minimum may have only a $300 limit.

It isn’t clear where the responsibility lies for determining winning and losing bets. The money placed on Bets on the Table is “at risk” of being lost. If the bet is a winner, the house pays the Player an amount based on the Outcome odds and the Bet amount. If the bet is a loser, the amount of the Bet is forfeit by the Player. Looking forward to stateful games like Craps, we’ll place the responsibility for determining winners and losers with the game, and not with the Table object.

We’ll wait, then, until we write the game to finalize paying winning bets and collecting losing bets.

Winning vs. Losing

Another open question is the timing of the payment for the bet from the player’s stake. In a casino, the payment to the casino – effectively – happens when the bet is placed on the table. In our Roulette simulation, this is a subtlety that doesn’t have any practical consequences. We could deduct the money as part of Bet creation, or we could deduct the money as part of resolving the spin of the wheel. In other games, however, there may several events and several opportunities for placing additional bets. For example, splitting a hand in blackjack, or placing additional odds bets in Craps.

Because we can’t allow a player to bet more than their stake, we should deduct the payment as the Bet is created.

A consequence of this is a change to our definition of the Bet class. We don’t need to compute the amount that is lost. We’re not going to deduct the money when the bet resolved, we’re going to deduct the money from the Player‘s stake as part of creating the Bet. This will become part of the design of Player and Bet.

Looking forward a little, a stateful game like Craps will introduce a subtle distinction that may be appropriate for a future subclass of Table. When the game is in the point off state, some of the bets on the table are not allowed, and others become inactive. When the game is in the point on state, all bets are allowed and active. In Craps parlance, some bets are “not working” or “working” depending on the game state. This does not apply to the version of Table that will support Roulette.

Container Implementation

A Table is a collection of Bets. We need to choose a concrete class for the collection of the bets. We can review the survey of collections in Design Decision – Choosing A Collection for some guidance here.

In this case, the bets are placed in no particular order, and are simply visited in an arbitrary order for resolution. Bets don’t have specific names.

Since the number of bets varies, we can’t use a Python tuple; a list will have to do. We could also use a set because duplicate bets don’t make any sense.

Table Limits

Table limits can be checked by providing a public method isValid() that compares the total of all existing Bets against the table limit. This should be used by the Game to confirm that bets are legal before proceeding.

In the unlikely event of the Player object creating an illegal Bet, this will raise an exception to indicate that we have a design error that was not detected via unit testing. This should be a subclass of Exception that has enough information to debug the problem with the Player that attempted to place the illegal bet.

Each individual bet must meet the table minimum. This is a separate rule that can be checked each time a bet is placed.

Adding and Removing Bets

A Table contains Bets. Instances of Bet are added by a Player. Later, Bets will be removed from the Table by the Game. When a bet is resolved, it must be deleted. Some games, like Roulette resolve all bets with each spin. Other games, like Craps, involve multiple rounds of placing and resolving some bets, and leaving other bets in play.

For bet deletion to work, we have to provide a method to remove a Bet instance. When we look at game and bet resolution we’ll return to bet deletion. It’s import not to over-design this class at this time; we will often add features as we develop designs for additional use cases.

InvalidBet Exception Design

We’ll raise an exception for an invalid bet. This is, in general, better than having a method which returns True for a valid bet and False for an invalid bet.

It’s better because we can simply place the bet, assuming that it is valid. The processing continues along this happy path.

If the bet is not valiid, the exception interrupts processing. The only way to get an invalid bet in Roulette is to have a badly damaged implementation of the Player class. We really need to have the application break in a catastrophic manner.

exception InvalidBet

InvalidBet is raised when the Player attempts to place a bet which exceeds the table’s limit.

This class simply inherits all features of its superclass.

Roulette Table Design

class Table

Table contains all the Bet s created by the Player. A table 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 in the simulation.

Fields

Table.limit

This is the table limit. The sum of the bets from a Player must be less than or equal to this limit.

Table.minimum

This is the table minimum. Each individual bet from a Player must be greate than this limit.

Table.bets

This is a list of the Bets currently active. These will result in either wins or losses to the Player.

Constructors

Table.Table()

Creates an empty list of bets.

Methods

Table.placeBet(self, bet)
Parameters:bet (Bet) – A Bet instance to be added to the table.
Raises:InvalidBet

Adds this bet to the list of working bets.

We’ll reserve the idea of raising an exception for an individual invalid bet. This is a rare circumstance, and indicates a bug in the Player more than anything else.

We might, for example, confirm that the Bet’s Outcome exists in one of the Bins. We might check that the bet amount is greater than or equal to the table minimum. We might also check the upper limit on betting will be honored by all existing bets plus this new bet.

It’s not necessary to validate each bet as they’re being placed. It’s only necessary to validate the bets once prior to spinning the wheel. This is a function of the Game, and a separate interface is available for this.

For an interactive game – not a simulation – we would want to validate each bet prior to accepting it so that we can provide an immediate response to the player that the potential bet is invalid. In this case, we’d leave the table untouched when a bad bet is offered.

Table.__iter__() → iter

Returns an iterator over the available list of Bet instances. This simply returns the iterator over the list of Bet objects.

Note that we need to be able remove Bets from the table. Consequently, we have to update the list, which requires that we create a copy of the list. This is done with bets[:].

Returns:iterator over all bets
Table.__str__() → str

Return an easy-to-read string representation of all current bets.

Table.__repr__() → str

Return a representation of the form Table( bet, bet, ... ).

Note that we will want to segregate validation as a separate method, or sequence of methods. This is used by the Game just prior to spinning the wheel (or rolling the dice, or drawing a next card.)

Table.isValid(self)
Raises:InvalidBet if the bets don’t pass the table limit rules.

Applies the table-limit rules:

  • The sum of all bets is less than or equal to the table limit.
  • All bet amounts are greater than or equal to the table minimum.

If there’s a problem an InvalidBet exception is raised.

Roulette Table Deliverables

There are three deliverables for this exercise. Each of these will have complete Python docstring comments.

  • An InvalidBet exception class. This is a simple subclass of Exception.
  • Since there’s no unique programming here, the unit test for InvalidBet is pretty simple. Indeed, it can seem silly to be sure that this class works with the raise statement; however, failure to extend Exception would lead to a program that more-or-less worked until a faulty Player class caused the invalid bet situation.
  • The Table class.
  • A class which performs a unit test of the Table class. The unit test should create at least two instances of Bet, and establish that these Bet s are managed by the table correctly.