File indexing completed on 2024-04-14 03:59:13

0001 #!/usr/bin/env python3
0002 # -*- coding: utf-8 -*-
0003 
0004 """
0005 Copyright (C) 2009-2016 Wolfgang Rohdewald <wolfgang@rohdewald.de>
0006 
0007 SPDX-License-Identifier: GPL-2.0
0008 
0009 """
0010 
0011 import unittest
0012 
0013 from common import Debug  # pylint: disable=unused-import
0014 from wind import Wind, East, South, West, North
0015 from player import Players
0016 from game import PlayingGame
0017 from hand import Hand, Score
0018 from tile import TileList
0019 from predefined import ClassicalChineseDMJL, ClassicalChineseBMJA
0020 
0021 RULESETS = []
0022 
0023 # test each of those rulesets twice: once with limit, once with roof off
0024 for testRuleset in [ClassicalChineseDMJL, ClassicalChineseBMJA] * 2:
0025     _ = testRuleset()
0026     _.load()
0027     RULESETS.append(_)
0028 
0029 for _ in RULESETS[2:]:
0030     _.roofOff = True
0031 
0032 # Do not create our test players in the data base:
0033 Players.createIfUnknown = str
0034 
0035 # RULESETS=RULESETS[:1]
0036 GAMES = [PlayingGame([tuple([wind, str(wind.char)]) for wind in Wind.all4], x) for x in RULESETS]
0037 PROGRAM = None
0038 
0039 
0040 class Expected:
0041 
0042     """define what we expect from test"""
0043 
0044     def __init__(self, won, points, doubles, limits):
0045         self.won = won
0046         self.score = Score(points, doubles, limits)
0047 
0048     def __str__(self):
0049         return 'Won with %s' % self.score if self.won else 'Lost with %s' % self.score
0050 
0051 
0052 class Win(Expected):
0053 
0054     """we expect a winning hand"""
0055 
0056     def __init__(self, points=0, doubles=0, limits=0.0):
0057         Expected.__init__(self, True, points, doubles, limits)
0058 
0059 
0060 class NoWin(Expected):
0061 
0062     """we expect a losing hand"""
0063 
0064     def __init__(self, points=0, doubles=0, limits=0.0):
0065         Expected.__init__(self, False, points, doubles, limits)
0066 
0067 
0068 class Helpers:
0069 
0070     """for my test classes"""
0071     # pylint: disable=no-member, too-many-locals
0072 
0073     def scoreTest(self, string, expected, myWind=None, roundWind=None, totals=None):
0074         """execute one scoreTest test"""
0075         if myWind is None:
0076             myWind = East
0077         if roundWind is None:
0078             roundWind = East
0079         for idx, ruleset in enumerate(RULESETS):
0080             if isinstance(expected, list):
0081                 expIdx = idx
0082                 if expIdx >= len(expected):
0083                     expIdx %= len(RULESETS) // 2
0084                 exp = expected[expIdx]
0085             else:
0086                 exp = expected
0087             if exp is None:
0088                 continue
0089             exp.ruleset = ruleset
0090             variants = []
0091             game = GAMES[idx]
0092             for widx, wind in enumerate(Wind.all4):
0093                 game.players[widx].wind = wind
0094             game.winner = game.players[myWind]
0095             game.myself = game.winner
0096             game.roundsFinished = roundWind.__index__()
0097             game.winner.clearCache()
0098             if Debug.hand:
0099                 print('')
0100                 print('starting test for %s' % ruleset.name)
0101             variant = Hand(game.winner, string)
0102             score = variant.score
0103             variants.append(variant)
0104             self.assertTrue(
0105                 variant.won == isinstance(exp, Win) and score == exp.score,
0106                 self.dumpCase(variant, exp, total=None))
0107 # activate depending on what you are testing
0108 #            print(string, 'expected:', expected.__str__()), variant
0109 #            print(ruleset.name.encode('utf-8'))
0110 #            print('\n'.join(variant.explain).encode('ut-f8'))
0111             if totals:
0112                 self.assertTrue(score.total() == totals[idx], '\n'.join(
0113                     self.dumpCase(x, exp, totals[idx]) for x in variants))
0114 
0115     def callingTest(self, string, expected):
0116         """test a calling hand"""
0117         for idx, ruleset in enumerate(RULESETS):
0118             game = GAMES[idx]
0119             game.players[0].clearCache()
0120             hand = Hand(game.players[0], string)
0121             testSays = TileList(
0122                 {x.lastTile.exposed for x in hand.callingHands}).sorted()
0123             if isinstance(expected, list):
0124                 if idx >= len(expected):
0125                     idx %= len(RULESETS) // 2
0126                 expIdx = idx
0127                 if expIdx >= len(expected):
0128                     expIdx %= len(RULESETS) // 2
0129                 exp = expected[expIdx]
0130             else:
0131                 exp = expected
0132             completingTiles = TileList(exp)
0133             self.assertTrue(testSays == completingTiles,
0134                             '%s: %s may be completed by %s but testresult is %s' % (
0135                                 ruleset.name, string, completingTiles or 'None', testSays or 'None'))
0136 
0137     def dumpCase(self, hand, expected, total):
0138         """dump test case"""
0139         assert self
0140         result = []
0141         result.append('')
0142         if not hand:
0143             result.append('Hand was illegal: claimed to be won but was not')
0144         else:
0145             if hand.won != isinstance(expected, Win):
0146                 result.append(
0147                     'hand.won is %s, expected %s' %
0148                     (hand.won, isinstance(expected, Win)))
0149             result.append(hand.string)
0150             roofOff = ' roofOff' if hand.player.game.ruleset.roofOff else ''
0151             score = hand.score
0152             if score != expected.score:
0153                 result.append('%s%s: %s with %s should be %s' % (
0154                     hand.player.game.ruleset.name, roofOff, 'Won' if hand.won else 'Lost', score, expected))
0155                 result.append('hand:%s' % hand)
0156             if total is not None:
0157                 if score.total() != total:
0158                     result.append('%s %s%s: total %s for %s should be %s' % (
0159                         id(
0160                             hand.player.game.ruleset), hand.player.game.ruleset.name, roofOff,
0161                         score.total(), score.__str__(), total))
0162                 result.append('hand:%s' % hand)
0163             result.extend(hand.explain())
0164             result.append(
0165                 'base=%d,doubles=%d,total=%d' %
0166                 (score.points, score.doubles, hand.total()))
0167             result.append('')
0168         return '\n'.join(result)
0169 
0170 # pylint: disable=missing-docstring, too-many-public-methods
0171 
0172 
0173 class Base(unittest.TestCase, Helpers):
0174 
0175     """tests lots of hand examples. We might want to add comments which test should test which rule"""
0176 
0177 
0178 class Partials(Base):
0179 
0180     """some partial hands"""
0181 
0182     def testMe(self):
0183         self.scoreTest('drdrdr fe Ldrdrdrdr', [NoWin(8, 1), NoWin(8, 2)])
0184         self.scoreTest('fe', [NoWin(4), NoWin(4, 1)])
0185         self.scoreTest('fs fw fe fn', [NoWin(16, 1), NoWin(16, 3)])
0186         self.scoreTest('fs ys', [NoWin(8, 1), NoWin(8, 2)], myWind=South)
0187         self.scoreTest('drdrdr Ldrdrdrdr', NoWin(4, 1))
0188 
0189 
0190 class ZeroHand(Base):
0191 
0192     """zero hand games"""
0193 
0194     def testMe(self):
0195         self.scoreTest('c1c2c3 c7c8c9 b2b3b4 c5c5 s1s2s3 fw yw Lc1c1c2c3',
0196                        [Win(points=28, doubles=2), NoWin()], myWind=West, roundWind=North)
0197         self.scoreTest('c1c2c3 c7c8c9 b2b3b4 drdr s1s2s3 fw yw Lc1c1c2c3',
0198                        [Win(points=30, doubles=1), NoWin()], myWind=West, roundWind=North)
0199 
0200 
0201 class FalseColorGame(Base):
0202 
0203     """false color games"""
0204 
0205     def testMe(self):
0206         self.scoreTest(
0207             'c1c1c1 c7c7c7 c2c3c4 c5c5 c6c6c6 Lc5c5c5', [Win(32, 3), Win(28)])
0208         self.scoreTest('c1c2c3 wewewe drdrdr dbdb DgDgDg Ldbdbdb', [
0209             Win(44, 4), Win(38, 2)], myWind=West, roundWind=North)
0210         self.scoreTest(
0211             's1s1s1 wewewe c2c3c4 c5c5 c6c6c6 Lc5c5c5',
0212             [Win(34),
0213              Win(30)],
0214             myWind=West, roundWind=North)
0215         self.scoreTest(
0216             'RB1B1B1B1B2B3B4B5B6B7B8B9DrDr fe ys LDrDrDr', [Win(46, 2), NoWin()])
0217         self.scoreTest(
0218             'b1B1B1b1 RB2B3B4B5B6B7B8B8B8 DrDr fe ys LDrDrDr', [Win(74, 2), NoWin()])
0219         self.scoreTest('b1B1B1b1 RB2B2B2B5B6B7B8B8B8 DrDr fe ys LDrDrDr', [
0220             Win(78, 3), Win(72, 1)], myWind=West)
0221 
0222 
0223 class WrigglingSnake(Base):
0224 
0225     """the wriggling snake as in BMJA"""
0226 
0227     def testMe(self):
0228         self.scoreTest(
0229             'c1c1 c2c3c4 c5c6c7 RC8C9WeWwWsWn Lc1c1c1', [NoWin(), NoWin()])
0230         self.scoreTest(
0231             'c2c3c4 c5c6c7 RC1C1C8C9WeWwWsWn LC1C1C1', [NoWin(), NoWin()])
0232         self.scoreTest(
0233             'c2c3c4 c5c6c7 RC1C1C8C9WeWwWsWn LWnWn', [NoWin(), NoWin()])
0234         self.scoreTest(
0235             'RC1C1C2C3C4C5C6C7C8C9WeWwWsWn LC1C1',
0236             [NoWin(),
0237              Win(limits=1)])
0238         self.callingTest('RS1S3WwS6WsS3S3WnWeS5 s7s8s9 fs', '')
0239         self.callingTest('RS1S2WwS6WsS3S3WnWeS5S7S8S9 fs', '')
0240         self.callingTest('RS1S2WwS6WsS3S4WnWeS5S7S8S9 fs', ['', 's1'])
0241         self.callingTest('RS1S2WwS6WsS3S4WnWeS1S7S8S9 fs', ['', 's5'])
0242 
0243 
0244 class SquirmingSnake(Base):
0245 
0246     """the winding snake"""
0247 
0248     def testMe(self):
0249         self.scoreTest(
0250             'c1c1c1 c3c4c5 c9c9c9 c6c7c8 RC2C2 Lc1c1c1c1',
0251             [Win(limits=1),
0252              NoWin()])
0253         self.scoreTest('c1c1c1 c4c5c6 c9c9c9 c6c7c8 RC2C2 Lc1c1c1c1', [
0254             Win(points=28, doubles=3), NoWin()])
0255         self.scoreTest(
0256             'c1c1c1 c3c4c5 c9c9c9 c6c7c8 RS2S2 Lc1c1c1c1',
0257             [Win(points=28),
0258              NoWin()])
0259         self.scoreTest(
0260             's1s1s1 s2s3s4 s9s9s9 s6s7s8 RS5S5 Ls1s1s1s1',
0261             [Win(limits=1),
0262              NoWin()])
0263         self.scoreTest(
0264             'b1b1b1 c3c4c5 c6c7c8 c9c9c9 RC2C2 Lc3c3c4c5',
0265             [Win(points=28),
0266              NoWin()])
0267         self.scoreTest(
0268             'b1b1b1 c3c4c5 c6c7c8 c9c9c9 RC2C2 Lc4c3c4c5',
0269             [Win(points=30),
0270              NoWin()])
0271         self.scoreTest('RC1C1C1C2C3C4C5C6C7C8C9C9C9C5 LC5', Win(limits=1))
0272         self.scoreTest(
0273             'RC1C1C1C3C4C5C6C7C8C9C9C9C5 LC3',
0274             [NoWin(16),
0275              NoWin(limits=0.4)])
0276         self.scoreTest(
0277             'RC1C1C2C3C4C5C6C7C8C9C9C9C5 LC3',
0278             [NoWin(8),
0279              NoWin(limits=0.4)])
0280 
0281 
0282 class Purity(Base):
0283 
0284     """Purity BMJA"""
0285 
0286     def testMe(self):
0287         self.scoreTest(
0288             'b1b1b1b1 RB2B3B4B5B6B7B8B8B2B2B2 fe fs fn fw LB3B2B3B4',
0289             [Win(points=60, doubles=4), NoWin()])
0290         self.scoreTest(
0291             'b1b1b1 RB3B3B3B6B6B6B8B8B2B2B2 fe fs fn fw LB3', [Win(54, 6), Win(54, 7)])
0292         self.scoreTest(
0293             'b1b1b1 RB3B3B3B6B6B8B8B2B2B2 fe fs fn fw LB3', [NoWin(28, 1), NoWin(28, 6)])
0294         self.scoreTest(
0295             'c1C1C1c1 c3c3c3 c8c8c8 RC4C5C6C7C7 fs fw ys yw Lc8c8c8c8',
0296             [Win(72, 4), Win(72, 2)], myWind=West)
0297 
0298 
0299 class TrueColorGame(Base):
0300 
0301     """true color games"""
0302 
0303     def testMe(self):
0304         self.scoreTest(
0305             'b1b1b1b1 RB2B3B4B5B6B7B8B8B2B2B2 fe fs fn fw LB3B2B3B4',
0306             [Win(points=60, doubles=4), NoWin()])
0307         self.callingTest(
0308             'RB1B2B3B4B5B5B6B6B7B7B8B8B8 LB1', ['b1b3b4b6b7b9', ''])
0309         self.scoreTest(
0310             'b1b1b1B1 RB2B3B4B5B6B7B8B8B2B2B2 fe fs fn fw LB3B2B3B4',
0311             [Win(limits=1),
0312              NoWin()])
0313 
0314 
0315 class OnlyConcealedMelds(Base):
0316 
0317     """only concealed melds"""
0318 
0319     def testMe(self):
0320         self.scoreTest(
0321             'RB1B1B1B1B2B3B4B5B6B7B8B9DrDr fe ys LDrDrDr', [Win(46, 2), NoWin()])
0322         self.scoreTest('RB1B1B1B2B2B2B4B4B4B7B8B9DrDr fe ys LDrDrDr', [
0323             Win(54, 3), Win(48, 1)], myWind=West)
0324         self.scoreTest(
0325             'b1B1B1b1 RB2B3B4B5B6B7B8B8B8DrDr fe ys LDrDrDr', [Win(74, 2), NoWin()])
0326         self.scoreTest('b1B1B1b1 RB2B2B2B5B6B7B8B8B8DrDr fe ys LDrDrDr', [
0327             Win(78, 3), Win(72, 1)], myWind=West)
0328 
0329 
0330 class LimitHands(Base):
0331 
0332     """various limit hands"""
0333 
0334     def testMe(self):
0335         self.scoreTest(
0336             'c1c1c1 c9c9 b9b9b9b9 s1s1s1 s9s9s9 Lc1c1c1c1',
0337             Win(limits=1))
0338         self.scoreTest(
0339             'c1c1c1c1 drdr wewewewe c3c3c3C3 s1S1S1s1 Ldrdrdr',
0340             Win(limits=1))
0341         self.scoreTest(
0342             'drdr c1c1c1c1 wewewewe c3c3c3C3 s1S1S1s1 Ldrdrdr',
0343             Win(limits=1))
0344         self.scoreTest(
0345             'c1c1c1c1 wewewewe c3c3c3C3 s1S1S1s1 drdr Ldrdrdr',
0346             Win(limits=1))
0347 
0348 
0349 class AllGreen(Base):
0350 
0351     """the green hand"""
0352 
0353     def testMe(self):
0354         self.scoreTest(
0355             'c1c1c1 c7c7c7 c2c3c4 c5c5 c6c6c6 Lc5c5c5', [Win(32, 3), Win(28)])
0356         self.scoreTest(
0357             'b2b2b2b2 RDgDgDg b6b6b6 b4b4b4 b8b8 Lb6b6b6b6',
0358             Win(limits=1))
0359         self.scoreTest(
0360             'b2b2b2b2 RDgDg b6b6b6 b4b4b4 b8b8 Lb6b6b6b6',
0361             [NoWin(14),
0362              NoWin(limits=0.4)])
0363         self.scoreTest(
0364             'b1b1b1b1 RDgDgDg b6b6b6 b4b4b4 b8b8 Lb6b6b6b6', [Win(48, 3), Win(48, 2)])
0365 
0366 
0367 class NineGates(Base):
0368 
0369     """the nine gates"""
0370         # DMJL allows 1..9 as last tile, BMJA allows only 2..8
0371 
0372     def testMe(self):
0373         self.scoreTest('RC1C1C1C2C3C4C5C6C7C8C9C9C9C5 LC5', Win(limits=1))
0374         self.scoreTest(
0375             'RC1C1C1C2C3C4C5C6C7C8C9C9C9C5 LC6',
0376             [Win(limits=1),
0377              Win(limits=1)])
0378         self.scoreTest(
0379             'RC1C1C1C2C3C4C5C6C7C8C9C9C9C9 LC9',
0380             [Win(limits=1),
0381              NoWin()])
0382         self.scoreTest(
0383             'RC1C1C1C2C3C4C5C6C7C8C9C9C9C9 LC9',
0384             [Win(limits=1),
0385              NoWin()])
0386         self.scoreTest('RC1C1C1C2C3C4C5C6C7C8C9C9C9C5 LC2', Win(limits=1))
0387         self.scoreTest(
0388             'RC1C1C1C2C3C4C5C6C7C8C9C9C9C9 LC1',
0389             [Win(limits=1),
0390              NoWin()])
0391         self.scoreTest('RC1C1C2C3C4C5C6C7C8C9C9C9C9 LC1', NoWin(8))
0392         self.scoreTest(
0393             'RC1C1C2C3C4C5C6C7C8C8C9C9C9 LC1',
0394             [NoWin(8),
0395              NoWin(limits=0.4)])
0396 
0397 
0398 class Manual(Base):
0399 
0400     """some manual rules for manual scoring"""
0401         # this should actually never happen but anyway we want to be sure that no rule
0402         # fires on this
0403 
0404     def testMe(self):
0405         self.scoreTest('', NoWin())
0406         self.scoreTest('', NoWin())
0407 
0408 
0409 class ThirteenOrphans(Base):
0410 
0411     """The 13 orphans"""
0412 
0413     def testMe(self):
0414         self.scoreTest('RC1C9B9B1S1S9WeDgWsWnWwDbDrS1 LDgDg', Win(limits=1))
0415         self.scoreTest('ww RC1C9B9B1S1S9WeDgWsWnDbDrS8 Lww', NoWin())
0416         self.scoreTest('ww RC1C9B9B1S1S9WeDgWsWnDbDrS9 Lww', Win(limits=1))
0417         self.scoreTest('RC1C9B9B1S1S9S9WeDgWsWnWwDbDr LDrDr', Win(limits=1))
0418         self.scoreTest('dr RC1C9B9B1S1S9S9WeDgWsWnWwDb Ldrdr', Win(limits=1))
0419         self.scoreTest(
0420             'RC1C9B9B1S1S9S9WeDgWnWwDbDr LDb',
0421             [NoWin(),
0422              NoWin(limits=0.4)])
0423         self.callingTest('Dg Dg Dr We Ws Ww Wn Wn RB1B9C1S1S9 LWe', '')
0424         self.callingTest('Db Dg Dr We Ws Ww Wn B7 RB1B9C1S1S9 LWe', '')
0425         self.callingTest('RDbDgDrWeWsWwWnWnB1B9C1S1S9 LWn', 'c9')
0426         self.callingTest('RDbDgDrWsWwWnWnB1B9C1S1S9C9 LDg', 'we')
0427 
0428 
0429 class SimpleNonWinningCases(Base):
0430 
0431     """normal hands"""
0432 
0433     def testMe(self):
0434         self.scoreTest('s2s2s2 s2s3s4 RB1B1B1B1 c9c9c9C9 Ls2s2s3s4', NoWin(26))
0435 
0436 
0437 class FourBlessingsOverTheDoor(Base):
0438 
0439     """lots of winds"""
0440 
0441     def testMe(self):
0442         self.scoreTest(
0443             'b1b1 wewewe wswsws WnWnWn wwwwwwww Lb1b1b1',
0444             Win(limits=1))
0445         self.scoreTest(
0446             'RDgDg wewewe wswsws WnWnWn wwwwwwww LDgDgDg',
0447             Win(limits=1))
0448         self.scoreTest(
0449             'wewewe wswsws WnWnWn wwwwwwww DrDr LDrDrDr',
0450             Win(limits=1))
0451         self.scoreTest(
0452             'wewewe wswsws WnWnWn wwwwwwww DrDr LDrDrDr',
0453             Win(limits=1))
0454         self.scoreTest(
0455             'wewewe wswsws WnWnWn wwwwwwww DrDr mz LDrDrDr',
0456             Win(limits=1))
0457         self.scoreTest(
0458             'wewewe wswsws RWnWnWnDr wwwwwwww', [NoWin(32, 4), NoWin(limits=0.4)])
0459 
0460 
0461 class AllHonors(Base):
0462 
0463     """only honors"""
0464 
0465     def testMe(self):
0466         self.scoreTest(
0467             'drdrdr wewe wswsws wnwnwn dbdbdb mz Ldrdrdrdr',
0468             Win(limits=1))
0469         self.scoreTest(
0470             'wewewe wswsws RWnWnWnB1 wwwwwwww LB1', [NoWin(32, 4), NoWin(limits=0.4)])
0471         # this one is limits=0.4 = 400 points, but points 512 are higher:
0472         self.scoreTest(
0473             'wewewe drdrdr RDrDrDrDb wwwwwwww LDb', [NoWin(32, 4), NoWin(32, 4)])
0474         self.scoreTest('wewe wswsws RWnWnWn wwwwwwww b1b1 Lwewewe', [
0475             NoWin(30, 2), NoWin(30, 1)], myWind=North)
0476 
0477 
0478 class BuriedTreasure(Base):
0479 
0480     """buried treasure, CC BMJA"""
0481 
0482     def testMe(self):
0483         self.scoreTest('RWeWeWeC3C3C3S3S3 c4c4c4C4 b8B8B8b8 me LWeWeWeWe',
0484                        [Win(limits=1), Win(58, 4)])
0485         self.scoreTest('RWeWeWeC3C3C3S3S3C4C4C4B8B8B8 me LWeWeWeWe',
0486                        [Win(limits=1), Win(42, 4)])
0487         self.scoreTest('RWeWeWeC3C3C3C5C5C4C4C4C8C8C8 me LWeWeWeWe',
0488                        [Win(limits=1), Win(limits=1)])
0489         self.scoreTest('RWeWeC3C3C3C5C5C4C4C4C8C8C8 me LWe',
0490                        [NoWin(16, 1), NoWin(limits=0.4)])
0491         self.scoreTest('RWeWeWeC3C3C3C5C5C4C4C4C7C8C9 me LWeWeWeWe',
0492                        [Win(38, 6), Win(38, 3)])
0493 
0494 
0495 class HiddenTreasure(Base):
0496 
0497     """hidden treasure, CC DMJL"""
0498 
0499     def testMe(self):
0500         self.scoreTest('RWeWeWeC3C3C3S3S3 c4c4c4C4 b8B8B8b8 me LWeWeWeWe',
0501                        [Win(limits=1), Win(58, 4)])
0502         self.scoreTest('RWeWeWeC3C3C3S3S3 c4c4c4C4 b8B8B8b8 LC3C3C3C3',
0503                        [Win(limits=1), Win(58, 4)])
0504         self.scoreTest('RWeWeWeC3C3C3 c4c4c4C4 b8B8B8b8 s3s3 Ls3s3s3',
0505                        [Win(60, 4), Win(56, 3)])
0506 
0507 
0508 class FourfoldPlenty(Base):
0509 
0510     """4 kongs"""
0511 
0512     def testMe(self):
0513         self.scoreTest('RB3B3B3C1C1C1 b1b1b1 s3s4s5 wewe LB3B3B3B3', Win(42))
0514         self.scoreTest(
0515             'b3B3B3b3 c1C1C1c1 b1b1b1b1 s3s3s3s3 wewe Lwewewe',
0516             Win(limits=1))
0517         self.scoreTest(
0518             'b3B3B3b3 c1C1C1c1 b1b1b1b1 s3s3s3s3 WeWe LWeWeWe',
0519             Win(limits=1))
0520         self.scoreTest(
0521             'b3b3 c1C1C1c1 b1b1b1b1 s3s3s3s3 wewewewe Lb3b3b3',
0522             Win(limits=1))
0523         self.scoreTest(
0524             'b3b3b3b3 c1c1 b1b1b1b1 s3s3s3s3 wewewewe Lc1c1c1',
0525             Win(limits=1))
0526         self.scoreTest(
0527             'b3b3b3b3 RC1 b1b1b1b1 s3s3s3s3 wewewewe', [NoWin(48, 2), NoWin(limits=0.4)])
0528 
0529 
0530 class PlumBlossom(Base):
0531 
0532     """Gathering the plum blossom from the roof"""
0533 
0534     def testMe(self):
0535         self.scoreTest(
0536             's2s2s2 RS5S5S5B1B1B1B2B2 c9C9C9c9 me LS5S5S5S5',
0537             Win(limits=1))
0538         self.scoreTest(
0539             's2s2s2 RS5S5S5B1B1B1B2B2 c9C9C9c9 me Ls2s2s2s2', [Win(66, 3), Win(66, 1)])
0540         self.scoreTest(
0541             's2s2s2 RS5S5S5B1B1B1B2B2 c9C9C9c9 LS5S5S5S5', [Win(68, 2), Win(68, 1)])
0542 
0543 
0544 class PluckingMoon(Base):
0545 
0546     """plucking the moon from the bottom of the sea"""
0547 
0548     def testMe(self):
0549         self.scoreTest(
0550             's2s2s2 RS1S1S1B1B1B1B2B2 c9C9C9c9 mz LS1S1S1S1',
0551             Win(limits=1))
0552         self.scoreTest(
0553             's2s2s2 RS1S1S1B1B1B1B2B2 c9C9C9c9 mz Ls2s2s2s2', [Win(70, 3), Win(70, 2)])
0554         self.scoreTest(
0555             's2s2s2 RS1S1S1B1B1B1B2B2 c9C9C9c9 LS1S1S1S1', [Win(72, 2), Win(72, 1)])
0556 
0557 
0558 class ScratchingPole(Base):
0559 
0560     """scratch a carrying pole"""
0561 
0562     def testMe(self):
0563         self.scoreTest(
0564             'b2b3b4 RS1S1S1B1B1B1B4B4 c9C9C9c9 mk Lb2b2b3b4',
0565             Win(limits=1))
0566         self.scoreTest(
0567             'b2b2b2 RS1S1S1B1B1B1B4B4 c9C9C9c9 LS1S1S1S1', [Win(72, 2), Win(72, 1)])
0568         self.scoreTest(
0569             'b2b2b2 RS1S1S1B1B1B1B4B4 c9C9C9c9 Lb2b2b2b2', [Win(70, 2), Win(70, 1)])
0570 
0571 
0572 class ThreeGreatScholars(Base):
0573 
0574     """three concealed pungs"""
0575 
0576     def testMe(self):
0577         self.scoreTest('dgdgdg RDrDrDrDbDbDb s4s4s4 c5c5', Win(limits=1))
0578         self.scoreTest(
0579             'dgdgdg RDrDrDrDbDb s4s4s4 c5c5', [NoWin(16, 3), NoWin(limits=0.4)])
0580         self.scoreTest(
0581             'dgdgdg RDrDrDrDbDbDb s4s5s6 c5c5', [Win(limits=1), Win(40, 3)])
0582         # calling for Three Great Scholars:
0583         self.scoreTest(
0584             's2s2 RDgDgDgDbDbDbDrDrDr b2b2b2b2 Ls2s2s2',
0585             Win(limits=1))
0586         self.scoreTest(
0587             'RDgDgDgDbDbDbDrDrDrS2 b2b2b2b2 LDbDbDbDb', [NoWin(32, 6), NoWin(limits=0.4)])
0588         # 40, 5 is more than 0.4 limits:
0589         self.scoreTest(
0590             'RS2DgDgDgDbDbDbDrDrDr wewewewe LDbDbDbDb', [NoWin(40, 8), NoWin(40, 5)])
0591 
0592 
0593 class ThreeConcealedPungs(Base):
0594 
0595     """three concealed pungs"""
0596 
0597     def testMe(self):
0598         self.scoreTest(
0599             'RB2B2B2S1S1S1B1B1B1B4B4 c9c9c9c9 LB2B2B2B2', [Win(58, 2), Win(58, 1)])
0600         self.scoreTest(
0601             'RB2B2B2S1S1S1B4B4 b1b1b1 c9c9c9c9 LB2B2B2B2', Win(54, 1))
0602         self.scoreTest(
0603             'RB2B2B2S1S1S1B4B4 b1b1b1 c9c9c9C9 LB2B2B2B2', [Win(54, 2), Win(54, 1)])
0604 
0605 
0606 class Rest(Base):
0607 
0608     """various tests"""
0609 
0610     def testMe(self):
0611         self.scoreTest('s1s1s1s1 s2s2s2 wewe RS3S3S3 s4s4s4 Ls2s2s2s2',
0612                        [Win(44, 2), Win(44, 1)], myWind=South, roundWind=West)
0613         self.scoreTest('s1s1s1s1 s2s2s2 RWeWeS3S3S3 s4s4s4 me LS3S3S3S3',
0614                        [Win(46, 3), Win(46, 1)], myWind=South, roundWind=West)
0615         self.scoreTest(
0616             'b3B3B3b3 RDbDbDbDrDrDr wewewewe s2s2 Ls2s2s2', [Win(72, 6), Win(68, 5)])
0617         self.scoreTest('s1s2s3 s1s2s3 b3b3b3 b4b4b4 RB5 fn yn LB5', [
0618             NoWin(12, 1), NoWin(12, 2)], myWind=North)
0619         self.scoreTest(
0620             'b3b3b3b3 RDbDbDb drdrdr weWeWewe s2s2 Ls2s2s2', [Win(76, 5), Win(72, 5)])
0621         self.scoreTest('s2s2s2 s2s3s4 RB1B1B1B1 c9C9C9c9 Ls2s2s3s4', NoWin(42))
0622         self.scoreTest(
0623             's2s2s2 RDgDgDbDbDbDrDrDr b2b2b2b2 Ls2s2s2s2', [Win(48, 4), Win(48, 3)])
0624         self.scoreTest(
0625             's1s1s1s1 s2s2s2 s3s3s3 s4s4s4 s5s5 mw Ls3s3s3s3', Win(42, 4))
0626         self.scoreTest(
0627             'RB2C1B2C1B2C1WeWeS4WeS4WeS6 LC1', [NoWin(20, 3), NoWin(20, 2)])
0628         self.scoreTest('b6b6b6 RB1B1B2B2B3B3B7S7C7B8 LB3', NoWin(2))
0629         self.scoreTest('RB1B1B1B1B2B3B4B5B6B7B8B9DrDr fe fs fn fw LDrDrDr',
0630                        [Win(54, 3), NoWin()])
0631         self.scoreTest('RB1B1B1B2B2B2B5B5B5B7B8B9DrDr fe fs fn fw LDrDrDr',
0632                        [Win(62, 4), Win(56, 4)])
0633         self.scoreTest('RB1B1B1B1B2B3B4B5B6B7B8B9DrDr fe fs fn fw me LDrDrDr',
0634                        [Win(54, 4), NoWin()])
0635         self.scoreTest('RB1B1B1B1B2B3B4B4B4B7B7B7DrDr fe fs fn fw mz LDrDrDr',
0636                        [Win(62, 5), Win(56, 5)])
0637         self.scoreTest('RB1B1B1B1B2B3B4B4B4B7B7B7DrDr fe fs fn fw mZ LDrDrDr',
0638                        [Win(62, 5), Win(56, 5)])
0639         self.scoreTest('drdr RB1B1B1B1B2B3B4B5B6B7B8B9 fe fs fn fw mZ Ldrdrdr',
0640                        [Win(52, 3), NoWin()])
0641         self.scoreTest(
0642             'RB1B1B1B1B2B3B4B5B6B7B8B8B2B2 fe fs fn fw LB4',
0643             NoWin())
0644         self.scoreTest('RB1B1B1B1B2B3B4B5B6B8B8B2B2 fe fs fn fw LB4',
0645                        [NoWin(28, 1), NoWin(28, 3)])
0646         self.scoreTest('wewe wswsws RWnWnWn wwwwwwww b1b1b1 mz Lb1b1b1b1',
0647                        [Win(54, 6), Win(54, 4)], myWind=North)
0648         self.scoreTest('wswsws RWeWeWnWnWnB1B1B1 wwwwwwww mz LB1B1B1B1',
0649                        [Win(60, 6), Win(60, 4)], myWind=North)
0650         self.scoreTest(
0651             'RB2B2 b4b4b4 b5b6b7 b7b8b9 c1c1c1 md Lb7b7b8b9', [Win(28), NoWin()])
0652         self.scoreTest(
0653             'RB8B8 s4s4s4 b1b2b3 b4b5b6 c1c1c1 md Lb3b1b2b3', [Win(28), NoWin()])
0654         self.scoreTest(
0655             'RB2B2 s4s4s4 b1b2b3 b4b5b6 c1c1c1 md Lb3b1b2b3', [Win(26), NoWin()])
0656         self.scoreTest(
0657             'RB2B2 s4s4s4 b1b2b3 b4b5b6 c1c1c1 md Lb3b1b2b3', [Win(26), NoWin()])
0658 
0659 
0660 class TwofoldFortune(Base):
0661 
0662     """Twofold fortune"""
0663 
0664     def testMe(self):
0665         self.scoreTest(
0666             'b1B1B1b1 RB2B3B4B5B6B7 b8b8b8b8 b5b5 fe fs fn fw m.t LB4',
0667             [Win(limits=1),
0668              NoWin()])
0669         self.scoreTest(
0670             'b1B1B1b1 RB2B3B4B6B6B6 b8b8b8b8 b5b5 fe fs fn fw m.t LB4',
0671             Win(limits=1))
0672 
0673 
0674 class OriginalCall(Base):
0675 
0676     """original call"""
0677 
0678     def testMe(self):
0679         # in DMJL, b4 would also win:
0680         self.scoreTest('s1s1s1 s1s2s3 RB6B6B6B8B8B8B5B5 fn yn m.a LB5',
0681                        [Win(44, 2), Win(42, 3)], myWind=North)
0682         self.scoreTest('s1s1s1 s1s2s3 RB6B6B6B8B8B8B5 fn yn m.a LB5',
0683                        [NoWin(20, 1), NoWin(20, 2)], myWind=North)
0684 
0685 
0686 class RobbingKong(Base):
0687 
0688     """robbing the kong"""
0689 
0690     def testMe(self):
0691         # this hand is only possible if the player declared a hidden chow.
0692         # is that legal?
0693         self.scoreTest('s1s2s3 s1s2s3 RB6B6B7B7B8B8B5 fn yn m.a LB5',
0694                        [NoWin(8, 1), NoWin(8, 2)], myWind=North)
0695         self.scoreTest('s1s2s3 s2s3s4 RB6B6B7B7B8B8B5B5 fn yn mka Ls1s1s2s3',
0696                        [Win(28, 4), NoWin()], myWind=North)
0697         self.scoreTest('s4s5s6 RS1S2S3B6B6B7B7B8B8B5B5 fn yn m.a LS1S1S2S3',
0698                        [Win(30, 3), NoWin()], myWind=North)
0699         self.scoreTest('s4s5s6 RS1S2S3B6B6B7B7B8B8 fn yn m.a Ls4s4s5s6',
0700                        [NoWin(8, 1), NoWin(8, 2)], myWind=North)
0701 
0702 
0703 class Blessing(Base):
0704 
0705     """blessing of heaven or earth"""
0706 
0707     def testMe(self):
0708         self.scoreTest('s4s5s6 RS1S2S3B6B6B7B7B8B8 b5b5 fn yn m1',
0709                        [Win(limits=1), NoWin()])
0710         self.scoreTest('s4s5s6 RS1S2S3B6B6B7B7B8B8 b5b5 fn yn m1',
0711                        [Win(limits=1), NoWin()])
0712         self.scoreTest('s4s5s6 RS1S1S1B6B6B6B8B8B8 b5b5 fn yn m1',
0713                        Win(limits=1))
0714         self.scoreTest('s4s5s6 RS1S2S3B6B6B7B7B8B8 b5b5 fn yn m1 Ls4s4s5s6',
0715                        [Win(limits=1), NoWin()], myWind=West)
0716 
0717 
0718 class Terminals(Base):
0719 
0720     """only terminals"""
0721 
0722     def testMe(self):
0723         # must disallow chows:
0724         self.scoreTest(
0725             'b1b1 c1c2c3 c1c2c3 c1c2c3 c1c2c3 Lb1b1b1', [Win(26, 1), NoWin()])
0726         self.scoreTest(
0727             'b1b1 c1c2c3 c9c9c9 s1s1s1 s9s9s9 Lb1b1b1', [Win(38), Win(32)])
0728         self.scoreTest(
0729             'b1b1 c1c1c1 c9c9c9 s1s1s1 s9s9s9 Lb1b1b1',
0730             Win(limits=1))
0731         self.scoreTest(
0732             'RB1 c1c1c1 c9c9c9 s1s1s1 s9s9s9',
0733             [NoWin(16),
0734              NoWin(limits=0.4)])
0735 
0736 
0737 class LongHand(Base):
0738 
0739     """long hand"""
0740 
0741     def testMe(self):
0742         self.scoreTest('s1s2s3 s1s2s3 b3b3b3 b4b4b4 RB5B6 fn yn LB5', NoWin())
0743         self.scoreTest('RB2C1B2C1B2C1WeWeS4WeS4WeS6S5 LS5', NoWin())
0744         self.scoreTest('RB2C1B2C1B2C1WeWeS4WeS4WeS6S5S5 LS5', NoWin())
0745         self.scoreTest('RB2C1B2C1B2C1WeWeS4WeS4WeS6S5S5 LS5', NoWin())
0746         self.scoreTest('RWsWsWsWsWnS6C1C1WeWeWeS4S4S5S5 LS5', NoWin())
0747 
0748 
0749 class MJ(Base):
0750 
0751     """test winner hands.
0752         Are the hidden melds grouped correctly?"""
0753 
0754     def testMe(self):
0755         self.scoreTest(
0756             'RB1B1B1B2B2B2B3B4 wnwnwn wewewe Lwnwnwnwn', [Win(36, 3), Win(36, 2)])
0757         self.scoreTest(
0758             'RB1B1B1B2B2B2B3B3B3S1S1 c3c4c5 Lc3c3c4c5', [Win(36, 1), Win(36)])
0759         self.scoreTest(
0760             'RB1B1B1B2B2B2B3B3S1S2S3 c3c4c5 Lc3c3c4c5', [Win(32), NoWin()])
0761         self.scoreTest(
0762             'c1C1C1c1 b5B5B5b5 c2C2C2c2 c3C3C3c3 RC4B6 fs fw fn fe LC4',
0763             NoWin())
0764         self.scoreTest(
0765             'wewewe wswsws wnwnwnWn RWwWwWwC6 LC6', [NoWin(32, 4), NoWin(limits=0.4)])
0766         self.scoreTest('wewewe wswsws wnwnwnWn RWwWwWwC3B6 LC3', NoWin())
0767         self.scoreTest('wewewe wswsws wnwnwnWn RWwWwWwC3C3 LC3', Win(limits=1))
0768 
0769 
0770 class LastIsOnlyPossible(Base):
0771 
0772     """tests for determining if this was the only possible last tile"""
0773 
0774     def testMe(self):
0775         self.scoreTest(
0776             's2s3s4 DrDr S1S2S3 S6S7S8 B5B6B7 fw yw fs md Ls2s2s3s4', [Win(34), NoWin(0)])
0777         self.scoreTest('b3B3B3b3 wewewewe s2s2 RDbDbDbDrDrDr Ls2s2s2',
0778                        [Win(72, 6), Win(68, 5)], totals=(500, 1000, 4608, 2176))
0779         self.scoreTest(
0780             'b3B3B3b3 wewewe RDbDbDbS1S1S1S2S2 LS2S2S2', [Win(60, 5), Win(58, 4)])
0781         self.scoreTest(
0782             'b3B3B3b3 wewewe RDbDbDbS1S1S1S3S3 LS3S3S3', [Win(60, 5), Win(58, 4)])
0783         self.scoreTest(
0784             'b3B3B3b3 wewewe RDbDbDbS1S1S1S4S4 LS4S4S4', [Win(62, 5), Win(58, 4)])
0785         self.scoreTest(
0786             'b3B3B3b3 wewewe RDbDbDbS1S1S1S3S3 LS1S1S1S1', [Win(58, 5), Win(58, 4)])
0787         self.scoreTest(
0788             's9s9s9 s8s8s8 RDgDgS1S2S3S3S4S5 LS3 fe', [Win(34, 1), NoWin()])
0789         self.scoreTest(
0790             's9s9s9 s8s8s8 RDgDgS1S2S3S4S4S4 LS3 fe', [Win(40, 1), Win(38, 1)])
0791         self.scoreTest(
0792             's9s9s9 s8s8s8 RDgDgS1S2S3S4S5S6 LS6 fe', [Win(34, 1), NoWin()])
0793         self.scoreTest(
0794             's9s9s9 s8s8s8 RDgDgS1S2S3S7S8S9 LS7 fe', [Win(36, 1), NoWin()])
0795 
0796 
0797 class TripleKnitting(Base):
0798 
0799     """triple knitting BMJA"""
0800 
0801     def testMe(self):
0802         self.scoreTest(
0803             'RS2B2C2S4B4C4S6B6C6S7B7C7 s8b8 Ls8s8b8',
0804             [None,
0805              Win(limits=0.5)])
0806         self.scoreTest(
0807             'RB2C2S4B4C4S6B6C6S7B7C7S8B8 LS8',
0808             [NoWin(),
0809              NoWin(limits=0.2)])
0810         self.scoreTest(
0811             'RS2B2C2S4B4C4S6B6C6S7B7C7S8 LS8',
0812             [NoWin(),
0813              NoWin(limits=0.2)])
0814         self.scoreTest(
0815             'RS2B2C2S7B7C7S4B4C4S6B6C6 s8b8 Ls8s8b8',
0816             [NoWin(),
0817              Win(limits=0.5)])
0818         self.scoreTest('RS2B2C2S6B6C6S7B7C7 s4b4c4 s8b8 Ls8s8b8', NoWin())
0819         self.scoreTest(
0820             'RS2B2C2S4B4C4S6B6C6S4B4C4 s8c8 Ls8s8c8',
0821             [NoWin(),
0822              Win(limits=0.5)])
0823         self.scoreTest(
0824             'RS2B2C2S3B3C3S4B4C4S4B4C4 s8c8 Ls8s8c8',
0825             [NoWin(),
0826              Win(limits=0.5)])
0827         self.scoreTest('RB2C2B3C3B4C4S4B4C4 s2s3s4 s8c8 Ls8s8c8', NoWin())
0828         self.scoreTest('RB5S7B7B3B9S7S6C9C3B7S3 c5c6c7 mw LS3', NoWin())
0829         self.scoreTest('RC1C2C5C8C9S2S4S9B4B5B6B6B8S1', NoWin())
0830         self.scoreTest('RC1C2C5C8C9S2S4S9B4B5B6B6B8', NoWin())
0831         self.scoreTest(
0832             'RS2B2C2S4B4C4S6B6C6S4B4C4S8',
0833             [NoWin(),
0834              NoWin(limits=0.2)])
0835         self.scoreTest('RS1B1C1S2B2C2S5B5S8B8C8B9C7C9 LB2', NoWin())
0836         self.callingTest('RS2B2C2S4B4C4S6B6C6S7B7C7S8 LS8', ['', 'b8c8'])
0837         self.callingTest('RS2B2C2S4B4C4S6B6C6B7C7S8C8 LC7', ['', 's7b8'])
0838 
0839 
0840 class Knitting(Base):
0841 
0842     """knitting BMJA"""
0843 
0844     def testMe(self):
0845         self.scoreTest(
0846             'RS2B2S3B3S4B4S5B5S6B6S7B7 s9b9 Ls9s9b9',
0847             [NoWin(),
0848              Win(limits=0.5)])
0849         self.scoreTest(
0850             'RS2B2S3B3B4S5B5S6B6S7B7 S9B9 LS9',
0851             [NoWin(),
0852              NoWin(limits=0.2)])
0853         self.scoreTest('RS2B2S3S5B5B3S4B4S6B6S7B7 s9c9 Ls9s9c9', NoWin())
0854         self.scoreTest('RS2B3S3B3S4B4S5B5S6B6S7B7 s9b9 Ls9s9b9', NoWin())
0855         self.scoreTest(
0856             'RS2B2S2B2S4B4S5B5S6B6S7B7 s9b9 Ls9s9b9',
0857             [NoWin(),
0858              Win(limits=0.5)])
0859         self.scoreTest(
0860             'RS2S3S4S5S6S7S9B2B3B4B5B6B7B9 LB9',
0861             [NoWin(),
0862              Win(limits=0.5)])
0863         self.scoreTest(
0864             'RS3S4S5S6S7S9B2B3B4B5B6B7B9 LB9',
0865             [NoWin(),
0866              NoWin(limits=0.2)])
0867         self.scoreTest('RS3S4S9S9B1B1B2B3B4B8B8 s2s2s2 fs mw LS4', NoWin())
0868         self.scoreTest('RS3S9S9B1B1B2B3B8B8S2S2S2 s4b4 fs mw Ls4', NoWin())
0869         self.callingTest('RS2B2S3B3S4B4S5B5S6B6S7B7S9 LB7', ['s9', 'b9'])
0870 
0871 
0872 class AllPairHonors(Base):
0873 
0874     """all pairs honors BMJA"""
0875 
0876     def testMe(self):
0877         self.scoreTest(
0878             'RWeWeS1S1B9B9DgDgDrDrWsWsWwWw LS1S1S1',
0879             [NoWin(),
0880              Win(limits=0.5)])
0881         self.scoreTest(
0882             'RWeWeS1S1B9B9DgDgDrDrWsWs wwww Lww',
0883             [NoWin(),
0884              Win(limits=0.5)])
0885         self.scoreTest(
0886             'RWeWeS1S1B9B9DgDgDrDrWsWwWw LS1',
0887             [NoWin(6),
0888              NoWin(limits=0.2)],
0889             myWind=West, roundWind=North)
0890         self.scoreTest('RWeWeS2S2B9B9DgDgDrDrWsWsWwWw LS2S2S2', NoWin())
0891         self.scoreTest('RDbDbDgDgDrDrWsWsWnWnS9C1C1C9 LDrDrDr', NoWin())
0892 
0893 
0894 class BMJA(Base):
0895 
0896     """specials for chinese classical BMJA"""
0897 
0898     def testMe(self):
0899         self.scoreTest(
0900             'RS1S1S5S6S7S8S9WeWsWwWnS2S3S4 LS3S3',
0901             [NoWin(),
0902              Win(limits=1)])
0903 
0904 
0905 class LastTile(Base):
0906 
0907     """will the best last meld be chosen?"""
0908 
0909     def testMe(self):
0910         self.scoreTest(
0911             'wewewe s1s1s1 b9b9b9 RC1C1C1C2C3 LC1', [Win(38, 2), Win(34, 2)])
0912         self.scoreTest(
0913             'wewewe s1s1s1 b9b9b9 RC1C2C3C3C3 LC3', [Win(38, 2), Win(34, 2)])
0914         self.scoreTest(
0915             'b5b6b7 s1s1s1 RB8C6C7C5B8B8C7C7 mw LC7', [Win(32, 0), NoWin()])
0916 
0917 
0918 class CallingHands(Base):
0919 
0920     """diverse calling hands"""
0921 
0922     def testMe(self):
0923         self.callingTest('s1s1s1s1 b5b6b7 RB1B8C2C2C6C7C8 Lb5', '')
0924         self.callingTest('WnWn B1 B2 c4c5c6 b6b6b6 b8b8b8 ye yw', ['b3', ''])
0925         self.callingTest('WnWn B1 B2 dgdgdg b6b6b6 b8b8b8 ye yw', 'b3')
0926         self.callingTest('s1s1s1s1 b5b6b7 RB8B8C2C2C6C7C8 Lb5', ['b8c2', ''])
0927         self.callingTest('s1s1s1s1 b5b6b7 RB7B8C2C2C6C7C8 Lb5', ['b6b9', ''])
0928         self.callingTest('c3c3c3 RDbDbDbS5S6S7S7S8B2B2 LS8', ['s6s9', ''])
0929         self.callingTest('RC4C4C5C6C5C7C8 dgdgdg s6s6s6', 'c4c5')
0930         self.callingTest('RS1S4C5C6C5C7C8 dgdgdg s6s6s6', '')
0931         self.callingTest(
0932             'RDbDgDrWsWwWeWnB1B9C1S1S9C9 LWe',
0933             'dbdgdrwewswwwns1s9b1b9c1c9')
0934 
0935 
0936 class Recursion(Base):
0937 
0938     """recursion in Hand computing should never happen"""
0939 
0940     def testMe(self):
0941         self.scoreTest(
0942             'c6c6c6C6 fe fs RS8S8C1C2C3C4C5C7C8C9 LC6', [NoWin(16), NoWin(16, 1)])
0943         self.scoreTest(
0944             'c6c6c6C6 fe fs RS8S8C1C2C3C4C5C7C8C9 LC7', [NoWin(16), NoWin(16, 1)])
0945 
0946 
0947 class TstProgram(unittest.TestProgram):
0948 
0949     """we want global access to this program so we can check for verbosity in our tests"""
0950 
0951     def __init__(self, *args, **kwargs):
0952         global PROGRAM  # pylint: disable=global-statement
0953         PROGRAM = self
0954 #        unittest.TestProgram.__init__(self, exit=False, *args, **kwargs)
0955         unittest.TestProgram.__init__(self, *args, **kwargs)
0956 
0957 if __name__ == '__main__':
0958     # Debug.hand = True
0959     TstProgram()