Copyright © 2005 Steven F. Lott
This work is licensed under a Creative Commons License. You are free to copy, distribute, display, and perform the work under the following conditions:
Attribution. You must give the original author, Steven F. Lott, credit.
Noncommercial. You may not use this work for commercial purposes.
No Derivative Works. You may not alter, transform, or build upon this work.
For any reuse or distribution, you must make clear to others the license terms of this work.
5/4/2005
Table of Contents
List of Tables
List of Examples
instanceofplayer ModuleList of Equations
Table of Contents
The present letter is a very long one, simply because I had no leisure to make it shorter.
The coffee-shop answer is to provide the beginning designer with a sequence of interesting and moderately complex exercises in OO design.
Some software developers find themselves stalled when trying to do object-oriented (OO) design. As programmers, they've understood the syntax of a programming language, and pieced together small examples. However, it is often difficult to take the next step to becoming a designer. Because this transition from guided learning of language features to self-directed design work is often ignored, programmers are left to struggle through their first design projects without appropriate skills or support. While it is critically important to examine examples of good design, a finished product doesn't reveal the author's decision-making process that created the design.
The most notable consequence of this skills gap is the creation of software that is far more complex than necessary to effectively solve a given problem. This, in turn, leads to software with high maintenance costs stemming from the low quality. It also leads to an unfair indictment of OO technology; this is usually voiced as “we tried OO programming and it failed.”
As programming team leaders, educators and consultants, we find that software development training is focused on the programming tools, but does not expose the process of creating a design. In the building trades, we would neither expect nor allow apprentice plumbers to design the sanitary sewage system for an urban office building. Yet, in too many Information Technology (IT) departments, software developers are expected to leap from basic training in their tools to application design.
To continue this rant, we also find that some managers are entrusted with significant projects, but are uncomfortable with OO design on modern high-performance hardware. They tend to focus their design energies on the kinds of software architectures that were appropriate when the enterprise owned a single computer, when 64 megabytes of memory was all the enterprise would ever need, and centralized disk storage was charged back to end user departments at a rate of pennies per track per month. In some organizations, there are a few enduring symptoms of this mind set in some of the ways that “end-user computing” is separated from “enterprise computing”; we relegate everything non-mainframe to second class status.
Management discomfort with OO technology surfaces in many ways. One shocking comment was that “no application needs more than six classes.” A consequence of this management attitude is an unrealistic expectation for schedule and the evolution of the deliverables.
The intent of this book is to help the beginning designer by giving them a sequence of interesting and moderately complex exercises in OO design. This book can also help managers develop a level of comfort with the process of OO software development. The applications we will build are a step above trivial, and will require some careful thought and design. Further, because the applications are largely recreational in nature, they are interesting and engaging. This book allows the reader to explore the processes and artifacts of OO design before project deadlines make good design seem impossible.
We hope to prevent managers from saying the following: “We had a good design, but were forced to compromise it to meet our schedule.” We found this to be a sad statement of management's emphasis of one near-term goal over long-term value. This was the result of a series of poorly-informed management decisions compounded on weak design skills. One of the root causes was the inability of the designers and managers to agree to a suitable course of action when a new kind of requirement made devastating changes to an existing design. We believe that more informed managers would have made a decision that created better long-term value.
We have noted that programmers who are new to OO programming find their first exposure to objects to be more distasteful than empowering. Why does this happen? Some instructors launch into extensive presentations on object orientation before getting to the language fundamentals, leaving students lost as to how they will accomplish the noble and lofty goals of OO. Other instructors leave OO for last, exposing the procedural side of the language first, and treating objects as a kind of add-on. This leaves students feeling that objects are optional. Additionally, some very skilled instructors are not skilled developers, and will often show examples that don't reflect currently accepted best practices.
These programmers who have an exposure to the language, but need more time to understand objects and object-orientation are our core audience. We want to provide exercises that have four key features: they are just complex enough to require careful design work, are just fun enough to be engaging, are easy enough that results are available immediately, and can be built in simple stages.
In our effort to support the beginning student, we'll provide a few additional details on language features. We'll mark these as “Tips” for the new programmer, and qualify the tip by language. For more advanced students, these tips will be review material. We will not provide a thorough background in any programming language. The student is expected to know the basics of the language and tools.
Instructors are always looking for classroom projects that are engaging, comprehensible, and focus on perfecting language skills. Many real-world applications require considerable explanation of the problem domain; the time spent reviewing background information detracts from the time available to do the relevant programming. While all applications require some domain knowledge, the idea behind these exercises is to pick a domain that many people know a little bit about. This allows an instructor to use some or all of these exercises without wasting precious classroom time on incidental details required to understand the problem.
This book assumes an introductory level of skill in an OO programming language. We provide specific examples in Java (at least version 1.4) and Python (at least version 2.3). Student skills we expect include the following.
java.util package. For Python programmers, this means the built-in sequence and mapping types.
This book presents a series of exercises to build simulations of the common, popular casino table games: Roulette, Craps and Blackjack. Each simulation can be extended to include variations on the player's betting system. With a simple statistical approach, we can show the realistic expectations for any betting system. Each of these games has a separate part in this book. Each part consists of a number of individual exercises to build the entire simulation. The completed project results in an application that can provide simple tabular results that shows the average losses expected from each betting strategy.
The interesting degree of freedom in each of the simulations is the player's betting strategy. The design will permit easy adaptation and maintenance of the player's strategies. The resulting application program can be extended by inserting additional betting systems, which allows exploration of what (if any) player actions can minimize the losses.
For Roulette, we proceed slowly, building up the necessary application one class at a time. Since this is the simplest game, the individual classes reflect that simplicity. We focus on isolation of responsibilities, creating a considerable number of classes. The idea is to build skills in object design by applying those skills to a number of classes.
The first chapter of the part provides details on the game of Roulette and the problem that the simulation solves. The second chapter is an overview of the solution, setting out the highest-level design for the application software. This chapter includes a technique for doing a “walk-through” of the design to be confident that the design will actually solve the problem.
Each of the remaining sixteen chapters is a design and programming exercise to be completed by the student. Plus or minus a Frequently Asked Questions (FAQ) section, each chapter has the same basic structure: an overview of the components being designed, some design details, and a summary of the deliverables to be built. The overview section presents some justification and rationale for the design. This material should help the student understand why the particular design was chosen. The design section provides a more detailed specification of the class or classes to be built. This will include some technical information on Java or Python implementation techniques.
For Craps, we build on the design patterns from Roulette. Craps, however, is a stateful game, so there is a more sophisticated design to handle the interactions between dice, game state and player. We exploit the State design pattern to show how the design pattern can be applied to this simple situation.
The first chapter is background information on the game of Craps, and the problem that the simulation solves. The second chapter is an overview of the solution, setting out the highest-level design for the application software. This chapter also provides a “walk-through” of the design.
Each of the remaining eleven chapters is an exercise to be completed by the student. Each chapter has the same basic structure: an overview of the component being designed, some design details, and a summary of the deliverables to be built.
The most sophisticated game that we cover here is Blackjack. The game states are more sophisticated than Craps since the available betting opportunities can change with split hands. Further, the player has two kinds of choices: betting opportunities, plus play opportunities. This makes the player's strategy considerably more complex. In casino gift shops, you can buy small summary cards that enumerate all possible game states and responses. The more advanced student can tackle these sophisticated betting strategies. For the less advanced student we will simplify the strategies down to some simpler conditions.
The first two chapters are background information on the game of Blackjack, the problem that the simulation solves, and an overview of the solution, setting out the highest-level design for the application software. Each of the remaining six chapters is an exercise to be completed by the student. Since this is more advanced material, and builds on previous work, this part has many simple deliverables compressed into the individual chapters.
Casino table games may seem like an odd choice of subject matter for programming exercises. We find that casino games have a number of advantages for teaching OO design and OO programming.
Casino games have an almost ideal level of complexity. If they were too simple, the house edge would be too obvious and people would not play them. If they were too complex, people would not enjoy them as simple recreation. Years (centuries?) of experience in the gaming industry has fine-tuned the table games to fit nicely with the limits of our human intellect.
Simulation of discrete phenomena lies at the origin of OO programming. We have found it easier to motivate, explain and justify OO design when solving simulation problems. The student can then leverage this insight into other applications of OO programming for more common transactional applications.
The results are sophisticated but easy to interpret. Probability theory has been applied by others to develop precise expectations for each game. These simulations should produce results consistent with the known probabilities. This book will skim over the probability theory in order to focus on the programming. For a few exercises, the theoretical results will be provided to serve as checks on the correctness of the student's work.
This book does not endorse casino gaming. Indeed, one of the messages of this book is that all casino games are biased against the player. Even the most casual study of the results of the exercises will allow the student to see the magnitude of the house edge in each of the games presented.
We have to adopt a style for each of the languages we're presenting. We won't present a complete set of coding standards; we will omit a number of issues that should be standardized. Some IT shops have documents they call “coding standards”, but are little more than descriptive style guides. What follows is not this kind of style guide; instead, it is some justification of the style we use for the examples in this book.
Just to continune this rant, we find that source code examples speak louder than any gratuitously detailed “specification” of the desired style. We find that some IT organizations waste time trying to write definitions of the preferred style. A good example trumps the description of the example. In particular, as consultants, we are often asked to provide standards to an inexperienced team of programmers. While the programmers only look at the examples (often cutting and pasting them), some managers prefer to spend money on empty verbiage peripheral to the useful example.
We use Java-centric terminology -- “field” and “method” -- throughout the book. Occaisionally, we will emphasize the differences between Java and Python by using the Python terms “attribute”, “instance variable” or “method function”.
We avoid using complex prefixes for variable names. In particular, we find prefixes to be little more than visual clutter. For example, an integer parameter with the amount of a bet might be called pi_amount where the prefix indicates the scope (p for a parameter) and type (i for an integer).
This style of name is only appropriate for primitive types, and doesn't address complex data structures well at all. How does one name a parameter that is a LinkedList of Sets of Outcomes? In Java programs, the variables are formally declared, therefore, we find that we don't need additional cues for their data type.
In some cases, prefixes are used to denote the scope of an instance variables. Variable names might include a cryptic one-letter prefix like “f” to denote an instance variable; sometimes programmers will use “my” or “the” as an English-like prefix. We prefer to reduce clutter. In Java, we have the qualifier this. available to disambiguate parameter from instance variable references. In Python, instance variables are always qualified, typically by self., making the scope very clear.
Code examples will be minimal, and will include Python and Java. Here is a Python example.
Example 1. Typical Python Example
combo = { }
for i in range(1,7):
for j in range(1,7):
roll= i+j
combo.setdefault( roll, 0 )
combo[roll] += 1
for n in range(2,13):
print "%d %.2f%%" % ( n, combo[n]/36.0 )
The output from the above program will be shown as follows:
2 0.03% 3 0.06% 4 0.08% 5 0.11% 6 0.14% 7 0.17% 8 0.14% 9 0.11% 10 0.08% 11 0.06% 12 0.03% Tool completed successfully
We will use the following type styles for references to a specific Class, method, or variable.
Most of the design specifications will provide Java-style method and variable descriptions. Python doesn't use type specifications, and Python programmers will have to translate the Java specifications into Python by removing the type names.
There will be design tips, and warnings, in the material for each exercise. These reflect considerations and lessons learned that aren't typically clear to starting OO designers.
We would like to thank Chuck Pyrak for putting us up to this. His idea of a One Room Schoolhouse to teach Java to an audience at multiple skill levels was a great idea. Additionally, our colleagues who collaborated through BLOKI brought infinte wisdom and insight to a complex and difficult project.
Table of Contents
We'll set our goal by presenting several elements that make up a complete problem statement: a context in which the problem arises, the problem, the forces that influence the choice of solution, the solution that balances the forces, and some consequences of the chosen solution.
Based on the problem statement, we'll present the very simple use case that this software implements. The use case is almost too trivial to bother defining. However, we have seen many projects run aground because they lacked even the most rudimentary description of the actor, the system and how the system helps the actor create value.
We will summarize the approach to the solution, describing the overall strategy that we will follow. This is a kind of overall design pattern that we'll use to establish some areas of responsibility.
We will also describe the technical foundations. In this case, they are not terribly complex, but this is an important part of describing any software solution, no matter how simple.
We will dance around the methodology issue. Our intent is not to sell a particular methodology, but to provide some perspective on how we broke the work into manageable pieces.
Finally, we'll present some important parts of getting started on the solution. These are more specific, technical considerations that define common aspects of our approach.
Context. Our context is the “classic” casino table games played against the house, including Roulette, Craps and Blackjack. We want to explore the consequences of various betting strategies for these casino games. Questions include “How well does the Cancellation strategy work?” “How well does the Martingale strategy works for the Come Line odds bet in Craps?” “How well does this Blackjack strategy I found on the Internet compare with the strategy card I bought in the gift shop?”
A close parallel to this is exploring variations in rules and how these different rules have an influence on outcomes. Questions include “What should we do with the 2x and 10x odds offers in Craps?” “How should we modify our play for a single-deck Blackjack game with 6:5 blackjack odds?”
Our context does not include exploring or designing new casino games. Our context also excludes multi-player games like poker. We would like to be able to include additional against-the-house games like Pai Gow Poker, Caribbean Stud Poker, and Baccarat.
Problem. Our problem is to answer the following question: For a given game, what player strategies produce the best results?
Forces. There are a number of forces that influence our choice of solution. First, we want an application that is relatively simple to build. Instead of producing an interactive user interface, we will produce raw data and statistical summaries. If we have little interaction, a command-line interface will work perfectly. We can have the user specify a player strategy and the application respond with a presentation of the results. If the results are tab-delimited, they can be pasted into a spreadsheet for further analysis.
Another force that influences our choice of solution is the need to be platform and language agnostic. In this case, we have selected an approach that works well on POSIX-compliant operating systems (i.e., Linux, MacOS, and all of the proprietary UNIX variants), and also works on non-compliant operating systems (i.e., all of the Windows versions). We have chosen two OO languages that work identically on both platform families: Java and Python.
We also need to strike a balance between interesting programming, probability theory and statistics. On one hand, the simplicity of these games means that complete analyses have been done using probability theory. However, that's not a very interesting programming exercise, so we will ignore the pure probability theory route in favor of learning OO design and programming.
Another force is the desire to reflect actual game play. While a long-running simulation of thousands of invidual cycles of play will approach the theoretical results, people typically don't spend more than a few hours at a table game. If, for example, a Roulette wheel is spun once each minute, a player is unlikely to see more that 480 spins in an 8-hour evening at a casino. Additionally, many players have a fixed budget, and the betting is confined by table limits. Finally, we need to address the subject of “money management”: a player may elect to stop playing when they are ahead. This structures our statistical analysis: we must simulate sessions of play that are limited in time, the amount lost and the amount won.
Solution. Our overall goal is to produce an application that allows us to experiment with different casino game betting strategies. We'll build a simple, command-line simulator that provides a reliable, accurate model of the game. We need to be able to easily pick one of a variety of player betting strategies, play a number of simulated rounds of the game, and produce a statistical summary of the results of that betting strategy.
Consequences. One of the most important consequences of our solution is that we will build an application into which new player betting strategies can be inserted. Clever gamblers invent new strategies all the time. We will not know all of the available strategies in advance, so we will not be able to fully specify all of the various design details in advance. Instead, we will find ourselves reworking some parts of the solution, to support a new player betting strategy. This forces us to take an agile approach to the design and implementation.
We have a single, small use case. We have some opinions on what makes a useful use case in our Soapbox on Use Cases. There is a single actor, the “investigator”. The actor's goal is to see the expected results of using a particular strategy for a particular game. The typical scenario is the following.
Procedure 1.1. Basic Use Case
Actor
Specifies which game and betting strategy to test. The strategy may need additional parameters, like an initial budget, or stake. The game may require additional parameters, like betting limits.
System
Responds with a statistical summary of the outcomes after a fixed number of cycles (spins, or throws or hands). The number of cycles needs to be small (on the order of 200, to reflect only a few hours of play).
This system use case is embedded in an overall cycle of investigation that forms a kind of business use case. From this overall view, the actor's goal is to find an optimal strategy for a given game. The procedure includes the following steps.
Procedure 1.2. Business Use Case
Actor
Researches alternative strategies. Uses IDE to build new classes.
IDE
Creates new classes for the simulator.
Actor
Uses the Basic Use Case. Runs the simulator with selection of game and strategy.
Simulator
Responds with statistical results.
Actor
Evaluates the results. Uses a spreadsheet or other tool for analysis and visualization.
We are addressing parts of this larger business use case. While not describing the detailed “how-to” of using the IDE to build the new classes, we will address the design of those new classes. Additionally, we won't address how to analyze the results.
From reading the problem and use case information, we can identify at least the following four general elements to our application.
When we look at common design patterns, the Model-View-Control pattern often helps to structure applications. A more sophisticated, transactional application may require a more complex structure. However, in this case, the game, the player, and the statistics are the model. The command line selection of player and the reporting of raw data is the view. The overall control component creates the various objects to start the simulation.
While interesting, we will not pursue the design of a general-purpose simulation framework. Nor will we use any of the available general frameworks. While these are handy and powerful tools, we want to focus on developing application software “from scratch” as a learning exercise.
Our solution will depend heavily on desktop integration: the actor will use their IDE to create a strategy and build a new version of the application program. Once the application is built, the actor can run the application from the command line, collecting the output file. The statistical results file can be analyzed using a spreadsheet application. There are at least three separate application programs involved: the IDE (including editor and compiler), the simulator, the spreadsheet used for analysis.
A typical execution of the simulator will look like the following example.
Example 1.1. Sample Java Execution
javacasino.MainCrapsSim
--Dplayer.name="Player1326"
>details.log
We are intentionally limiting our approach to a simple command-line application using the default language libraries. Avoiding additional libraries assures a “lowest-common denominator” multi-platform application. For Java, this standard is the J2SE set of libraries; we won't use any J2EE extensions. For Python, it is the base installation.
There are a number of more technical considerations that we will expand in the section called “Getting Started”. These include the use of an overall simulation framework and an approach for unit testing.
Among the topics this book deals with in a casual -- possibly misleading -- manner are probability and statitics. Experts will spot a number of gaps in our exposition. For example, there isn't a compelling need for simulation of the simpler games of Craps and Roulette, since they can be completely analyzed. However, our primary objective is to study programming, not casino games, therefore we don't mind solving known problems again. We are aware that our statistical analysis has a number of deficiencies. We will avoid any deeper investigation into statistics.
We want to focus on technical skills; we won't follow any particular software development methodology too closely. We hesitate to endorse a specific methodology; doing so inevitably alienates readers who embrace a different methodology. To continue this rant, we find that almost everyone has an existing notion of the proper way to organize software development work. This leads to the common practice of customizing methodologies, in most cases without a deep background in the methodology or the changes being made
The exercises are presented as if we are doing a kind of iterative design with very, very small deliverables. We present the exercises like this for a number of reasons.
First, we find that beginning designers work best with immediate feedback on their design decisions. While we present the design in considerable detail, we do not present the final code. Programmers new to OO design will benefit from repeated exposure to the transformation of problem statement through design to code.
Second, for iterative or agile methodologies, this presentation parallels the way software is developed. A project manager may use larger collections of deliverables. However, the actual creation of functional source eventually decomposes into classes, fields and methods. For project managers, this exposition will help them see where and how rework can occur; giving them a chance to plan for the kind of learning that occur in most projects.
Third, for project teams using a strict waterfall methodology -- with all design work completed before any programming work -- the book can be read in a slightly different order. From each exercise chapter, read only the overview and design sections. From that information, integrate the complete design. Then proceed through the deliverables sections of each chapter, removing duplicates and building only the final form of the deliverables based on the complete design. This will show how design rework arises as part of a waterfall methodology.
We try to embrace a variety of deliverables in addition to working source code. In particular, we expect unit test classes in addition to the functional classes. Further, we expect Javadoc comments or Python docstrings in each class. Additionally, we will need to write some demonstration classes in order to determine the exact sequence of random numbers that are created from a specific seed; these are not deliverable as part of the final application, but will be necessary to construct proper unit tests.
On Rework. In the section called “Problem Statement”, we described the problem. In the section called “Solution Approach”, we provided an overview of the solution. The following parts will guide you through an incremental design process; a process that involves learning and exploring. This means that we will coach you to build classes and then modify those classes based on lessons learned during later steps in the design process. See our Soapbox on Rework for an opinion on the absolute necessity for design rework.
We don't simply present a completed design. We feel that it is very important follow a realistic problem-solving trajectory so that beginning designers are exposed to the decisions involved in creating a complete design. In our experience, all problems involve a considerable amount of “learn as you go”. We want to reflect this in our series of exercises. In many respects, a successful OO design is one that respects the degrees of ignorance that people have when starting to build software. We will try to present the exercises in a way that teaches the reader how to manage ignorance and still develop valuable software.
Decision-Making. Many of the chapters will include some lengthy design decisions that appear to be little more than hand-wringning over nuances. While this is true to an extent, we need to emphasize our technique for doing appropriate hand-wringing over OO design. We call it “Looking For The Big Simple”, and find that managers don't often permit the careful enumeration of all the alternatives and the itemization of the pros and cons of each choice. We have worked with managers who capriciously play their schedule or budget trump cards, stopping useful discussion of alternatives. This may stem from a fundamental discomfort with the technology, and a consequent discomfort of appearing lost in front of team members and direct reports. Our suggestion in this book can be summarized as follows:
Good OO design comes from a good process for technical decision-making.
First, admit what we don't know, and then take steps to reduce our degrees of ignorance.
Which means not saying “work smarter not harder” unless we also provide the time and budget to actually get smarter. The learning process, as with all things, must be planned and managed. Our lesson learned from Blaise Pascal is that a little more time spent on design can result in considerable simplification, which will reduce development and maintenance costs.
On Reuse. While there is a great deal of commonality among the three games, the exercises do not start with an emphasis on constructing a general framework. We find that too much generalization and too much emphasis on reuse is not appropriate for beginning object designers. See Soapbox on Reuse for an opinion on reuse. Additionally, we find that projects that begin with too-lofty reuse goals often fail to deliver valuable solutions in a timely fashion. We prefer not to start out with a goal that amounts to boiling the ocean to make a pot of tea.
Design Patterns. These exercises will refer to several of the “Gang of Four” design patterns in Gamma95. The Design Patterns book is not a prerequisite; we use it as reference material to provide additional insight into the design patterns used here. We feel that use of common design patterns significantly expands the programmer's repertoire of techniques. We note where they are appropriate, and provide some guidance in their implementation.
In addition, we reference several other design patterns which are not as well documented. These are, in some cases, patterns of bad design more than patterns of good design.
Unit Tests. The deliverables section summarizes the classes to be built and the unit testing that is expected. We feel that unit testing is a critical skill, and emphasize it throughout the inividual exercises. We don't endorse a particular technology for implementing the unit tests. There are several approaches to unit testing that are in common use.
Formal Unit Tests.
For formal testing of some class, X, we create a separate class, TestX, which creates instances of X and exercises those instances to be sure they work. In Java, this is often done with JUnit. In Python, the unittest module is the mechanism for doing formal unit tests. Additionally, many Python developers also use the doctest module to assure that the sample code in the docstrings is actually correct.
Informal Unit Tests. This is often done by include a main program in the class definition file. We'll show two examples of this. By following the standard unit test naming conventions, these informal tests can be easily upgraded to more formal JUnit or PyUnit tests.
In Python, an informal is done by including the following block of code section in a module file. Each test method must have a name that starts with “test” to be compatible with the Python PyUnit module.
Example 1.2. Informal Python Unit Test
def testX():
test procedure X goes here
def testY():
test procedure Y goes here
if __name__ == "__main__":
testX()
testY()
In Java, this is done by putting
public static void main(String[] args);
in the class source file. Each test method must have a name that starts with “test” to be compatible with the Java JUnit package.
This part describes the game of Roulette. Roulette is a stateless game with numerous bets and a very simple process for game play.
The chapters of this part present the details on the game, an overview of the solution, and a series of sixteen exercises to build a complete simulation of the game, plus a variety of betting strategies. Each exercise chapter builds at least one class, plus unit tests; in some cases, this includes rework of previous deliverables.
Table of Contents
Table of Contents
In the first section we will present a summary of the game of Roulette as played in most American casinos.
We will follow this with a review the various bets available on the Roulette table in some depth. The definition of the various bets is an interesting programming exercise, and the first four exercise chapters will focus on this.
Finally, we will describe some common betting strategies that we will simulate. The betting strategies are interesting and moderately complex algorithms for changing the amount that is used for each bet in an attempt to recoup losses.
The game of Roulette centers around a wheel with thirty-eight numbered bins. The numbers include 0, 00 (double zero), 1 through 36. The table has a surface marked with spaces on which players can place bets. The spaces include the 38 numbers, plus a variety of additional bets, which will be detailed below. After the bets are placed by the players, the wheel is spun by the house, a small ball is dropped into the spinning wheel; when the wheel stops spinning, the ball will come to rest in one of the thirty-eight numbered bins, defining the winning number. The winning number and all of the related winning bets are paid off; the losing bets are collected. Roulette bets are all paid off using odds, which will be detailed with each of the bets, below.
The numbers from 1 to 36 are colored red and black in an arbitrary pattern. They fit into various ranges, as well as being even or odd, which defines many of the winning bets related to a given number. The numbers 0 and 00 are colored green, they fit into none of the ranges, and are considered to be neither even nor odd. There are relatively few bets related to the zeroes. The geometry of the betting locations on the table defines the relationships between number bets.
There are slight variations in Roulette between American and European casinos. We'll focus strictly on the American version.
There are a variety of bets available on the Roulette table. Each bet has a payout, which is stated as n:1 where n is the multiplier that defines the amount won based on the amount bet.
A $5 bet at 2:1 will win $10. After you are paid, there will be $15 sitting on the table, your original $5 bet, plus your $10 additional winnings.
Not all games state their odds using this convention. Some games state the odds as “2 for 1”. This means that the total left on the table after the bets are paid will be two times the original bet. So a $5 bet will win $5, there will be $10 sitting on the table.
The table is divided into two classes of bets. The “inside” bets are the 38 numbers and small groups of numbers; these bets all have relatively high odds. The “outside” bets are large groups of numbers, and have relatively low odds. If you are new to casino gambling, see Odds and Payouts for more information on odds and why they are offered.
The following bets are the “outside” bets. Each of these involves a group of twelve to eighteen related numbers. None of these outside bets includes 0 or 00. The only way to bet on 0 or 00 is to place a straight bet on the number itself, or use the five-number combination bet.
Perhaps because Roulette is a relatively simple game, elaborate betting systems have evolved around it. Searches on the Internet turn up a many copies of the same basic descriptions for a number of betting systems. Our purpose is not to uncover the actual history of these systems, but to exploit them for simple OO design exercises. Feel free to research additional betting systems or invent your own.
The Martingale system starts with a base wagering amount, w, and a count of the number of losses, c, initially 0. Each loss doubles the bet; any given spin will place an amount of w*2 c on a 1:1 proposition (for example, red). When a bet wins, the loss count is reset to zero; resetting the bet to the base amount, w. This assures that a single win will recoup all losses.
Note that the casinos effectively prevent successful use of this system by imposing a table limit. At a $10 Roulette table, the limit may be as low as $1,000. A Martingale bettor who lost six times in a row would be facing a $640 bet, and after the seventh loss, their next bet would exceed the table limit. At that point, the player is unable to recoup all of their losses. Seven losses in a row is only a 1 in 128 probability; making this a relatively likely situation.
Another system is to wait until some number of losses have elapsed. For example, wait until the wheel has spun seven reds in a row, and then bet on black. This can be combined with the Martingale system to double the bet on each loss as well as waiting for seven reds before betting on black.
Another betting system is called the 1-3-2-6 system. The idea is to avoid the doubling of the bet at each loss and running into the table limit. Rather than attempt to recoup all losses in a single win, this system looks to recoup all losses by waiting for four wins in a row. The sequence of numbers (1, 3, 2 and 6) are the multipliers to use when placing bets after winning. At each loss, the sequence resets to the multiplier of 1. At each win, the multiplier is advanced through the sequence. After one win, the bet is now 3w. After a second win, the bet is reduced to 2w, and the winnings of 4w are “taken down” or removed from play. In the event of a third win, the bet is advanced to 6w. Should there be a fourth win, the player has doubled their money, and the sequence resets.
Another method for tracking the lost bets is called the Cancellation system or the Labouchere system. The player starts with a betting budget allocated as a series of numbers. The usual example is 1, 2, 3, 4, 5, 6, 7, 8, 9. Each bet is sum of the first and last numbers in the last. In this case 1+9 is 10. At a win, cancel the two numbers used to make the bet. In the event of all the numbers being cancelled, reset the sequence of numbers and start again. For each loss, however, add the amount of the bet to the end of the sequence as a loss to be recouped.
Here's an example of the cancellation system using 1, 2, 3, 4, 5, 6, 7, 8, 9.
Bet 1+9. A win. Cancel 1 and 9 leaving 2, 3, 4, 5, 6, 7, 8.
Bet 2+8. A loss. Add 10 leaving 2, 3, 4, 5, 6, 7, 8, 10.
Bet 2+10. A loss. Add 12 leaving 2, 3, 4, 5, 6, 7, 8, 10, 12.
Bet 2+12. A win. Cancel 2 and 12 leaving 3, 4, 5, 6, 7, 8, 10.
Next bet will be 3+10.
A player could use the Fibonacci Sequence to structure a series of bets in a kind of cancellation system. The Fibonacci Sequence is 1, 1, 2, 3, 5, 8, 13, .... At each loss, the sum of the previous two bets -- the next number in the sequence -- becomes the new bet amount. In the event of a win, the last two numbers in the sequence are removed. This allows the player to easily track our accumulated losses, with bets that could recoup those losses through a series of wins.
The first section is a survey of the classes gleaned from the general problem statement in Problem Statement as well as the problem details in Roulette Details. This survey is drawn from a quick overview of the key nouns in these sections.
Given this survey of the candidate classes, the second section is a walkthrough of the possible design that will refine the definitions, and give us some assurance 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 about this preliminary design information. This should help clarify the design presentation and set the stage for the various development exercises in the chapters that follow.
To provide a starting point for the development effort, we have to identify the objects and define their responsibilities. The central principle behind the allocation of responsibility is encapsulation; we do this by attempting to isolate the information or isolate the processing that must be done. Encapsulation assures that the methods of a class are the exclusive users of the fields of that class. It also makes each class very loosely coupled with other classes; this permits change without a ripple through the application. For example, each Outcome contains both the name and the payout odds. That way each Outcome can be used to compute a winning amount, and no other element of the simulation needs to share the odds information or the payout calculation.
In a few cases, we have looked forward to anticipate some future considerations. One such consideration is the house “rake”, also known as the “vigorish”, “vig”, or commission. In some games, the house makes a 5% deduction from some payouts. This complexity is best isolated in the Outcome class. Roulette doesn't have any need for a rake, since the presence of the 0 and 00 on the wheel gives the house a little over 5% edge on each bet. We'll design our class so that this can be added later when we implement Craps.
In reading the background information and the problem statement, we noticed a number of nouns that seemed to be important parts of the game we are simulating.
| Wheel |
| Bet |
| Bin |
| Table |
| Red |
| Black |
| Green |
| Number |
| Odds |
| Player |
| House |
One common development milestone is to be able to develop a class model in the Unified Modeling Language (UML) to describe the relationships among the various nouns in the problem statement. Building (and interpreting) this model takes some experience with OO programming. In this first part, we'll avoid doing extensive modeling. Instead we'll simply identify some basic design principles. We'll focus in on the most important of these nouns and describe the kinds of classes that you will build.
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 preliminary classes more than once.
| Class | Responsibilities | Collaborations |
|---|---|---|
Outcome | A name for the bet and the payout odds. This isolates the calculation of the payout amount. Example: "Red", "1:1". | Collected by Wheel into the bins that reflect the bets that win; collected by Table into the available bets for the Player; used by Game to compute the amount won from the amount that was bet.
|
Wheel | Selects the Outcomes that win. This isolates the use of a random number generator to select Outcomes; and it encapsulates the set of winning Outcomes that are associated with each individual number on the wheel. Example: the “1” bin has the following winning Outcomes: “1”, “Red”, “Odd”, “Low”, “Column 1”, “Dozen 1-12”, “Split 1-2”, “Split 1-4”, “Street 1-2-3”, “Corner 1-2-4-5”, “Five Bet”, “Line 1-2-3-4-5-6”,
“00-0-1-2-3”, “Dozen 1”, “Low” and “Column 1”.
| Collects the Outcomes into bins; used by the overall Game to get a next set of winning Outcomes. |
Table | A collection of bets placed on Outcomes 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. | Collects the Outcomes; used by Player to place a bet amount on a specific Outcome; used by Game to compute the amount won from the amount that was bet. |
Player | Places bets on Outcomes, updates the stake with amounts won and lost. | Uses Table to place bets on Outcomes; used by Game to record wins and losses. |
Game | Runs the game: gets bets from Player, spins Wheel, collects losing bets, pays winning bets. This encapsulates the basic sequence of play into a single class. | Uses Wheel, Table, Outcome, Player. The overall statistical analysis will play a finite number of games and collect the final value of the Player's stake. |
The class Player has 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, so that changes to this class have no ripple effect in other classes of the application.
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 Class-Reponsibility-Collaborators (CRC) walkthrough. As preparation, get some 5" x 8" notecards. On each card, write down the name of a class, the responsibilities and the collaborators. Leave plenty of room around the responsibilities and collaborators to write notes. We've only identified five classes, so far, but others always show up during the walkthrough.
During the walkthrough, we identify areas of responsibility, allocate them to classes of objects and define any collaborating objects. An area of responsibility is a thing to do, a piece of information, a result. Sometimes a big piece of responsibility can be broken down into smaller pieces, and those smaller pieces assigned to other classes. There are a lot of reasons for decomposing, the purpose of this book is to explore many of them in depth. Therefore, we won't justify any of our suggestions until later in the book. For now, follow along closely to get a sense of where the exercises will be leading.
The basic processing outline is the responsibility of the Game class. To start, locate the Game card.
Our preliminary note was that this class “Runs the game.” The responsibilities section has a summary of four steps involved in running the game.
The first step is “gets bets from Player.” Find the Player card.
Does a Player collaborate with a Game to place bets? If not, update the cards as necessary to include this.
One of the responsibilities of a Player is to place bets. The step in the responsibility statement is merely “Places bets on Outcomes.” Looking at the classes, we note that the Table contains the amounts placed on the Bets. Fix the collaboration information on the Player to name the Table class. Find the Table card.
Does a Table collaborate with a Player to accept the bets? If not, update the cards as necessary to include this.
What card has responsibility for the amount of the bet? It looks like Table. We note one small problem: the Table contains the collection of amounts bet on Outcomes. What class contains the individual “amount bet on an Outcome?” This class appears to be missing. We'll call this new class Bet and start a new card. We know one responsibility is to hold the amount bet on a particular Outcome. We know three collaborators: the amount is paired with an Outcome, all of the Bets are collected by a Table, and the Bets are created by a Player. We'll update all of the existing cards to name their collaboration with Bet.
What card has responsibility for keeping all of the Bets? Does Table list that as a responsibility? We should update these cards to clarify this collaboration.
You should continue this tour, working your way through spinning the Wheel to get a list of winning Outcomes. From there, the Game can get all of the Bets from the Table and see which are based on winning Outcomes and which are based on losing Outcomes. The Game can notify the Player of each losing Bet, and notify the Player of each winning Bet, using the Outcome to compute the winning amount.
This walkthrough will give you an overview of some of the interactions among the objects in the working application. You may uncover additional design ideas from this walkthrough. The most important outcome of the walkthrough is a clear sense of the responsibilities and the collaborations required to create the necessary application behavior.