SevenReds Player Class

This section introduces an additional specialization of the Martingale betting strategy. Adding this new subclass should be a small change to the main application class. Additionally, we’ll also address some issues in how an overall application is composed of individual class instances.

We’ll also revisit a question in the design of the Table class. Should we really be checking for a minimum? Or was that needless?

In SevenReds Player Analysis we’ll examine the general strategy this player will follow.

We’ll revisit object-oriented design by composition in Soapbox on Composition.

In SevenReds Design we’ll look at the design of this player. We’ll need to revise the overall design for the abstract Player class, also, which we’ll look at in Player Rework.

These design changes will lead to other changes. We’ll look at these changes in Game Rework and Table Rework.

This will lead to SevenReds Player Deliverables, which enumerates all of the deliverables for this chapter.

SevenReds Player Analysis

The SevenReds player subclass waits for seven red wins in a row before betting black. This is a subclass of the Player class. We can create a subclass of our main Simulator class to use this new SevenReds instance.

We note that the Passenger57 class betting is stateless: this class places the same bets over and over until they are cleaned out or their playing session ends.

The Martingale player’s betting, however, is stateful. This player changes the bet based on wins and losses. The state is a loss counter than resets to zero on each win, and increments on each loss.

Our SevenReds player will have two states: waiting and betting. In the waiting state, they are simply counting the number of reds. In the betting state, they have seen seven reds and are now playing the Martingale system on black. We will defer serious analysis of this stateful betting until some of the more sophisticated subclasses of the Player class. For now, we will simply use an integer to count the number of reds.

Game Changer

Currently, the Player object is not informed of the final outcome unless they place a bet. We designed the :class: Game object to evaluate the Bet instances and notify the Player object of just their Bet instances that were wins or losses. We will need to add a method to the Player class to be given the overall list of winning Outcome instances even when the Player object has not placed a bet.

Once we have updated the design of :class: Game class to notify the Player object, we can add the feature to the new SevenReds class. Note that we intend introduce each new betting strategy via creation of new subclasses. A relatively straightforward update to our simulation main program allows us to use these new subclasses. The previously working subclasses are left in place, allowing graceful evolution by adding features with minimal rework of existing classes.

In addition to waiting for the wheel to spin seven reds, we will also follow the Martingale betting system to track our wins and losses, assuring that a single win will recoup all of our losses. This makes the SevenReds class a further specialization of the Martingale class. We will be using the basic features of the Martingale class, but doing additional processing to determine if we should place a bet or not.

Introducing a new subclass should be done by upgrading the main program. See Soapbox on Composition for comments on the ideal structure for a main program. Additionally, see the Roulette Solution Questions and Answers FAQ entry.

Table Changes

When we designed the Table class, we included a notion of a valid betting state. We required the sum of all bets placed by a Player to be below some limit. We also required that there be a table minimum present.

A casino has a table minimum for a variety of reasons. Most notably, it serves to distinguish casual players at low-stakes tables from “high rollers” who might prefer to play with other people who wager larger amounts.

In the rare event that a player is the only person at a roulette wheel, the croupier won’t spin the wheel until a bet is placed. This is an odd thing. It’s also very rare. Pragmatically, there are almost always other players, and the wheel is likely to be spun even if a given player is not betting.

Our design for a table really should not have any check for a minimum bet. It’s a rule that doesn’t make sense for the kind of simulation we’re doing. The simulated results can be scaled by the minimum betting amount, so it’s easiest to think of the bets as multiples of the minimum and use simple integer bet amounts.

Soapbox on Composition

Generally, a solution is composed of a number of objects. However, the consequences of this can be misunderstood. Since the solution is a composition of objects, it falls on the main method to do create the composition and do nothing more.

Our ideal main program creates and composes the working set of objects, then start the processing. In some cases, environment variables, command-line arguments and options, and configuration files may tailor what is built. For these simple exercises, however, we’re omitting the parsing of command-line parameters, and simply creating the necessary objects directly.

A main program should, therefore, look something like the following:

wheel = Wheel()
table = Table()
game = Game(wheel, table)
player = SevenReds(table)
sim = Simulator(game, player)
sim.gather()

We created an instance of the Wheel class to contain the bins and outcomes. We created an instance of the Table class as a place to put the bets. We’ve combined these two objects into an instance of the :class: Game object.

When we created the player object, we could have used the Martingale class or the Passenger57 class. The player object can use the Table object to get the Wheel instance. This Wheel instance provides the outcomes used to build bets.

The real work is done by Simulater.gather(). This relies on the game, table, and player to create the data we can analyze.

In some instances, the construction of objects is not done directly by the main method. Instead, the main method will use Builders to create the various objects. The idea is to avoid mentioning the class definitions directly. We can upgrade or replace a class, and also upgrade the Builder to use that class appropriately. This isolates change to the class hierarchy and a builder function.

SevenReds Design

class SevenReds

SevenReds is a Martingale player who places bets in Roulette. This player waits until the wheel has spun red seven times in a row before betting black.

Fields

SevenReds.redCount

The number of reds yet to go. This starts at 7 , is reset to 7 on each non-red outcome, and decrements by 1 on each red outcome.

Note that this class inherits betMultiple. This is initially 1, doubles with each loss and is reset to one on each win.

Methods

SevenReds.placeBets(self) → None

If redCount is zero, this places a bet on black, using the bet multiplier.

SevenReds.winners(self, outcomes: Set[Outcome]) → None
Parameters

outcomes (Set of Outcome instances) – The Outcome set from a Bin.

This is notification from the :class: Game of all the winning outcomes. If this vector includes red, redCount is decremented. Otherwise, redCount is reset to 7.

Player Rework

We’ll need to revise the Player class to add the following method. The superclass version doesn’t do anything with this information. Some subclasses, however, will process this.

Player.winners(self, outcomes: Set[Outcome]) → None
Parameters

outcomes (Set of Outcome instances) – The set of Outcome instances that are part of the current win.

The game will notify a player of each spin using this method. This will be invoked even if the player places no bets.

Game Rework

We’ll need to revise the :class: Game class to extend the cycle method. This method must provide the winning bin’s Outcome set.

Table Rework

We’ll need to revise the Table class to remove any minimum bet rule. If there are no bets, the game should still proceed.

SevenReds Player Deliverables

There are six deliverables from this exercise. The new classes will require complete Python docstrings.

  • A revision to the Player class to add the Player.winners() method. The superclass version doesn’t do anything with this information. Some subclasses, however, will process this.

  • A revision to the Player unit tests.

  • A revision to the :class: Game class. This will call the winners() with the winning Bin instance before paying off the bets.

  • A revision to the Table class. This will allow a table with zero bets to be considered valid for the purposes of letting the game continue.

  • The SevenReds subclass of Player class.

  • A unit test of the SevenReds class. This test should synthesize a fixed list of Outcome instances, Bin instances and the call a SevenReds instance with various sequences of reds and blacks. One test cases can assure that no bet is placed until 7 reds have been seen. Another test case can assure that the bets double (following the Martingale betting strategy) on each loss.

  • A main application function that creates the necessary objects, runs the Simulator’s gather() method, and writes the available outputs to sys.stdout

For this initial demonstration program, it should simply print the list of maxima, and the list of session lengths. This raw data can be redirected to a file, loaded into a spreadsheet and analyzed.

Looking Forward

We now have a few players to compare. It’s time to look at some basic statistics to compare the performance of the various betting strategies. In the next section we’ll look at some simple statistical processing techniques.