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
Tiles. 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 non-scoring 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
Tiles to the Sets until
all of the Tiles 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 Tiles. 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.