Craps Solution Overview

We will present a survey of the classes gleaned from the general problem statement in Preliminary Survey of Classes. In Preliminary Craps Class Structure we’ll describe some potential class definitions.

Given this survey of the candidate classes, we will then do a walk-through to refine the definitions. We’ll show this in A Walk-Through of Craps. We can use assure ourselves that we have a reasonable architecture. We will make some changes to the preliminary class list, revising and expanding on our survey.

We will also include a number of questions and answers in Craps Solution Questions and Answers. This should help clarify the design presentation and set the stage for the various development exercises in the chapters that follow.

Preliminary Survey of Classes

We have divided up the responsibilities to provide a starting point for the development effort. The central principle behind the allocation of responsibility is to encapsulate the information or algorithm into a class. In reading the background information and the problem statement, we noticed a number of nouns that seemed to be important parts of the Craps game.

  • Dice

  • Bet

  • Table

  • Point

  • Proposition

  • Number

  • Odds

  • Player

  • House

The following table summarizes some of the classes and responsibilities that we can identify from the problem statement. This is not the complete list of classes we need to build. As we work through the exercises, we’ll discover additional classes and rework some of these classes more than once.

We also have a legacy of classes available from the Roulette solution. We would like to build on this infrastructure as much as possible.

Preliminary Craps Class Structure

Outcome

Responsibilities

A name for a particular betting opportunity. Most outcomes have fixed odds, but the behind the line odds bets have odds that depend on a point value.

Collaborators

Collected by a Table object into the available bets for the Player; used by Game instance to compute the amount won from the amount that was bet.

Dice

Responsibilities

Selects any winning propositions as well as next state of the game.

Collaborators

Used by the overall Game instance to get a next set of winning Outcome instances, as well as change the state of the Game instance.

Table

Responsibilities

A collection of bets placed on Outcome instances by a Player. This isolates the set of possible bets and the management of the amounts currently at risk on each bet. This also serves as the interface between the Player and the other elements of the game.

Collaborators

Collects the Outcome instances; used by Player to place a bet amount on a specific Outcome objects; used by Game instance to compute the amount won from the amount that was bet.

Player

Responsibilities

Places bets on Outcome instances, updates the stake with amounts won and lost. This is the most important responsibility in the application, since we expect to update the algorithms this class uses to place different kinds of bets. Clearly, we need to cleanly encapsulate the Player class, so that changes to this class have no ripple effect in other classes of the application.

Collaborators

Uses Table object to place Bet instances on preferred Outcome instances; used by Game object to record wins and losses.

Game

Responsibilities

Runs the game: gets bets from Player object, throws the Dice objects, updates the state of the game, collects losing bets, pays winning bets. This encapsulates the basic sequence of play into a single class. The overall statistical analysis is based on playing a finite number of games and seeing the final value of the Player object’s stake.

Collaborators

Uses Dice, Table, Outcome, and Player objects.

A Walk-through of Craps

A good preliminary task is to review these responsibilities to confirm that a complete cycle of play is possible. This will help provide some design details for each class. It will also provide some insight into classes that may be missing from this overview. A good way to structure this task is to do a CRC walk-through. For more information on this technique see A Walk-Through of Roulette.

The basic processing outline is the responsibility of the Game class. To start, locate the Game card.

  1. Our preliminary note was that this class “Runs the game.” The responsibilities section has a summary of five steps involved in running the game.

  2. The first step is “gets bets from Player.” Find the Player card.

  3. Does a Player instance collaborate with a Game instance to place bets? Note that the game state influences the allowed bets. Does Game instance collaborate with Player instance to provide the state information? If not, add this information to one or both cards.

  4. The Game object’s second step is to throw the Dice objects. Is this collaboration on the Dice card?

  5. The Game object’s third step is to update the state of the game. While the state appears to be internal to the Game class, requiring no collaboration, we note that the Player instance needs to know the state, and therefore should collaborate with the Game class. Be sure this collaboration is documented.

  6. The Game object’s fourth and fifth steps are to pay winning bets and collect losing bets. Does the Game instance collaborate with the Table object to get the working bets? If not, update the collaborations.

Something we’ll need to consider is the complex relationship between the dice, the number rolled on the dice, the game state and the various bets. In Roulette, a Wheel instance picked a random Bin object; the bin had a simple list of winning Outcome instances. All Bet instances on other Outcome instances were losers. In Craps, however, we find that we have game bets that are based on changes to the game state, not simply the number on the dice. The random outcome is used to resolve one-roll proposition bets, resolve hardways bets, change the game state, and resolve game bets. It is not a simple Outcome collection.

We also note that the house moves Come Line (and Don’t Come) bets from the Come Line to the numbered spaces. In effect, the bet is changed from a generic Outcome instance to a more specific Outcome instance. This means that a Bet object has a kind of state change. The parallels the the Game instance’s state change and any possible Player instance state change.

Important

Stateful Objects

Many interesting applications involve stateful objects. Everything that has a state or status or attributes that change, is stateful. State changes are almost universally accompanied by rules that determine legal changes, events that precipitate changes, and actions that accompany a state change.

Stateful objects must be taken very seriously. The consequence of ignoring state change complications is software that performs invalid or unexpected state transitions.

A walk-through gives an overview of the interactions among the objects in the working application. You may uncover additional design ideas from this walk-through. The most important outcome of the walk-through is a clear sense of the responsibilities and the collaborations required to create the necessary application behavior.

Craps Solution Questions and Answers

Why is the Outcome class distinct? Each object that is an instance of the Outcome class is merely a number from 2 to 12.

We have complex interdependency between the dice, the game states, the bets and outcomes. An outcome has different meanings in different game states: sometimes a 7 is an immediate winner, other times it as an immediate loser. Clearly, we need to isolate these various rules into separate objects to be sure that we have captured them accurately without any confusion, gaps or conflicts.

We can foresee three general kinds of Outcome classes: the propositions that are resolved by a single throw of the dice, the hardways that are resolved periodically, and the game bets which are resolved when a point is made or missed. Some of the outcomes are only available in certain game states.

The alternative is deeply nested if-statements. Multiple objects introduce some additional details in the form of class declarations, but objects have the advantage of clearly isolating responsibilities. Clear, narrow responsibilities makes us more confident that our design will work properly. If-statements only conflate all of the various conditions into a tangle that includes a huge risk of missing an important and rare condition.

See the discussion under Design Decision – Object Identity for more discussion on object identity and why each Outcome is a separate object.

What is the real difference between the classes Dice and Wheel? Don’t they both represent simple collections with random selection?

Perhaps. At the present time, the distinction appears to be in the initialization. A Wheel instance is a collection of Bin objects. The Dice object has 36 outcomes, each with a number of meanings.

Generally, we are slow to merge classes together without evidence that they are really the same thing. In this case, they appear very similar, so we will note the similarities and differences as we work through the design details. There is a fine line between putting too many things together and splitting too many things apart.

Generally, the bigger mistake seems to be conflating too many distinct things, and resolving the differences through complex if-statements and other hidden processing logic.

Looking Forward

In the next chapter we’ll look closely at the various outcomes that stem from rolling the dice. This will lead us to a deeper understanding of the existing Outcome class definition, and some rework to make it suitable for both games.