def _clientDiscarded4(self, dummyResults, msg, dangerousText, mustPlayDangerous): """client told us he discarded a tile. Continue, check for dangerous game""" block = DeferredBlock(self) player = msg.player if dangerousText: if mustPlayDangerous and not player.lastSource.isDiscarded: if Debug.dangerousGame: tile = Tile(msg.args[0]) logDebug( '%s claims no choice. Discarded %s, keeping %s. %s' % (player, tile, ''.join( player.concealedTiles), ' / '.join(dangerousText))) player.claimedNoChoice = True block.tellAll(player, Message.NoChoice, tiles=TileList(player.concealedTiles)) else: player.playedDangerous = True if Debug.dangerousGame: tile = Tile(msg.args[0]) logDebug( '%s played dangerous. Discarded %s, keeping %s. %s' % (player, tile, ''.join( player.concealedTiles), ' / '.join(dangerousText))) block.tellAll(player, Message.DangerousGame, tiles=TileList(player.concealedTiles)) if msg.answer == Message.OriginalCall: player.isCalling = True block.callback(self.clientMadeOriginalCall, msg) else: block.callback(self._askForClaims, msg)
def __parseString(self, inString): """parse the string passed to Hand()""" # pylint: disable=too-many-branches tileStrings = [] for part in inString.split(): partId = part[0] if partId == 'm': if len(part) > 1: try: self.__lastSource = TileSource.byChar[part[1]] except KeyError: raise Exception('{} has unknown lastTile {}'.format(inString, part[1])) if len(part) > 2: self.__announcements = set(part[2]) elif partId == 'L': if len(part[1:]) > 8: raise Exception( 'last tile cannot complete a kang:' + inString) if len(part) > 3: self.__lastMeld = Meld(part[3:]) self.__lastTile = Tile(part[1:3]) else: if part != 'R': tileStrings.append(part) self.bonusMelds, tileStrings = self.__separateBonusMelds(tileStrings) tileString = ' '.join(tileStrings) self.tiles = TileList(tileString.replace(' ', '').replace('R', '')) self.tiles.sort() for part in tileStrings[:]: if part[:1] != 'R': self.melds.append(Meld(part)) tileStrings.remove(part) self.values = tuple(x.value for x in self.tiles) self.suits = set(x.lowerGroup for x in self.tiles) self.declaredMelds = MeldList(x for x in self.melds if x.isDeclared) declaredTiles = list(sum((x for x in self.declaredMelds), [])) self.tilesInHand = TileList(x for x in self.tiles if x not in declaredTiles) self.lenOffset = (len(self.tiles) - 13 - sum(x.isKong for x in self.melds)) assert len(tileStrings) < 2, tileStrings self.__rest = TileList() if len(tileStrings): self.__rest.extend(TileList(tileStrings[0][1:])) last = self.__lastTile if last and not last.isBonus: assert last in self.tiles, \ 'lastTile %s is not in hand %s' % (last, str(self)) if self.__lastSource is TileSource.RobbedKong: assert self.tiles.count(last.exposed) + \ self.tiles.count(last.concealed) == 1, ( 'Robbing kong: I cannot have ' 'lastTile %s more than once in %s' % ( last, ' '.join(self.tiles)))
def divided(self, dummyResults=None): """the wall is now divided for all clients""" if not self.running: return block = DeferredBlock(self) for clientPlayer in self.game.players: for player in self.game.players: if player == clientPlayer or self.game.playOpen: tiles = player.concealedTiles else: tiles = TileList(Tile.unknown * 13) block.tell(player, clientPlayer, Message.SetConcealedTiles, tiles=TileList(chain(tiles, player.bonusTiles))) block.callback(self.dealt)
def __init__(self, player, command, kwargs): if isinstance(command, Message): self.message = command else: self.message = Message.defined[command] self.table = None self.notifying = False self._player = weakref.ref(player) if player else None self.token = kwargs['token'] self.kwargs = kwargs.copy() del self.kwargs['token'] self.score = None self.lastMeld = None for key, value in kwargs.items(): assert not isinstance(value, bytes), 'value is bytes:{}'.format( repr(value)) if value is None: self.__setattr__(key, None) else: if key.lower().endswith('tile'): self.__setattr__(key, Tile(value)) elif key.lower().endswith('tiles'): self.__setattr__(key, TileList(value)) elif key.lower().endswith('meld'): self.__setattr__(key, Meld(value)) elif key.lower().endswith('melds'): self.__setattr__(key, MeldList(value)) elif key == 'playerNames': if Internal.isServer: self.__setattr__(key, value) else: self.__setattr__(key, self.__convertWinds(value)) else: self.__setattr__(key, value)
def without(self, remove): """self without tile. The rest will be uppercased.""" tiles = TileList() for tile in self: if tile is remove: remove = None else: tiles.append(tile.concealed) return tiles
def __possibleChows(self): """returns a unique list of lists with possible claimable chow combinations""" if self.game.lastDiscard is None: return [] exposedChows = [x for x in self._exposedMelds if x.isChow] if len(exposedChows) >= self.game.ruleset.maxChows: return [] tile = self.game.lastDiscard within = TileList(self.concealedTiles[:]) within.append(tile) return within.hasChows(tile)
def __new__(cls, newContent=None): """try to use cache""" if isinstance(newContent, str) and newContent in cls.cache: return cls.cache[newContent] if isinstance(newContent, Meld): return newContent tiles = TileList(newContent) cacheKey = tiles.key() if cacheKey in cls.cache: return cls.cache[cacheKey] return TileList.__new__(cls, tiles)
def __init__(self, player, string, prevHand=None): """evaluate string for player. rules are to be applied in any case""" if hasattr(self, 'string'): # I am from cache return # shortcuts for speed: self._player = weakref.ref(player) self.ruleset = player.game.ruleset self.intelligence = player.intelligence if player else AIDefault() self.string = string self.__robbedTile = Tile.unknown self.prevHand = prevHand self.__won = None self.__score = None self.__callingHands = None self.__mjRule = None self.ruleCache = {} self.__lastTile = None self.__lastSource = TileSource.Unknown self.__announcements = set() self.__lastMeld = 0 self.__lastMelds = MeldList() self.tiles = None self.melds = MeldList() self.bonusMelds = MeldList() self.usedRules = [] self.__rest = TileList() self.__arranged = None self.__parseString(string) self.__won = self.lenOffset == 1 and player.mayWin if Debug.hand or (Debug.mahJongg and self.lenOffset == 1): self.debug(fmt('{callers}', callers=callers(exclude=['__init__']))) Hand.indent += 1 self.debug('New Hand {} {}'.format(string, self.lenOffset)) try: self.__arrange() self.__calculate() self.__arranged = True except Hand.__NotWon as notwon: if Debug.mahJongg: self.debug(fmt(str(notwon))) self.__won = False self.__score = Score() finally: self._fixed = True if Debug.hand or (Debug.mahJongg and self.lenOffset == 1): self.debug('Fixing {} {} {}'.format(self, self.won, self.score)) Hand.indent -= 1
def callingTest(self, string, expected): """test a calling hand""" for idx, ruleset in enumerate(RULESETS): game = GAMES[idx] game.players[0].clearCache() hand = Hand(game.players[0], string) testSays = TileList( set(x.lastTile.exposed for x in hand.callingHands)).sorted() if isinstance(expected, list): if idx >= len(expected): idx %= len(RULESETS) // 2 expIdx = idx if expIdx >= len(expected): expIdx %= len(RULESETS) // 2 exp = expected[expIdx] else: exp = expected completingTiles = TileList(exp) self.assertTrue(testSays == completingTiles, '%s: %s may be completed by %s but testresult is %s' % ( ruleset.name, string, completingTiles or 'None', testSays or 'None'))
def endHand(self, dummyResults=None): """hand is over, show all concealed tiles to all players""" if not self.running: return if self.game.playOpen: self.saveHand() else: block = DeferredBlock(self) for player in self.game.players: # there might be no winner, winner.others() would be wrong if player != self.game.winner: # the winner tiles are already shown in claimMahJongg block.tellOthers(player, Message.ShowConcealedTiles, show=True, tiles=TileList(player.concealedTiles)) block.callback(self.saveHand)
def __sub__(self, subtractTile): """returns a copy of self minus subtractTiles. Case of subtractTile (hidden or exposed) is ignored. subtractTile must either be undeclared or part of lastMeld. Exposed melds of length<3 will be hidden.""" # pylint: disable=too-many-branches # If lastMeld is given, it must be first in the list. # Next try undeclared melds, then declared melds assert self.lenOffset == 1 if self.lastTile: if self.lastTile is subtractTile and self.prevHand: return self.prevHand declaredMelds = self.declaredMelds tilesInHand = TileList(self.tilesInHand) boni = MeldList(self.bonusMelds) lastMeld = self.lastMeld if subtractTile.isBonus: for idx, meld in enumerate(boni): if subtractTile is meld[0]: del boni[idx] break else: if lastMeld and lastMeld.isDeclared and ( subtractTile.exposed in lastMeld.exposed): declaredMelds.remove(lastMeld) tilesInHand.extend(lastMeld.concealed) tilesInHand.remove(subtractTile.concealed) for meld in declaredMelds[:]: if len(meld) < 3: declaredMelds.remove(meld) tilesInHand.extend(meld.concealed) # if we robbed a kong, remove that announcement mjPart = '' announcements = self.announcements - set('k') if announcements: mjPart = 'm.' + ''.join(announcements) rest = 'R' + str(tilesInHand) newString = ' '.join(str(x) for x in ( declaredMelds, rest, boni, mjPart)) return Hand(self.player, newString, prevHand=self)
def __init__(self, newContent=None): """init the meld: content can be either - a single string with 2 chars for every tile - a list containing such strings - another meld. Its tiles are not passed. - a list of Tile objects""" if not hasattr(self, '_fixed'): # already defined if I am from cache TileList.__init__(self, newContent) self.case = ''.join('a' if x.islower() else 'A' for x in self) self.key = TileList.key(self) if self.key not in self.cache: self.cache[self.key] = self self.cache[str(self)] = self self.isExposed = self.__isExposed() self.isConcealed = not self.isExposed self.isSingle = self.isPair = self.isChow = self.isPung = False self.isKong = self.isClaimedKong = self.isKnitted = False self.isDragonMeld = len(self) and self[0].isDragon self.isWindMeld = len(self) and self[0].isWind self.isHonorMeld = self.isDragonMeld or self.isWindMeld self.isBonus = len(self) == 1 and self[0].isBonus self.isKnown = len(self) and self[0].isKnown self.__setMeldType() self.isPungKong = self.isPung or self.isKong self.isDeclared = self.isExposed or self.isKong groups = set(x.group.lower() for x in self) if len(groups) == 1: self.group = self[0].group self.lowerGroup = self.group.lower() else: self.group = 'X' self.lowerGroup = 'x' self.isRest = False self.__staticRules = {} # ruleset is key self.__dynamicRules = {} # ruleset is key self.__staticDoublingRules = {} # ruleset is key self.__dynamicDoublingRules = {} # ruleset is key self.__hasRules = None # unknown yet self.__hasDoublingRules = None # unknown yet self.concealed = self.exposed = self.declared = self.exposedClaimed = None # to satisfy pylint self._fixed = True if len(self) < 4: TileList.__setattr__(self, 'concealed', Meld(TileList(x.concealed for x in self))) TileList.__setattr__(self, 'declared', self.concealed) TileList.__setattr__(self, 'exposed', Meld(TileList(x.exposed for x in self))) TileList.__setattr__(self, 'exposedClaimed', self.exposed) else: TileList.__setattr__(self, 'concealed', Meld(TileList(x.concealed for x in self))) TileList.__setattr__( self, 'declared', Meld( TileList([ self[0].exposed, self[1].concealed, self[2].concealed, self[3].exposed ]))) TileList.__setattr__(self, 'exposed', Meld(TileList(x.exposed for x in self))) TileList.__setattr__( self, 'exposedClaimed', Meld( TileList([ self[0].exposed, self[1].exposed, self[2].exposed, self[3].concealed ])))
def tiles(self): """flat view of all tiles in all melds""" return TileList(sum(self, []))
def setUp(self): lv = LoadValues("test.txt", groups=True) self.tl = TileList() for tile in lv.raw_values: T = Tile(tile) self.tl.add_tile(T)