A winning Mah Jongg hand has five scoring
sets exactly one of which is a pair. There are
four varieties of set: pair, three of a kind, three in a row of the same
suit, and four of a kind. The most common winning hands have 14 tiles: 4
sets of three and a pair. Each four of a kind extends the hand by one
tile to accomodate the larger set.
A Mah Jongg Hand
object, then, is a list of
Tile
s. This class needs a method,
mahjongg
that returns True
is
the hand is a winning hand. The evaluation is rather complex, because a
tile can participate in a number of sets, and several alternative
interpretations may be necessary to determine the appropriate use for a
given tile.
Consider a hand with 2, 2, 2, 3, 4 of bamboo. This is either a set
of three 2's and a nonscoring 3 and 4, or it is a pair of 2's and a
sequence of 2, 3, 4.
The mahjongg
method, then must create five
Set
objects, assigning individual
Tile
s to the Set
s until
all of the Tile
s find a home. The hand is a
winning hand if all sets are full, there are five sets, and one set is a
pair.
We'll cover the design of the Set
classes
in this section, and return to the design of the
Hand
class in the next section.
We can create a class hierarchy around the four varieties of
Set
: pairs, threes, fours and straights. A
PairSet
holds two of a kind: both of the tiles
have the same name or the same suit and rank. A
ThreeSet
and FourSet
are
similar, but have a different expectation for being full. A
SequenceSet
holds three suit tiles of the same
suit with adjacent ranks. Since we will sort the tiles into ascending
order, this set will be built in ascending order, making the comparison
rules slightly simpler.
We'll define a Set
superclass to hold a
sequence of Tile
s. We will be able to add new
tiles to a Set
, as well as check to see if a tile
could possibly belong to a Set
. Finally, we can
check to see if the Set
is full. The superclass,
Set
, is abstract and returns
NotImplemented
for the full
. The
sublasses will override this methods with specific rules appropriate to
the kind of set.
class
Set
:
def __init__(self):
def __str__(self):
def canContain(self,
aTile
):
def add(self,
aTile
):
def full(self):
def fallback(self,
tileStack
):
def pair(self):

The Set
__init__
function should create an internal list to store the tiles.

The add
method appends the new tile to
the internal list. A pop function can remove the last tile appended
to the list.

The superclass canContain
method returns
True
if the list is empty; it returns
False
if the list is full. Otherwise it compares
the new tile against the last tile in the list to see if they are
equal. Since most of the subclasses must match exactly, this rule is
what is used. The straight subclass must override this to compare
suit and rank correctly.

The superclass fallback
pushes all of the
tiles from the Set
back onto the given stack.
The superclass version pushes the tiles and then returns
None
. Each subclass must override this to return
a different fallback Set instance.

The superclass pair
returns
False
. The PairSet
subclass must override this to return
True
.
An important note about the fallback
is that
the stack that will be given as an argument in
tileStack
is part of the Hand
,
and is using is maintainted by doing tileStack.pop(0)
to
get the first tile, and tileStack.insert( 0, aTile )
to
push a tile back onto the front of the hand of tiles.
We'll need the following four subclasses of
Set
.

FourSet
. Specializes Set
for sets of four
matching tiles. The full
method returns
True
when there are four elements in the list.
The fallback
method pushes the set's tiles
onto the given tileStack
; it returns a new
ThreeSet
with the first tile from the
tileStack
.

ThreeSet
. Specializes Set
for sets of three
matching tiles. The full
method returns
True
when there are three elements in the list.
The fallback
method pushes the set's tiles
onto the given tileStack
; it returns a new
SequenceSet
with the first tile from the
tileStack
.

SequenceSet
. Specializes Set
for sets of three
tiles of the same suit and ascending rank. The
belongs
returns True
for
an empty list of tiles, False
for a full list
of tiles, otherwise it compares the suit and rank of the last tile
in the list with the new tile to see if the suits match and the
new tile's rank is one more than the last tile in the list. The
full
method returns True
when there are three elements in the list. The
fallback
method pushes the set's tiles onto
the given tileStack
; it returns a new
PairSet
with the first tile from the
tileStack
.

PairSet
. The full
method returns
True
when there are two elements in the list.
The fallback
method is inherited from the
superclass method in Set
; this method
returns None, since there is no fallback from a pair. This
subclass also returns True
for the
pair
method.
The idea is to attempt to use a FourSet
to
collect a group of tiles. If this doesn't work out, we put the tiles
back into the hand, and try a ThreeSet
. If this
doesn't work out, we put the tiles back and try a
SequenceSet
. The last resort is to try a
PairSet
. There is no fallback after a pair set,
and the hand cannot be a winner.