Beispiel #1
0
 def addUser(self, user):
     """add user to this table"""
     if user.name in list(x.name for x in self.users):
         raise srvError(pb.Error, m18nE('You already joined this table'))
     if len(self.users) == self.maxSeats():
         raise srvError(pb.Error, m18nE('All seats are already taken'))
     self.users.append(user)
     self.sendChatMessage(ChatMessage(self.tableid, user.name,
         m18nE('takes a seat'), isStatusMessage=True))
Beispiel #2
0
 def __failed(self, result, request):
     """a user did not or not correctly answer"""
     if request in self.requests:
         self.removeRequest(request)
     if result.type in [pb.PBConnectionLost]:
         msg = m18nE('The game server lost connection to player %1')
         self.table.abort(msg, request.user.name)
     else:
         msg = m18nE('Error for player %1: %2\n%3')
         try:
             traceBack = result.getTraceback()
         except BaseException:
             # may happen with twisted 12.3.0
             traceBack = 'twisted cannot give us a traceback'
         self.table.abort(msg, request.user.name, result.getErrorMessage(), traceBack)
Beispiel #3
0
 def requestAvatarId(self, cred): # pylint: disable=R0201
     """get user id from database"""
     args = cred.username.split(SERVERMARK)
     if len(args) > 1:
         if args[0] == 'adduser':
             cred.username = args[1]
             password = args[2]
             with Transaction():
                 query = Query('insert into player(name,password) values(?,?)',
                     list([cred.username.decode('utf-8'), password.decode('utf-8')]))
                 if not query.success:
                     if query.msg.startswith('ERROR: constraint failed') \
                     or 'not unique' in query.msg:
                         template = m18nE('User %1 already exists')
                         logInfo(m18n(template, cred.username))
                         query.msg = srvMessage(template, cred.username)
                     else:
                         logInfo(query.msg)
                     return fail(credError.UnauthorizedLogin(query.msg))
         elif args[1] == 'deluser':
             pass
     query = Query('select id, password from player where name=?',
         list([cred.username.decode('utf-8')]))
     if not len(query.records):
         template = 'Wrong username: %1'
         logInfo(m18n(template, cred.username))
         return fail(credError.UnauthorizedLogin(srvMessage(template, cred.username)))
     userid, password = query.records[0]
     # checkPassword uses md5 which cannot handle unicode strings (python 2.7)
     defer1 = maybeDeferred(cred.checkPassword, password.encode('utf-8'))
     defer1.addCallback(DBPasswordChecker._checkedPassword, userid)
     return defer1
Beispiel #4
0
 def __failed(self, result, request):
     """a user did not or not correctly answer"""
     if request in self.requests:
         self.removeRequest(request)
     if result.type in [pb.PBConnectionLost]:
         msg = m18nE('The game server lost connection to player %1')
         self.table.abort(msg, request.user.name)
     else:
         msg = m18nE('Error for player %1: %2\n%3')
         try:
             traceBack = result.getTraceback()
         except BaseException:
             # may happen with twisted 12.3.0
             traceBack = 'twisted cannot give us a traceback'
         self.table.abort(msg, request.user.name, result.getErrorMessage(),
                          traceBack)
Beispiel #5
0
 def logout(self, user):
     """remove user from all tables"""
     if user in self.users and user.mind:
         self.callRemote(user,'serverDisconnects')
         user.mind = None
         for block in DeferredBlock.blocks:
             for request in block.requests:
                 if request.player.remote == user:
                     block.removeRequest(request)
         if user in self.users: # avoid recursion : a disconnect error calls logout
             for table in self.tables.values():
                 if user in table.users:
                     if table.game:
                         self.closeTable(table, 'abort', m18nE('Player %1 has logged out'), user.name)
                     else:
                         self.leaveTable(user, table.tableid)
             self.users.remove(user)
     if InternalParameters.socket and not InternalParameters.continueServer \
         and not self.users and reactor.running:
         # do not stop right now, the client might reconnect right away
         # this happens if the wanted human player name did not yet exist
         # in the data base - in that case login fails. Next the client
         # might tell us to add that user to the data base. So let's wait
         # to see for 5 seconds if he does
         reactor.callLater(5, self.stopNowAfterLastDisconnect)
Beispiel #6
0
 def showConcealedMelds(self, concealedMelds, ignoreDiscard=None):
     """the server tells how the winner shows and melds his
     concealed tiles. In case of error, return message and arguments"""
     for part in concealedMelds.split():
         meld = Meld(part)
         for pair in meld.pairs:
             if pair == ignoreDiscard:
                 ignoreDiscard = None
             else:
                 if not pair in self.__concealedTileNames:
                     msg = m18nE('%1 claiming MahJongg: She does not really have tile %2')
                     return msg, self.name, pair
                 self.__concealedTileNames.remove(pair)
         self.addMeld(meld)
     if self.__concealedTileNames:
         msg = m18nE('%1 claiming MahJongg: She did not pass all concealed tiles to the server')
         return msg, self.name
     self.__hand = None
Beispiel #7
0
 def delUser(self, user):
     """remove user from this table"""
     if user in self.users:
         self.game = None
         self.users.remove(user)
         self.sendChatMessage(ChatMessage(self.tableid, user.name,
             m18nE('leaves the table'), isStatusMessage=True))
         if user is self.owner:
             # silently pass ownership
             if self.users:
                 self.owner = self.users[0]
Beispiel #8
0
 def claimTile(self, player, claim, meldTiles, nextMessage):
     """a player claims a tile for pung, kong or chow.
     meldTiles contains the claimed tile, concealed"""
     if not self.game:
         return
     lastDiscard = self.game.lastDiscard
     claimedTile = lastDiscard.element
     hasTiles = meldTiles[:]
     discardingPlayer = self.game.activePlayer
     hasTiles.remove(claimedTile)
     meld = Meld(meldTiles)
     if len(meldTiles) != 4 and meld.meldType not in [PAIR, PUNG, KONG, CHOW]:
         msg = m18nE('%1 wrongly said %2 for meld %3') + 'x:' + str(meld.meldType) + meld.joined
         self.abort(msg, player.name, claim.name, str(meld))
         return
     if not player.hasConcealedTiles(hasTiles):
         msg = m18nE('%1 wrongly said %2: claims to have concealed tiles %3 but only has %4')
         self.abort(msg, player.name, claim.name, ' '.join(hasTiles), ''.join(player.concealedTileNames))
         return
     # update our internal state before we listen to the clients again
     self.game.discardedTiles[claimedTile.lower()] -= 1
     self.game.activePlayer = player
     if claimedTile:
         player.lastTile = claimedTile.lower()
         player.lastSource = 'd'
     player.exposeMeld(hasTiles, claimedTile)
     self.game.lastDiscard = None
     block = DeferredBlock(self)
     if (nextMessage != Message.Kong
             and self.game.dangerousFor(discardingPlayer, lastDiscard)
             and discardingPlayer.playedDangerous):
         player.usedDangerousFrom = discardingPlayer
         if Debug.dangerousGame:
             logDebug('%s claims dangerous tile %s discarded by %s' % \
                      (player, lastDiscard, discardingPlayer))
         block.tellAll(player, Message.UsedDangerousFrom, source=discardingPlayer.name)
     block.tellAll(player, nextMessage, source=meldTiles)
     if claim == Message.Kong:
         block.callback(self.pickKongReplacement)
     else:
         block.callback(self.moved)
Beispiel #9
0
 def perspective_setClientProperties(self, dbIdent, voiceId, maxGameId, clientVersion=None):
     """perspective_* methods are to be called remotely"""
     self.dbIdent = dbIdent
     self.voiceId = voiceId
     self.maxGameId = maxGameId
     serverVersion = InternalParameters.version
     if clientVersion != serverVersion:
         # we assume that versions x.y.* are compatible
         if clientVersion is None:
             # client passed no version info
             return fail(srvError(pb.Error,
                 m18nE('Your client has a version older than 4.9.0 but you need %1 for this server'),
                     serverVersion))
         else:
             commonDigits = len([x for x in zip(
                 clientVersion.split('.'),
                 serverVersion.split('.'))
                 if x[0] == x[1]])
             if commonDigits < 2:
                 return fail(srvError(pb.Error,
                     m18nE('Your client has version %1 but you need %2 for this server'),
                         clientVersion or '<4.9.0',
                         '.'.join(serverVersion.split('.')[:2]) + '.*'))
Beispiel #10
0
 def declareKong(self, player, meldTiles):
     """player declares a Kong, meldTiles is a list"""
     if not player.hasConcealedTiles(meldTiles) and not player.hasExposedPungOf(meldTiles[0]):
         # pylint: disable=W0142
         msg = m18nE('declareKong:%1 wrongly said Kong for meld %2')
         args = (player.name, ''.join(meldTiles))
         logError(m18n(msg, *args))
         logError('declareKong:concealedTileNames:%s' % ''.join(player.concealedTileNames))
         logError('declareKong:concealedMelds:%s' % \
             ' '.join(x.joined for x in player.concealedMelds))
         logError('declareKong:exposedMelds:%s' % \
             ' '.join(x.joined for x in player.exposedMelds))
         self.abort(msg, *args)
         return
     player.exposeMeld(meldTiles)
     self.tellAll(player, Message.DeclaredKong, self.pickKongReplacement, source=meldTiles)
Beispiel #11
0
 def readyForGameStart(self, user):
     """the table initiator told us he wants to start the game"""
     # pylint: disable=R0912
     # pylint too many branches
     if len(self.users) < self.maxSeats() and self.owner != user:
         raise srvError(pb.Error,
             m18nE('Only the initiator %1 can start this game, you are %2'),
             self.owner.name, user.name)
     if not self.suspended:
         self.preparedGame = self.prepareNewGame()
     game = self.preparedGame
     self.connectPlayers(game)
     self.__checkDbIdents(game)
     if self.suspended:
         self.initGame()
     else:
         self.proposeGameId(self.calcGameId())
Beispiel #12
0
 def nextHand(self, dummyResults):
     """next hand: maybe rotate"""
     if not self.game:
         return
     DeferredBlock.garbageCollection()
     for block in DeferredBlock.blocks:
         if block.table == self:
             logError('request left from previous hand: %s' % block.outstandingStr())
     token = self.game.handId() # we need to send the old token until the
                                # clients started the new hand
     rotateWinds = self.game.maybeRotateWinds()
     if self.game.finished():
         self.close('gameOver', m18nE('The game is over!'))
         return
     self.game.sortPlayers()
     playerNames = list((x.wind, x.name) for x in self.game.players)
     self.tellAll(None, Message.ReadyForHandStart, self.startHand,
         source=playerNames, rotateWinds=rotateWinds, token=token)
Beispiel #13
0
 def chat(self):
     """chat. Only generate ChatWindow after the
     message has successfully been sent to the server.
     Because the server might have gone away."""
     def initChat(_):
         """now that we were able to send the message to the server
         instantiate the chat window"""
         table.chatWindow = ChatWindow(table)
         table.chatWindow.receiveLine(msg)
     table = self.selectedTable()
     if not table.chatWindow:
         line = m18nE('opens a chat window')
         msg = ChatMessage(table.tableid, table.client.name, line, isStatusMessage=True)
         table.client.sendChat(msg).addCallback(initChat).addErrback(self.client.tableError)
     elif table.chatWindow.isVisible():
         table.chatWindow.hide()
     else:
         table.chatWindow.show()
Beispiel #14
0
 def claimMahJongg(self, msg):
     """a player claims mah jongg. Check this and if correct, tell all."""
     # pylint: disable=R0912
     # too many branches
     if not self.game:
         return
     player = msg.player
     concealedMelds, withDiscard, lastMeld = msg.args
     if self.game.ruleset.mustDeclareCallingHand:
         assert player.isCalling, '%s: concmelds:%s withdiscard:%s lastmeld:%s' % (
             player, concealedMelds, withDiscard, lastMeld)
     discardingPlayer = self.game.activePlayer
     # pylint: disable=E1103
     # (pylint ticket 8774)
     lastMove = self.game.lastMoves(without=[Message.PopupMsg]).next()
     robbedTheKong = lastMove.message == Message.DeclaredKong
     if robbedTheKong:
         player.lastSource = 'k'
         withDiscard = lastMove.source[0].capitalize()
         lastMove.player.robTile(withDiscard)
     lastMeld = Meld(lastMeld)
     msgArgs = player.showConcealedMelds(concealedMelds, withDiscard)
     if msgArgs:
         self.abort(*msgArgs) # pylint: disable=W0142
     player.declaredMahJongg(concealedMelds, withDiscard, player.lastTile, lastMeld)
     if not player.hand.won:
         msg = m18nE('%1 claiming MahJongg: This is not a winning hand: %2')
         self.abort(msg, player.name, player.hand.string)
         return
     block = DeferredBlock(self)
     if robbedTheKong:
         block.tellAll(player, Message.RobbedTheKong, tile=withDiscard)
     if (player.lastSource == 'd'
             and self.game.dangerousFor(discardingPlayer, player.lastTile)
             and discardingPlayer.playedDangerous):
         player.usedDangerousFrom = discardingPlayer
         if Debug.dangerousGame:
             logDebug('%s wins with dangerous tile %s from %s' % \
                          (player, self.game.lastDiscard, discardingPlayer))
         block.tellAll(player, Message.UsedDangerousFrom, source=discardingPlayer.name)
     block.tellAll(player, Message.MahJongg, source=concealedMelds, lastTile=player.lastTile,
                  lastMeld=list(lastMeld.pairs), withDiscard=withDiscard)
     block.callback(self.endHand)
Beispiel #15
0
 def tell(self, about, receivers, command, **kwargs):
     """send info about player 'about' to users 'receivers'"""
     if about.__class__.__name__ == 'User':
         about = self.playerForUser(about)
     if not isinstance(receivers, list):
         receivers = list([receivers])
     assert receivers, 'DeferredBlock.tell(%s) has no receiver' % command
     self.__enrichMessage(self.table.game, about, command, kwargs)
     aboutName = about.name if about else None
     if self.table.running and len(receivers) in [1, 4]:
         # messages are either identical for all 4 players
         # or identical for 3 players and different for 1 player. And
         # we want to capture each message exactly once.
         self.table.game.appendMove(about, command, kwargs)
     localDeferreds = []
     for rec in self.__convertReceivers(receivers):
         isClient = rec.__class__.__name__.endswith('Client')
         if Debug.traffic and not isClient:
             message = '-> {receiver:<15} about {about} {command}{kwargs}'.format(
                 receiver=rec.name[:15],
                 about=about,
                 command=command,
                 kwargs=Move.prettyKwargs(kwargs))
             logDebug(message)
         if isClient:
             defer = Deferred()
             defer.addCallback(rec.remote_move, command, **kwargs)
         else:
             defer = self.table.server.callRemote(rec, 'move', aboutName,
                                                  command.name, **kwargs)
         if defer:
             defer.command = command.name
             defer.notifying = 'notifying' in kwargs
             self.__addRequest(defer, rec, about)
         else:
             msg = m18nE('The game server lost connection to player %1')
             self.table.abort(msg, rec.name)
         if isClient:
             localDeferreds.append(defer)
     for defer in localDeferreds:
         defer.callback(aboutName)  # callback needs an argument !
Beispiel #16
0
 def joinTable(self, user, tableid):
     """user joins table"""
     if tableid in self.tables:
         table = self._lookupTable(tableid)
         table.addUser(user)
         for user in self.users:
             self.callRemote(user, 'replaceTable', table.msg(user))
         return True
     else:
         # might be a suspended table:
         for suspTable in self.suspendedTables.values():
             assert isinstance(suspTable.preparedGame, RemoteGame), suspTable.preparedGame
             if suspTable.tableid == tableid:
                 self.setTableId(suspTable) # puts suspTable into self.tables
                 del self.suspendedTables[suspTable.preparedGame.gameid]
                 suspTable.addUser(user)
                 for user in self.users:
                     self.callRemote(user, 'replaceTable', suspTable.msg(user))
                 if len(suspTable.users) == suspTable.maxSeats():
                     suspTable.readyForGameStart(suspTable.owner)
                 return True
     raise srvError(pb.Error, m18nE('table with id <numid>%1</numid> not found'), tableid)
Beispiel #17
0
 def tell(self, about, receivers, command, **kwargs):
     """send info about player 'about' to users 'receivers'"""
     if about.__class__.__name__ == 'User':
         about = self.playerForUser(about)
     if not isinstance(receivers, list):
         receivers = list([receivers])
     assert receivers, 'DeferredBlock.tell(%s) has no receiver' % command
     self.__enrichMessage(self.table.game, about, command, kwargs)
     aboutName = about.name if about else None
     if self.table.running and len(receivers) in [1, 4]:
         # messages are either identical for all 4 players
         # or identical for 3 players and different for 1 player. And
         # we want to capture each message exactly once.
         self.table.game.appendMove(about, command, kwargs)
     localDeferreds = []
     for rec in self.__convertReceivers(receivers):
         isClient = rec.__class__.__name__.endswith('Client')
         if Debug.traffic and not isClient:
             message = '-> {receiver:<15} about {about} {command}{kwargs}'.format(
                 receiver=rec.name[:15], about=about, command=command,
                 kwargs=Move.prettyKwargs(kwargs))
             logDebug(message)
         if isClient:
             defer = Deferred()
             defer.addCallback(rec.remote_move, command, **kwargs)
         else:
             defer = self.table.server.callRemote(rec, 'move', aboutName, command.name, **kwargs)
         if defer:
             defer.command = command.name
             defer.notifying = 'notifying' in kwargs
             self.__addRequest(defer, rec, about)
         else:
             msg = m18nE('The game server lost connection to player %1')
             self.table.abort(msg, rec.name)
         if isClient:
             localDeferreds.append(defer)
     for defer in localDeferreds:
         defer.callback(aboutName) # callback needs an argument !
Beispiel #18
0
    def chat(self):
        """chat. Only generate ChatWindow after the
        message has successfully been sent to the server.
        Because the server might have gone away."""
        def initChat(_):
            """now that we were able to send the message to the server
            instantiate the chat window"""
            table.chatWindow = ChatWindow(table)
            table.chatWindow.receiveLine(msg)

        table = self.selectedTable()
        if not table.chatWindow:
            line = m18nE('opens a chat window')
            msg = ChatMessage(table.tableid,
                              table.client.name,
                              line,
                              isStatusMessage=True)
            table.client.sendChat(msg).addCallback(initChat).addErrback(
                self.client.tableError)
        elif table.chatWindow.isVisible():
            table.chatWindow.hide()
        else:
            table.chatWindow.show()
Beispiel #19
0
    def loadRules(self):
# TODO: we need a separate part for any number of announcements. Both r for robbing kong and a for
# Original Call can be possible together.
        ClassicalChinese.loadRules(self)
        del self.winnerRules['Zero Point Hand']
        originalCall = self.winnerRules.pop('Mah Jongg with Original Call')
        originalCall.name = m18nE('Original Call')
        self.handRules.add(originalCall)
        del self.mjRules['Nine Gates']
        self.mjRules.add(Rule('Gates of Heaven', 'FGatesOfHeaven||Opair28', limits=1,
                description=m18n('All tiles concealed of same color: Values 1-1-1-2-3-4-5-6-7-8-9-9-9 and '
                'another tile 2..8 of the same color')))
        self.mjRules.add(Rule('Wriggling Snake', 'FWrigglingSnake', limits=1,
                description=m18n('Pair of 1s and a run from 2 to 9 in the same suit with each of the winds')))
        self.mjRules.add(Rule('Triple Knitting', 'FTripleKnitting', limits=0.5,
                description=m18n('Four sets of three tiles in the different suits and a pair: No Winds or Dragons')))
        self.mjRules.add(Rule('Knitting', 'FKnitting', limits=0.5,
                description=m18n('7 pairs of tiles in any 2 out of 3 suits; no Winds or Dragons')))
        self.mjRules.add(Rule('All pair honors', 'FAllPairHonors', limits=0.5,
                description=m18n('7 pairs of 1s/9s/Winds/Dragons')))
        del self.handRules['Own Flower and Own Season']
        del self.handRules['Three Concealed Pongs']
        self.handRules.add(Rule('Own Flower', 'FOwnFlower', doubles=1))
        self.handRules.add(Rule('Own Season', 'FOwnSeason', doubles=1))
        del self.winnerRules['Last Tile Taken from Dead Wall']
        del self.winnerRules['Hidden Treasure']
        del self.winnerRules['False Color Game']
        del self.winnerRules['Concealed True Color Game']
        del self.winnerRules['East won nine times in a row']
        del self.winnerRules['Last Tile Completes Pair of 2..8']
        del self.winnerRules['Last Tile Completes Pair of Terminals or Honors']
        del self.winnerRules['Last Tile is Only Possible Tile']
        self.winnerRules.add(Rule('Buried Treasure', 'FBuriedTreasure', limits=1,
                description=m18n('Concealed pungs of one suit with winds/dragons and a pair')))
        del self.winnerRules['True Color Game']
        self.winnerRules.add(Rule('Purity', 'FPurity', doubles=3,
                description=m18n('Only same-colored tiles (no chows, dragons or winds)')))
        self.winnerRules['All Greens'].name = m18nE('Imperial Jade')
        self.mjRules['Thirteen Orphans'].name = m18nE('The 13 Unique Wonders')
        del self.winnerRules['Three Great Scholars']
        self.winnerRules.add(Rule('Three Great Scholars', 'FThreeGreatScholars||Onochow', limits=1,
                description=m18n('3 Pungs or Kongs of dragons plus any pung/kong and a pair')))
        self.handRules['All Flowers'].score.doubles = 2
        self.handRules['All Seasons'].score.doubles = 2
        self.penaltyRules.add(Rule('False Naming of Discard, Claimed for Chow/Pung/Kong', points = -50))
        self.penaltyRules.add(Rule('False Declaration of Mah Jongg by One Player',
                'Oabsolute payees=3', limits = -0.5))
        self.winnerRules.add(Rule('False Naming of Discard, Claimed for Mah Jongg', 'FFalseDiscardForMJ||Opayforall'))

        self.loserRules.add(Rule('Calling for Only Honors', 'FCallingHand||Ohand=OnlyHonors', limits=0.4))
        self.loserRules.add(Rule('Calling for Wriggling Snake', 'FCallingHand||Ohand=WrigglingSnake', limits=0.4))
        self.loserRules.add(Rule('Calling for Triple Knitting', 'FCallingHand||Ohand=TripleKnitting', limits=0.2))
        self.loserRules.add(Rule('Calling for Gates of Heaven', 'FCallingHand||Ohand=GatesOfHeaven||Opair28',
                limits=0.4))
        self.loserRules.add(Rule('Calling for Knitting', 'FCallingHand||Ohand=Knitting', limits=0.2))
        self.loserRules.add(Rule('Calling for Imperial Jade', 'FCallingHand||Ohand=AllGreen', limits=0.4))
        self.loserRules.add(Rule('Calling for 13 Unique Wonders', 'FCallingHand||Ohand=ThirteenOrphans',
                limits=0.4))
        self.loserRules.add(Rule('Calling for Three Great Scholars', 'FCallingHand||Ohand=ThreeGreatScholars',
                limits=0.4))
        self.loserRules.add(Rule('Calling for All pair honors', 'FCallingHand||Ohand=AllPairHonors', limits=0.2))
        self.loserRules.add(Rule('Calling for Heads and Tails', 'FCallingHand||Ohand=AllTerminals', limits=0.4))
        self.loserRules.add(Rule('Calling for Four Blessings Hovering over the Door',
                'FCallingHand||Ohand=FourBlessingsHoveringOverTheDoor', limits=0.4))
        self.loserRules.add(Rule('Calling for Buried Treasure', 'FCallingHand||Ohand=BuriedTreasure', limits=0.4))
        self.loserRules.add(Rule('Calling for Fourfold Plenty', 'FCallingHand||Ohand=FourfoldPlenty', limits=0.4))
        self.loserRules.add(Rule('Calling for Purity', 'FCallingHand||Ohand=Purity', doubles=3))
Beispiel #20
0
class Score(object):
    """holds all parts contributing to a score. It has two use cases:
    1. for defining what a rules does: either points or doubles or limits, holding never more than one unit
    2. for summing up the scores of all rules: Now more than one of the units can be in use. If a rule
    should want to set more than one unit, split it into two rules.
    For the first use case only we have the attributes value and unit"""

    __hash__ = None

    def __init__(self, points=0, doubles=0, limits=0, ruleset=None):
        self.points = 0  # define the types for those values
        self.doubles = 0
        self.limits = 0.0
        self.ruleset = ruleset
        self.points = type(self.points)(points)
        self.doubles = type(self.doubles)(doubles)
        self.limits = type(self.limits)(limits)

    unitNames = {
        m18nE('points'): 0,
        m18nE('doubles'): 50,
        m18nE('limits'): 9999
    }

    def clear(self):
        """set all to 0"""
        self.points = self.doubles = self.limits = 0

    def change(self, unitName, value):
        """sets value for unitName. If changed, return True"""
        oldValue = self.__getattribute__(unitName)
        if isinstance(value, QVariant):
            value = value.toString()
        newValue = type(oldValue)(value)
        if newValue == oldValue:
            return False, None
        if newValue:
            if unitName == 'points':
                if self.doubles:
                    return False, 'Cannot have points and doubles'
            if unitName == 'doubles':
                if self.points:
                    return False, 'Cannot have points and doubles'
        self.__setattr__(unitName, newValue)
        return True, None

    def __str__(self):
        """make score printable"""
        parts = []
        if self.points:
            parts.append('points=%d' % self.points)
        if self.doubles:
            parts.append('doubles=%d' % self.doubles)
        if self.limits:
            parts.append('limits=%f' % self.limits)
        return ' '.join(parts)

    def __repr__(self):
        return 'Score(%s)' % str(self)

    def contentStr(self):
        """make score readable for humans, i18n"""
        parts = []
        if self.points:
            parts.append(m18nc('Kajongg', '%1 points', self.points))
        if self.doubles:
            parts.append(m18nc('Kajongg', '%1 doubles', self.doubles))
        if self.limits:
            parts.append(m18nc('Kajongg', '%1 limits', self.limits))
        return ' '.join(parts)

    def __eq__(self, other):
        """ == comparison """
        assert isinstance(other, Score)
        return self.points == other.points and self.doubles == other.doubles and self.limits == other.limits

    def __ne__(self, other):
        """ != comparison """
        return self.points != other.points or self.doubles != other.doubles or self.limits != other.limits

    def __lt__(self, other):
        return self.total() < other.total()

    def __le__(self, other):
        return self.total() <= other.total()

    def __gt__(self, other):
        return self.total() > other.total()

    def __ge__(self, other):
        return self.total() >= other.total()

    def __add__(self, other):
        """implement adding Score"""
        return Score(self.points + other.points, self.doubles + other.doubles,
                     max(self.limits, other.limits), self.ruleset
                     or other.ruleset)

    def total(self):
        """the total score"""
        if self.ruleset is None:
            raise Exception('Score.total: ruleset unknown for %s' % self)
        score = int(self.points * (2**self.doubles))
        if self.limits:
            if self.limits >= 1:
                self.points = self.doubles = 0
            elif self.limits * self.ruleset.limit >= score:
                self.points = self.doubles = 0
            else:
                self.limits = 0
        if self.limits:
            return int(round(self.limits * self.ruleset.limit))
        if not self.ruleset.roofOff:
            score = min(score, self.ruleset.limit)
        return score

    def __int__(self):
        """the total score"""
        return self.total()

    def __nonzero__(self):
        """for bool() conversion"""
        return self.points != 0 or self.doubles != 0 or self.limits != 0
Beispiel #21
0
 def __init__(self, name=None):
     PredefinedRuleset.__init__(self, name
                                or m18nE('Classical Chinese standard'))
Beispiel #22
0
 def __init__(self, name=None):
     PredefinedRuleset.__init__(self, name or m18nE('Classical Chinese standard'))
Beispiel #23
0
 def __init__(self, name=None):
     ClassicalChinese.__init__(self, name
                               or m18nE('Classical Chinese BMJA'))
Beispiel #24
0
    def loadRules(self):
        # TODO: we need a separate part for any number of announcements. Both r for robbing kong and a for
        # Original Call can be possible together.
        ClassicalChinese.loadRules(self)
        del self.winnerRules['Zero Point Hand']
        originalCall = self.winnerRules.pop('Mah Jongg with Original Call')
        originalCall.name = m18nE('Original Call')
        self.handRules.add(originalCall)
        del self.mjRules['Nine Gates']
        self.mjRules.add(
            Rule(
                'Gates of Heaven',
                'FGatesOfHeaven||Opair28',
                limits=1,
                description=m18n(
                    'All tiles concealed of same color: Values 1-1-1-2-3-4-5-6-7-8-9-9-9 and '
                    'another tile 2..8 of the same color')))
        self.mjRules.add(
            Rule(
                'Wriggling Snake',
                'FWrigglingSnake',
                limits=1,
                description=m18n(
                    'Pair of 1s and a run from 2 to 9 in the same suit with each of the winds'
                )))
        self.mjRules.add(
            Rule(
                'Triple Knitting',
                'FTripleKnitting',
                limits=0.5,
                description=m18n(
                    'Four sets of three tiles in the different suits and a pair: No Winds or Dragons'
                )))
        self.mjRules.add(
            Rule(
                'Knitting',
                'FKnitting',
                limits=0.5,
                description=m18n(
                    '7 pairs of tiles in any 2 out of 3 suits; no Winds or Dragons'
                )))
        self.mjRules.add(
            Rule('All pair honors',
                 'FAllPairHonors',
                 limits=0.5,
                 description=m18n('7 pairs of 1s/9s/Winds/Dragons')))
        del self.handRules['Own Flower and Own Season']
        del self.handRules['Three Concealed Pongs']
        self.handRules.add(Rule('Own Flower', 'FOwnFlower', doubles=1))
        self.handRules.add(Rule('Own Season', 'FOwnSeason', doubles=1))
        del self.winnerRules['Last Tile Taken from Dead Wall']
        del self.winnerRules['Hidden Treasure']
        del self.winnerRules['False Color Game']
        del self.winnerRules['Concealed True Color Game']
        del self.winnerRules['East won nine times in a row']
        del self.winnerRules['Last Tile Completes Pair of 2..8']
        del self.winnerRules['Last Tile Completes Pair of Terminals or Honors']
        del self.winnerRules['Last Tile is Only Possible Tile']
        self.winnerRules.add(
            Rule(
                'Buried Treasure',
                'FBuriedTreasure',
                limits=1,
                description=m18n(
                    'Concealed pungs of one suit with winds/dragons and a pair'
                )))
        del self.winnerRules['True Color Game']
        self.winnerRules.add(
            Rule('Purity',
                 'FPurity',
                 doubles=3,
                 description=m18n(
                     'Only same-colored tiles (no chows, dragons or winds)')))
        self.winnerRules['All Greens'].name = m18nE('Imperial Jade')
        self.mjRules['Thirteen Orphans'].name = m18nE('The 13 Unique Wonders')
        del self.winnerRules['Three Great Scholars']
        self.winnerRules.add(
            Rule(
                'Three Great Scholars',
                'FThreeGreatScholars||Onochow',
                limits=1,
                description=m18n(
                    '3 Pungs or Kongs of dragons plus any pung/kong and a pair'
                )))
        self.handRules['All Flowers'].score.doubles = 2
        self.handRules['All Seasons'].score.doubles = 2
        self.penaltyRules.add(
            Rule('False Naming of Discard, Claimed for Chow/Pung/Kong',
                 points=-50))
        self.penaltyRules.add(
            Rule('False Declaration of Mah Jongg by One Player',
                 'Oabsolute payees=3',
                 limits=-0.5))
        self.winnerRules.add(
            Rule('False Naming of Discard, Claimed for Mah Jongg',
                 'FFalseDiscardForMJ||Opayforall'))

        self.loserRules.add(
            Rule('Calling for Only Honors',
                 'FCallingHand||Ohand=OnlyHonors',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Wriggling Snake',
                 'FCallingHand||Ohand=WrigglingSnake',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Triple Knitting',
                 'FCallingHand||Ohand=TripleKnitting',
                 limits=0.2))
        self.loserRules.add(
            Rule('Calling for Gates of Heaven',
                 'FCallingHand||Ohand=GatesOfHeaven||Opair28',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Knitting',
                 'FCallingHand||Ohand=Knitting',
                 limits=0.2))
        self.loserRules.add(
            Rule('Calling for Imperial Jade',
                 'FCallingHand||Ohand=AllGreen',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for 13 Unique Wonders',
                 'FCallingHand||Ohand=ThirteenOrphans',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Three Great Scholars',
                 'FCallingHand||Ohand=ThreeGreatScholars',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for All pair honors',
                 'FCallingHand||Ohand=AllPairHonors',
                 limits=0.2))
        self.loserRules.add(
            Rule('Calling for Heads and Tails',
                 'FCallingHand||Ohand=AllTerminals',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Four Blessings Hovering over the Door',
                 'FCallingHand||Ohand=FourBlessingsHoveringOverTheDoor',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Buried Treasure',
                 'FCallingHand||Ohand=BuriedTreasure',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Fourfold Plenty',
                 'FCallingHand||Ohand=FourfoldPlenty',
                 limits=0.4))
        self.loserRules.add(
            Rule('Calling for Purity', 'FCallingHand||Ohand=Purity',
                 doubles=3))
Beispiel #25
0
 def _lookupTable(self, tableid):
     """return table by id or raise exception"""
     if tableid not in self.tables:
         raise srvError(pb.Error, m18nE('table with id <numid>%1</numid> not found'), tableid)
     return self.tables[tableid]
Beispiel #26
0
 def _checkedPassword(matched, userid):
     """after the password has been checked"""
     if not matched:
         return fail(credError.UnauthorizedLogin(srvMessage(m18nE('Wrong password'))))
     return userid
Beispiel #27
0
 def __init__(self, name=None):
     ClassicalChinese.__init__(self, name or m18nE('Classical Chinese BMJA'))