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()