Wheel Class¶
This chapter builds on the previous two chapters, creating a more
complete composite object from the Outcome
and Bin
classes we have already defined. In Wheel Analysis we’ll look
at the responsibilities of a wheel and it’s collaboration.
In the Wheel Design we’ll provide the detailed design information. In the Test Setup we’ll address some considerations for testing a class which has random behavior. In Wheel Deliverables we’ll enumerate what must be built.
Wheel Analysis¶
The wheel has two responsibilities:
Separately, we’ll look at ways to initialize the various Bin
instances that comprise a
standard Roulette wheel.
In The Container Responsibility we’ll look at the container aspect in detail.
In The Random Bin Selection Responsibility we’ll look at the random selection aspects.
Based on this, the Constructing a Wheel section provides a description of how we can build the Wheel instance.
The Container Responsibility¶
Since a Wheel
object contains 38 Bin
instances,
it is a collection. We can review our survey of available collections in
Design Decision – Choosing A Collection for some guidance on how to choose the
best collection.
In this case, the choice of the winning Bin
will be selected by a random numeric index. We need some kind of sequential
collection.
This makes an immutable tuple
very appealing.
This is a subclass of collections.abc.Sequence
and has
the features we’re looking for.
Once we’ve decided to use a sequential collection, we have a second decision.
We need to choose an indexing scheme for the various Bin
instances.
In the case of Roulette, we have a problem with zero and double-zero:
there’s no 00 integer.
The index values of 1
to 36 are logical mappings to Bin
instances based on the straight bet.
The roulette wheel’s bins have the 36 numbers prominently displayed.
The Bin
at index 1 would contain Outcome("1", 35)
among several others.
The Bin
at index 2 would contain Outcome("2", 35)
.
We have a small problem, however, with 0 and 00: we need two separate indexes. While 0 is a valid index, what do we do with 00?
The trick here is to step away from being too literal in our mappings
from numbers to bins. There’s no real reason why the bin with
Outcome("1", 35)
should be at index position 1 in the Wheel
collection.
Because the index of the Bin
doesn’t have any significance at
all, we can assign the Bin
that has the Outcome("00", 35)
to position 37 in the Wheel
collection.
The index value doesn’t actually matter because we’ll never
use the index for any purpose other than random selection.
The Random Bin Selection Responsibility¶
In order for the Wheel
class to select a Bin
instance
at random, we’ll need a random number from 0 to 37 that we can use an an
index. There is an alternative, however.
The random
module offers a Random.choice()
function
which picks a random value from a
sequence. This is ideal for returning a randomly selected Bin
from our list of Bin
instances. The numeric value doesn’t
matter if we use the choice()
method.
Testability. Note that testing a class using random numbers isn’t going to be easy. To do testing properly, need a non-random random number generator with predictable results.
To create a non-random random-number generator, we can do something like the following.
Set a specific seed value. This will generate a known sequence of values.
Create a mock class for the random number generator that returns a known, fixed sequence of values. We can leverage the
unittest.mock
module for this.
We’ll address this in detail in Review of Testability. For now, we’ll suggest using the first technique – set a specific seed value.
Constructing a Wheel¶
Each instance of the Bin
class has a list of Outcome
instances.
The zero (“0”) and double zero (“00”) Bin
instances only have two Outcome
instances.
The other numbers have anywhere from twelve to fourteen Outcome
instances.
Clearly, there’s quite a bit of complexity in building some of the bins.
Rather than dwell on these algorithms, we’ll apply a common OO principle of deferred binding. We’ll build a very basic wheel first and work on the bin-building algorithms in the next chapter.
It’s often simplest to build a class incrementally. This is an example where a simpler overall structure includes rather complex details.
Wheel Design¶
-
class
Wheel
¶ Wheel
contains the 38 individual bins on a Roulette wheel, plus a random number generator. It can select aBin
at random, simulating a spin of the Roulette wheel.
Fields¶
Constructors¶
Methods¶
-
Wheel.
addOutcome
(number: int, outcome: Outcome) → None Adds the given
Outcome
object to theBin
instance with the given number.- Parameters
bin (int) – bin number, in the range zero to 37 inclusive.
outcome (Outcome) – The Outcome to add to this Bin
Test Setup¶
We need a controlled kind of random number generation for testing purposes. This is done with tests that look like the following:
Test Outline
def test_wheel_sequence():
wheel = Wheel()
wheel.addOutcome(8, Outcome("test", 1))
wheel.rng.seed(1)
assert Outcome("test", 1) in wheel.choose()
The values delivered from this seeded random number generator can be seen from this experiment.
Fixed pseudo-random sequence
>>> x = random.Random()
>>> x.seed(1)
>>> [x.randint(0,37) for i in range(10)]
[8, 36, 4, 16, 7, 31, 28, 30, 24, 13]
This allows us to predict the output from the Wheel.next()
method.
Because the first value is 8, we only need to put an outcome into
Bin
instance at position 8 in the Wheel
collection.
The special `` Outcome(“test”, 1)`` object should be found
in the expected Bin
instance.
Wheel Deliverables¶
There are three deliverables for this exercise. The new class and the unit test will have Python docstrings.
The
Wheel
class. This is part of theroulette.py
file, along with theOutcome
andBin
classes.A class which performs a unit test of building the
Wheel
class. The unit test should create several instances of theOutcome
class, two instances of theBin
class, and an instance of theWheel
class. The unit test should establish thatBin
instances can be added to theWheel
.A class which tests the Wheel class by selecting “random” values from a
Wheel
object using a fixed seed value.