Table of Contents
This section provides the design for the
Table to hold the bets. It also introduces the
concepts behind exception handling, and the proper role of
exceptions.
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.
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's odds and the
Bet's 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.
Our second open question is the timing of the payment for the bet from the player's stake. In a casino, the payment 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 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. Rather than deduct the money when the bet resolved,
we've decided that the money is deducted 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 our survey of the Java
collections in Java Collections and the Python collections in
Python
Collections 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. We can use a
Java LinkedList for this. Since the number of
bets varies, we can't use a Python
tuple; a
list will do.
Table Limits. Table limits can be checked by providing a public method
isValid that compares the total of a new
prospective amount plus all existing Bets to
the table limit. This can be used by the
Player to evaluate each potential bet prior
to creating it.
In the unlikely event of the Player
object creating an illegal Bet, we can also
throw (or 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.
Additionally, the game can check the overall state of a
Player's Bets to be sure
that the table minimum is met. We'll need to provide a public method
isValid that is used by the game. In the
event of the minimum not being met, there are serious design issues,
and an exception should be thrown. Generally, this situation arises
because of a bug where the Player should have
declined to bet rather than placing incorrect bets that don't meet the
table minimum.
Bet Resolution. An important consideration is the collaboration between
Table and some potential game class for
resolving bets. The Table has the collection
of Bets, each of which has a specific
Outcome. The Wheel
selects a Bin, which has a collection of
Outcomes. All bets with a winning outcome
will be resolved as a winner.
Because some games are stateful, and the winning and losing bets depend on game state, we will defer the details of the collaboration design until we get to the Game class. For now, we'll simply collect the Bets.
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. 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.