Exemple #1
0
class Merge(HandHistoryConverter):
    sitename = "Merge"
    filetype = "text"
    codepage = ("cp1252", "utf8")
    siteId = 12
    copyGameHeader = True
    Structures = MergeStructures.MergeStructures()

    limits = {
        'No Limit': 'nl',
        'No Limit ': 'nl',
        'Limit': 'fl',
        'Pot Limit': 'pl',
        'Pot Limit ': 'pl',
        'Half Pot Limit': 'hp'
    }
    games = {              # base, category
                    'Holdem' : ('hold','holdem'),
                    'Omaha'  : ('hold','omahahi'),
               'Omaha H/L8'  : ('hold','omahahilo'),
              '2-7 Lowball'  : ('draw','27_3draw'),
              'A-5 Lowball'  : ('draw','a5_3draw'),
                   'Badugi'  : ('draw','badugi'),
           '5-Draw w/Joker'  : ('draw','fivedraw'),
                   '5-Draw'  : ('draw','fivedraw'),
                   '7-Stud'  : ('stud','studhi'),
              '7-Stud H/L8'  : ('stud','studhilo'),
                   '5-Stud'  : ('stud','5studhi'),
                     'Razz'  : ('stud','razz'),
            }

    mixes = {
        'HA': 'ha',
        'RASH': 'rash',
        'HO': 'ho',
        'SHOE': 'shoe',
        'HORSE': 'horse',
        'HOSE': 'hose',
        'HAR': 'har'
    }

    Lim_Blinds = {
        '0.04': ('0.01', '0.02'),
        '0.10': ('0.02', '0.05'),
        '0.20': ('0.05', '0.10'),
        '0.25': ('0.05', '0.10'),
        '0.50': ('0.10', '0.25'),
        '1.00': ('0.25', '0.50'),
        '1': ('0.25', '0.50'),
        '2.00': ('0.50', '1.00'),
        '2': ('0.50', '1.00'),
        '4.00': ('1.00', '2.00'),
        '4': ('1.00', '2.00'),
        '6.00': ('1.50', '3.00'),
        '6': ('1.50', '3.00'),
        '8.00': ('2.00', '4.00'),
        '8': ('2.00', '4.00'),
        '10.00': ('2.00', '5.00'),
        '10': ('2.00', '5.00'),
        '12.00': ('3.00', '6.00'),
        '12': ('3.00', '6.00'),
        '20.00': ('5.00', '10.00'),
        '20': ('5.00', '10.00'),
        '30.00': ('10.00', '15.00'),
        '30': ('10.00', '15.00'),
        '40.00': ('10.00', '20.00'),
        '40': ('10.00', '20.00'),
        '50.00': ('10.00', '25.00'),
        '50': ('10.00', '25.00'),
        '60.00': ('15.00', '30.00'),
        '60': ('15.00', '30.00'),
        '100.00': ('25.00', '50.00'),
        '100': ('25.00', '50.00'),
        '200.00': ('50.00', '100.00'),
        '200': ('50.00', '100.00'),
        '400.00': ('100.00', '200.00'),
        '400': ('100.00', '200.00'),
    }

    Multigametypes = {
        '1': ('hold', 'holdem'),
        '2': ('hold', 'holdem'),
        '4': ('hold', 'omahahi'),
        '9': ('hold', 'holdem'),
        '23': ('hold', 'holdem'),
        '34': ('hold', 'omahahilo'),
        '35': ('hold', 'omahahilo'),
        '37': ('hold', 'omahahilo'),
        '38': ('stud', 'studhi'),
        '39': ('stud', 'studhi'),
        '41': ('stud', 'studhi'),
        '42': ('stud', 'studhi'),
        '43': ('stud', 'studhilo'),
        '45': ('stud', 'studhilo'),
        '46': ('stud', 'razz'),
        '47': ('stud', 'razz'),
        '49': ('stud', 'razz')
    }

    # Static regexes
    re_Identify = re.compile(u'<game\sid=\"[0-9]+\-[0-9]+\"\sstarttime')
    re_SplitHands = re.compile(r'</game>\n+(?=<)')
    re_TailSplitHands = re.compile(r'(</game>)')
    re_GameInfo = re.compile(
        r'<description type="(?P<GAME>Holdem|Omaha|Omaha|Omaha\sH/L8|2\-7\sLowball|A\-5\sLowball|Badugi|5\-Draw\sw/Joker|5\-Draw|7\-Stud|7\-Stud\sH/L8|5\-Stud|Razz|HORSE|RASH|HA|HO|SHOE|HOSE|HAR)(?P<TYPE>\sTournament)?" stakes="(?P<LIMIT>[a-zA-Z ]+)(\s\(?\$?(?P<SB>[.0-9]+)?/?\$?(?P<BB>[.0-9]+)?(?P<blah>.*)\)?)?"(\sversion="\d+")?\s?/>',
        re.MULTILINE)
    # <game id="46154255-645" starttime="20111230232051" numholecards="2" gametype="1" seats="9" realmoney="false" data="20111230|Play Money (46154255)|46154255|46154255-645|false">
    # <game id="46165919-1" starttime="20111230161824" numholecards="2" gametype="23" seats="10" realmoney="true" data="20111230|Fun Step 1|46165833-1|46165919-1|true">
    # <game id="46289039-1" starttime="20120101200100" numholecards="2" gametype="23" seats="9" realmoney="true" data="20120101|$200 Freeroll - NL Holdem - 20%3A00|46245544-1|46289039-1|true">
    re_HandInfo = re.compile(
        r'<game id="(?P<HID1>[0-9]+)-(?P<HID2>[0-9]+)" starttime="(?P<DATETIME>.+?)" numholecards="[0-9]+" gametype="[0-9]+" (multigametype="(?P<MULTIGAMETYPE1>\d+)" )?(seats="(?P<SEATS>[0-9]+)" )?realmoney="(?P<REALMONEY>(true|false))" (multigametype="(?P<MULTIGAMETYPE2>\d+)" )?(data="[0-9]+[|:](?P<TABLENAME>[^|:]+)[|:](?P<TDATA>[^|:]+)[|:]?)?.*>',
        re.MULTILINE)
    re_Button = re.compile(r'<players dealer="(?P<BUTTON>[0-9]+)"\s?>')
    re_PlayerInfo = re.compile(
        r'<player seat="(?P<SEAT>[0-9]+)" nickname="(?P<PNAME>.+)" balance="\$?(?P<CASH>[.0-9]+)" dealtin="(?P<DEALTIN>(true|false))" />',
        re.MULTILINE)
    re_Board = re.compile(r'<cards type="COMMUNITY" cards="(?P<CARDS>[^"]+)"',
                          re.MULTILINE)
    re_Buyin = re.compile(
        r'\$(?P<BUYIN>[.,0-9]+)\s(?P<TYPE>Freeroll|Satellite|Guaranteed)?',
        re.MULTILINE)
    re_secondGame = re.compile(r'\$?(?P<SB>[.0-9]+)?/?\$?(?P<BB>[.0-9]+)',
                               re.MULTILINE)

    # The following are also static regexes: there is no need to call
    # compilePlayerRegexes (which does nothing), since players are identified
    # not by name but by seat number
    re_PostSB = re.compile(
        r'<event sequence="[0-9]+" type="SMALL_BLIND" (?P<TIMESTAMP>timestamp="[0-9]+" )?player="(?P<PSEAT>[0-9])" amount="(?P<SB>[.0-9]+)"\s?/>',
        re.MULTILINE)
    re_PostBB = re.compile(
        r'<event sequence="[0-9]+" type="(BIG_BLIND|INITIAL_BLIND)" (?P<TIMESTAMP>timestamp="[0-9]+" )?player="(?P<PSEAT>[0-9])" amount="(?P<BB>[.0-9]+)"\s?/>',
        re.MULTILINE)
    re_PostBoth = re.compile(
        r'<event sequence="[0-9]+" type="RETURN_BLIND" (?P<TIMESTAMP>timestamp="[0-9]+" )?player="(?P<PSEAT>[0-9])" amount="(?P<SBBB>[.0-9]+)"\s?/>',
        re.MULTILINE)
    re_Antes = re.compile(
        r'<event sequence="[0-9]+" type="ANTE" (?P<TIMESTAMP>timestamp="\d+" )?player="(?P<PSEAT>[0-9])" amount="(?P<ANTE>[.0-9]+)"\s?/>',
        re.MULTILINE)
    re_BringIn = re.compile(
        r'<event sequence="[0-9]+" type="BRING_IN" (?P<TIMESTAMP>timestamp="\d+" )?player="(?P<PSEAT>[0-9])" amount="(?P<BRINGIN>[.0-9]+)"\s?/>',
        re.MULTILINE)
    re_HeroCards = re.compile(
        r'<cards type="(HOLE|DRAW_DRAWN_CARDS)" cards="(?P<CARDS>.+)" player="(?P<PSEAT>[0-9])"',
        re.MULTILINE)
    re_Action = re.compile(
        r'<event sequence="[0-9]+" type="(?P<ATYPE>FOLD|CHECK|CALL|BET|RAISE|ALL_IN|SIT_OUT|DRAW|COMPLETE)"( timestamp="(?P<TIMESTAMP>[0-9]+)")? player="(?P<PSEAT>[0-9])"( amount="(?P<BET>[.0-9]+)")?( text="(?P<TXT>.+)")?\s?/>',
        re.MULTILINE)
    re_AllActions = re.compile(
        r'<event sequence="[0-9]+" type="(?P<ATYPE>FOLD|CHECK|CALL|BET|RAISE|ALL_IN|SIT_OUT|DRAW|COMPLETE|BIG_BLIND|INITIAL_BLIND|SMALL_BLIND|RETURN_BLIND|BRING_IN|ANTE)"( timestamp="(?P<TIMESTAMP>[0-9]+)")? player="(?P<PSEAT>[0-9])"( amount="(?P<BET>[.0-9]+)")?( text="(?P<TXT>.+)")?\s?/>',
        re.MULTILINE)
    re_CollectPot = re.compile(
        r'<winner amount="(?P<POT>[.0-9]+)" uncalled="(?P<UNCALLED>false|true)" potnumber="[0-9]+" player="(?P<PSEAT>[0-9])"',
        re.MULTILINE)
    re_SitsOut = re.compile(
        r'<event sequence="[0-9]+" type="SIT_OUT" player="(?P<PSEAT>[0-9])"\s?/>',
        re.MULTILINE)
    re_ShownCards = re.compile(
        r'<cards type="(?P<SHOWED>SHOWN|MUCKED)" cards="(?P<CARDS>.+)" player="(?P<PSEAT>[0-9])"\s?/>',
        re.MULTILINE)
    re_Connection = re.compile(
        r'<event sequence="[0-9]+" type="(?P<TYPE>RECONNECTED|DISCONNECTED)" timestamp="[0-9]+" player="[0-9]"\s?/>',
        re.MULTILINE)
    re_Cancelled = re.compile(
        r'<event sequence="\d+" type="GAME_CANCELLED" timestamp="\d+"\s?/>',
        re.MULTILINE)
    re_LeaveTable = re.compile(
        r'<event sequence="\d+" type="LEAVE" timestamp="\d+" player="\d"\s?/>',
        re.MULTILINE)
    re_PlayerOut = re.compile(
        r'<event sequence="\d+" type="(PLAYER_OUT|LEAVE)" timestamp="\d+" player="(?P<PSEAT>[0-9])"\s?/>',
        re.MULTILINE)
    re_EndOfHand = re.compile(r'<round id="END_OF_GAME"', re.MULTILINE)
    re_DateTime = re.compile(
        r'(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)',
        re.MULTILINE)
    re_PlayMoney = re.compile(r'realmoney="false"')

    def compilePlayerRegexs(self, hand):
        pass

    def playerNameFromSeatNo(self, seatNo, hand):
        # This special function is required because Merge Poker records
        # actions by seat number (0 based), not by the player's name
        for p in hand.players:
            if p[0] == int(seatNo) + 1:
                return p[1]

    def readSupportedGames(self):
        return [
            ["ring", "hold", "nl"],
            ["ring", "hold", "pl"],
            ["ring", "hold", "fl"],
            ["ring", "hold", "hp"],
            ["ring", "stud", "fl"],
            ["ring", "stud", "pl"],
            ["ring", "stud", "nl"],
            ["ring", "draw", "fl"],
            ["ring", "draw", "pl"],
            ["ring", "draw", "nl"],
            ["ring", "draw", "hp"],
            ["tour", "hold", "nl"],
            ["tour", "hold", "pl"],
            ["tour", "hold", "fl"],
            ["tour", "stud", "fl"],
            ["tour", "stud", "pl"],
            ["tour", "stud", "nl"],
            ["tour", "draw", "fl"],
            ["tour", "draw", "pl"],
            ["tour", "draw", "nl"],
        ]

    def parseHeader(self, handText, whole_file):
        gametype = self.determineGameType(handText)
        if gametype is None:
            gametype = self.determineGameType(whole_file)
            if gametype is None:
                if not re.search('<description', whole_file):
                    raise FpdbHandPartial(
                        "Partial hand history: No <desription> tag")
                else:
                    tmp = handText[0:200]
                    log.error(_("MergeToFpdb.determineGameType: '%s'") % tmp)
                    raise FpdbParseError
            else:
                if 'mix' in gametype and gametype['mix'] != None:
                    self.mergeMultigametypes(handText)
        return gametype

    def determineGameType(self, handText):
        """return dict with keys/values:
    'type'       in ('ring', 'tour')
    'limitType'  in ('nl', 'cn', 'pl', 'cp', 'fl', 'hp')
    'base'       in ('hold', 'stud', 'draw')
    'category'   in ('holdem', 'omahahi', omahahilo', 'razz', 'studhi', 'studhilo', 'fivedraw', '27_1draw', '27_3draw', 'badugi')
    'hilo'       in ('h','l','s')
    'smallBlind' int?
    'bigBlind'   int?
    'smallBet'
    'bigBet'
    'currency'  in ('USD', 'EUR', 'T$', <countrycode>)
or None if we fail to get the info """

        m = self.re_GameInfo.search(handText)
        if not m: return None

        self.info = {}
        mg = m.groupdict()
        #print "DEBUG: mg: %s" % mg

        if 'LIMIT' in mg:
            self.info['limitType'] = self.limits[mg['LIMIT']]
        if 'GAME' in mg:
            if mg['GAME'] in self.mixes:
                self.info['mix'] = self.mixes[mg['GAME']]
                self.mergeMultigametypes(handText)
            else:
                (self.info['base'],
                 self.info['category']) = self.games[mg['GAME']]
        if 'SB' in mg:
            self.info['sb'] = mg['SB']
        if 'BB' in mg:
            self.info['bb'] = mg['BB']
        self.info['secondGame'] = False
        if mg['blah'] is not None:
            if self.re_secondGame.search(mg['blah']):
                self.info['secondGame'] = True
        if ' Tournament' == mg['TYPE']:
            self.info['type'] = 'tour'
            self.info['currency'] = 'T$'
        else:
            self.info['type'] = 'ring'
            if self.re_PlayMoney.search(handText):
                self.info['currency'] = 'play'
            else:
                self.info['currency'] = 'USD'

        if self.info['limitType'] == 'fl' and self.info[
                'bb'] is not None and self.info['type'] == 'ring':
            try:
                self.info['sb'] = self.Lim_Blinds[mg['BB']][0]
                self.info['bb'] = self.Lim_Blinds[mg['BB']][1]
            except KeyError:
                tmp = handText[0:200]
                log.error(
                    _("MergeToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'"
                      ) % (mg['BB'], tmp))
                raise FpdbParseError

        return self.info

    def readHandInfo(self, hand):
        m = self.re_HandInfo.search(hand.handText)
        if m is None:
            tmp = hand.handText[0:200]
            log.error(_("MergeToFpdb.readHandInfo: '%s'") % tmp)
            raise FpdbParseError

        #print "DEBUG: mg: %s" % m.groupdict()
        self.determineErrorType(hand, None)

        hand.handid = m.group('HID1') + m.group('HID2')

        m1 = self.re_DateTime.search(m.group('DATETIME'))
        if m1:
            mg = m1.groupdict()
            datetimestr = "%s/%s/%s %s:%s:%s" % (mg['Y'], mg['M'], mg['D'],
                                                 mg['H'], mg['MIN'], mg['S'])
            #tz = a.group('TZ')  # just assume ET??
            hand.startTime = datetime.datetime.strptime(
                datetimestr,
                "%Y/%m/%d %H:%M:%S")  # also timezone at end, e.g. " ET"
        else:
            hand.startTime = datetime.datetime.strptime(
                m.group('DATETIME')[:14], '%Y%m%d%H%M%S')

        hand.startTime = HandHistoryConverter.changeTimezone(
            hand.startTime, "ET", "UTC")
        hand.newFormat = datetime.datetime.strptime('20100908000000',
                                                    '%Y%m%d%H%M%S')
        hand.newFormat = HandHistoryConverter.changeTimezone(
            hand.newFormat, "ET", "UTC")

        if hand.gametype['type'] == 'tour':
            tid_table = m.group('TDATA').split('-')
            tid = tid_table[0]
            if len(tid_table) > 1:
                table = tid_table[1]
            else:
                table = '0'
            self.info['tablename'] = m.group('TABLENAME').replace(
                '  - ', ' - ').strip()
            self.info['tourNo'] = hand.tourNo
            hand.tourNo = tid
            hand.tablename = table
            structure = self.Structures.lookupSnG(self.info['tablename'],
                                                  hand.startTime)
            if structure != None:
                hand.buyin = int(100 * structure['buyIn'])
                hand.fee = int(100 * structure['fee'])
                hand.buyinCurrency = structure['currency']
                hand.maxseats = structure['seats']
                hand.isSng = True
                self.summaryInFile = True
            else:
                #print 'DEBUG', 'no match for tourney %s tourNo %s' % (self.info['tablename'], tid)
                hand.buyin = 0
                hand.fee = 0
                hand.buyinCurrency = "NA"
                hand.maxseats = None
                if m.group('SEATS') != None:
                    hand.maxseats = int(m.group('SEATS'))
        else:
            #log.debug("HID %s-%s, Table %s" % (m.group('HID1'), m.group('HID2'), m.group('TABLENAME')))
            hand.maxseats = None
            if m.group('TABLENAME') != None:
                hand.tablename = m.group('TABLENAME')
            else:
                hand.tablename = self.base_name
            if m.group('SEATS') != None:
                hand.maxseats = int(m.group('SEATS'))
        # Check that the hand is complete up to the awarding of the pot; if
        # not, the hand is unparseable
        if self.re_EndOfHand.search(hand.handText) is None:
            self.determineErrorType(hand, "readHandInfo")

    def readPlayerStacks(self, hand):
        acted = {}
        seated = {}
        m = self.re_PlayerInfo.finditer(hand.handText)
        for a in m:
            seatno = a.group('SEAT')
            seated[seatno] = [a.group('PNAME'), a.group('CASH')]

        if hand.gametype['type'] == "ring":
            # We can't 100% trust the 'dealtin' field. So read the actions and see if the players acted
            m2 = self.re_AllActions.finditer(hand.handText)
            fulltable = False
            for action in m2:
                acted[action.group('PSEAT')] = True
                if acted.keys() == seated.keys():  # We've faound all players
                    fulltable = True
                    break
            if fulltable != True:
                for seatno in seated.keys():
                    if seatno not in acted:
                        del seated[seatno]

                for seatno in acted.keys():
                    if seatno not in seated:
                        log.error(
                            _("MergeToFpdb.readPlayerStacks: '%s' Seat:%s acts but not listed"
                              ) % (hand.handid, seatno))
                        raise FpdbParseError

        for seat in seated:
            name, stack = seated[seat]
            # Merge indexes seats from 0. Add 1 so we don't have to add corner cases everywhere else.
            hand.addPlayer(int(seat) + 1, name, stack)

        if hand.maxseats == None:
            if hand.gametype['type'] == 'tour' and self.maxseats == 0:
                hand.maxseats = self.guessMaxSeats(hand)
                self.maxseats = hand.maxseats
            elif hand.gametype['type'] == 'tour':
                hand.maxseats = self.maxseats
            else:
                hand.maxseats = None

        # No players found at all.
        if not hand.players:
            self.determineErrorType(hand, "readPlayerStacks")

    def markStreets(self, hand):
        if hand.gametype['base'] == 'hold':
            m = re.search(
                r'<round id="PREFLOP" sequence="[0-9]+"\s?>(?P<PREFLOP>.+(?=<round id="POSTFLOP")|.+)'
                r'(<round id="POSTFLOP" sequence="[0-9]+"\s?>(?P<FLOP>.+(?=<round id="POSTTURN")|.+))?'
                r'(<round id="POSTTURN" sequence="[0-9]+"\s?>(?P<TURN>.+(?=<round id="POSTRIVER")|.+))?'
                r'(<round id="POSTRIVER" sequence="[0-9]+"\s?>(?P<RIVER>.+))?',
                hand.handText, re.DOTALL)
        elif hand.gametype['base'] == 'draw':
            if hand.gametype['category'] in ('27_3draw', 'badugi', 'a5_3draw'):
                m = re.search(
                    r'(?P<PREDEAL>.+(?=<round id="PRE_FIRST_DRAW" sequence="[0-9]+">)|.+)'
                    r'(<round id="PRE_FIRST_DRAW" sequence="[0-9]+"\s?>(?P<DEAL>.+(?=<round id="FIRST_DRAW")|.+))?'
                    r'(<round id="FIRST_DRAW" sequence="[0-9]+"\s?>(?P<DRAWONE>.+(?=<round id="SECOND_DRAW")|.+))?'
                    r'(<round id="SECOND_DRAW" sequence="[0-9]+"\s?>(?P<DRAWTWO>.+(?=<round id="THIRD_DRAW")|.+))?'
                    r'(<round id="THIRD_DRAW" sequence="[0-9]+"\s?>(?P<DRAWTHREE>.+))?',
                    hand.handText, re.DOTALL)
            else:
                m = re.search(
                    r'(?P<PREDEAL>.+(?=<round id="PRE_FIRST_DRAW" sequence="[0-9]+"\s?>)|.+)'
                    r'(<round id="PRE_FIRST_DRAW" sequence="[0-9]+"\s?>(?P<DEAL>.+(?=<round id="FIRST_DRAW")|.+))?'
                    r'(<round id="FIRST_DRAW" sequence="[0-9]+"\s?>(?P<DRAWONE>.+(?=<round id="SECOND_DRAW")|.+))?',
                    hand.handText, re.DOTALL)
        elif hand.gametype['base'] == 'stud':
            m = re.search(
                r'(?P<ANTES>.+(?=<round id="BRING_IN" sequence="[0-9]+"\s?>)|.+)'
                r'(<round id="BRING_IN" sequence="[0-9]+"\s?>(?P<THIRD>.+(?=<round id="FOURTH_STREET")|.+))?'
                r'(<round id="FOURTH_STREET" sequence="[0-9]+"\s?>(?P<FOURTH>.+(?=<round id="FIFTH_STREET")|.+))?'
                r'(<round id="FIFTH_STREET" sequence="[0-9]+"\s?>(?P<FIFTH>.+(?=<round id="SIXTH_STREET")|.+))?'
                r'(<round id="SIXTH_STREET" sequence="[0-9]+"\s?>(?P<SIXTH>.+(?=<round id="SEVENTH_STREET")|.+))?'
                r'(<round id="SEVENTH_STREET" sequence="[0-9]+"\s?>(?P<SEVENTH>.+))?',
                hand.handText, re.DOTALL)
        if m == None:
            self.determineErrorType(hand, "markStreets")
        hand.addStreets(m)

    def readCommunityCards(self, hand, street):
        m = self.re_Board.search(hand.streets[street])
        if m and street in ('FLOP', 'TURN', 'RIVER'):
            if street == 'FLOP':
                hand.setCommunityCards(street, m.group('CARDS').split(','))
            elif street in ('TURN', 'RIVER'):
                hand.setCommunityCards(street,
                                       [m.group('CARDS').split(',')[-1]])
        else:
            self.determineErrorType(hand, "readCommunityCards")

    def readAntes(self, hand):
        for player in self.re_Antes.finditer(hand.handText):
            pname = self.playerNameFromSeatNo(player.group('PSEAT'), hand)
            #print "DEBUG: hand.addAnte(%s,%s)" %(pname, player.group('ANTE'))
            self.adjustMergeTourneyStack(hand, pname, player.group('ANTE'))
            hand.addAnte(pname, player.group('ANTE'))

    def readBringIn(self, hand):
        m = self.re_BringIn.search(hand.handText)
        if m:
            pname = self.playerNameFromSeatNo(m.group('PSEAT'), hand)
            #print "DEBUG: hand.addBringIn(%s,%s)" %(pname, m.group('BRINGIN'))
            self.adjustMergeTourneyStack(hand, pname, m.group('BRINGIN'))
            hand.addBringIn(pname, m.group('BRINGIN'))

        if hand.gametype['sb'] == None and hand.gametype['bb'] == None:
            hand.gametype['sb'] = "1"
            hand.gametype['bb'] = "2"

    def readBlinds(self, hand):
        if (hand.gametype['category'],
                hand.gametype['limitType']) == ("badugi", "hp"):
            if hand.gametype['sb'] == None and hand.gametype['bb'] == None:
                hand.gametype['sb'] = "1"
                hand.gametype['bb'] = "2"
        else:
            if hand.gametype['base'] == 'hold':
                street = 'PREFLOP'
            elif hand.gametype['base'] == 'draw':
                street = 'DEAL'
            allinBlinds = {}
            blindsantes = hand.handText.split(street)[0]
            bb, sb = None, None
            for a in self.re_PostSB.finditer(blindsantes):
                #print "DEBUG: found sb: '%s' '%s'" %(self.playerNameFromSeatNo(a.group('PSEAT'), hand), a.group('SB'))
                sb = a.group('SB')
                player = self.playerNameFromSeatNo(a.group('PSEAT'), hand)
                self.adjustMergeTourneyStack(hand, player, sb)
                hand.addBlind(player, 'small blind', sb)
                if not hand.gametype['sb'] or hand.gametype['secondGame']:
                    hand.gametype['sb'] = sb
            for a in self.re_PostBB.finditer(blindsantes):
                #print "DEBUG: found bb: '%s' '%s'" %(self.playerNameFromSeatNo(a.group('PSEAT'), hand), a.group('BB'))
                bb = a.group('BB')
                player = self.playerNameFromSeatNo(a.group('PSEAT'), hand)
                self.adjustMergeTourneyStack(hand, player, bb)
                hand.addBlind(player, 'big blind', bb)
                if not hand.gametype['bb'] or hand.gametype['secondGame']:
                    hand.gametype['bb'] = bb
            for a in self.re_PostBoth.finditer(blindsantes):
                bb = Decimal(self.info['bb'])
                amount = Decimal(a.group('SBBB'))
                player = self.playerNameFromSeatNo(a.group('PSEAT'), hand)
                self.adjustMergeTourneyStack(hand, player, a.group('SBBB'))
                if amount < bb:
                    hand.addBlind(player, 'small blind', a.group('SBBB'))
                elif amount == bb:
                    hand.addBlind(player, 'big blind', a.group('SBBB'))
                else:
                    hand.addBlind(player, 'both', a.group('SBBB'))
            if sb is None or bb is None:
                m = self.re_Action.finditer(blindsantes)
                for action in m:
                    player = self.playerNameFromSeatNo(action.group('PSEAT'),
                                                       hand)
                    #print "DEBUG: found: '%s' '%s'" %(self.playerNameFromSeatNo(action.group('PSEAT'), hand), action.group('BET'))
                    if sb is None:
                        if action.group(
                                'BET') and action.group('BET') != '0.00':
                            sb = action.group('BET')
                            self.adjustMergeTourneyStack(hand, player, sb)
                            hand.addBlind(player, 'small blind', sb)
                            if not hand.gametype['sb'] or hand.gametype[
                                    'secondGame']:
                                hand.gametype['sb'] = sb
                        elif action.group('BET') == '0.00':
                            allinBlinds[player] = 'small blind'
                            #log.error(_(_("MergeToFpdb.readBlinds: Cannot calcualte tourney all-in blind for hand '%s'")) % hand.handid)
                            #raise FpdbParseError
                    elif sb and bb is None:
                        if action.group(
                                'BET') and action.group('BET') != '0.00':
                            bb = action.group('BET')
                            self.adjustMergeTourneyStack(hand, player, bb)
                            hand.addBlind(player, 'big blind', bb)
                            if not hand.gametype['bb'] or hand.gametype[
                                    'secondGame']:
                                hand.gametype['bb'] = bb
                        elif action.group('BET') == '0.00':
                            allinBlinds[player] = 'big blind'
                            #log.error(_(_("MergeToFpdb.readBlinds: Cannot calcualte tourney all-in blind for hand '%s'")) % hand.handid)
                            #raise FpdbParseError
            self.fixTourBlinds(hand, allinBlinds)

    def fixTourBlinds(self, hand, allinBlinds):
        # FIXME
        # The following should only trigger when a small blind is missing in a tournament, or the sb/bb is ALL_IN
        # see http://sourceforge.net/apps/mantisbt/fpdb/view.php?id=115
        if hand.gametype['type'] == 'tour' or hand.gametype['secondGame']:
            if hand.gametype['sb'] == None and hand.gametype['bb'] == None:
                hand.gametype['sb'] = "1"
                hand.gametype['bb'] = "2"
            elif hand.gametype['sb'] == None:
                hand.gametype['sb'] = str(
                    int(Decimal(hand.gametype['bb'])) / 2)
            elif hand.gametype['bb'] == None:
                hand.gametype['bb'] = str(
                    int(Decimal(hand.gametype['sb'])) * 2)
            if int(Decimal(hand.gametype['bb'])) / 2 != int(
                    Decimal(hand.gametype['sb'])):
                if int(Decimal(hand.gametype['bb'])) / 2 < int(
                        Decimal(hand.gametype['sb'])):
                    hand.gametype['bb'] = str(
                        int(Decimal(hand.gametype['sb'])) * 2)
                else:
                    hand.gametype['sb'] = str(
                        int(Decimal(hand.gametype['bb'])) / 2)
            hand.sb = hand.gametype['sb']
            hand.bb = hand.gametype['bb']
            for player, blindtype in allinBlinds.iteritems():
                if blindtype == 'big blind':
                    self.adjustMergeTourneyStack(hand, player, hand.bb)
                    hand.addBlind(player, 'big blind', hand.bb)
                else:
                    self.adjustMergeTourneyStack(hand, player, hand.sb)
                    hand.addBlind(player, 'small blind', hand.sb)

    def mergeMultigametypes(self, handText):
        m2 = self.re_HandInfo.search(handText)
        if m2 is None:
            tmp = handText[0:200]
            log.error(_("MergeToFpdb.readHandInfo: '%s'") % tmp)
            raise FpdbParseError
        multigametype = m2.group('MULTIGAMETYPE1') if m2.group(
            'MULTIGAMETYPE1') else m2.group('MULTIGAMETYPE2')
        if multigametype:
            try:
                (self.info['base'],
                 self.info['category']) = self.Multigametypes[multigametype]
            except KeyError:
                tmp = handText[0:200]
                log.error(
                    _("MergeToFpdb.determineGameType: Multigametypes has no lookup for '%s'"
                      ) % multigametype)
                raise FpdbParseError

    def adjustMergeTourneyStack(self, hand, player, amount):
        amount = Decimal(amount)
        if hand.gametype['type'] == 'tour':
            for p in hand.players:
                if p[1] == player:
                    stack = Decimal(p[2])
                    stack += amount
                    p[2] = str(stack)
            hand.stacks[player] += amount

    def readButton(self, hand):
        hand.buttonpos = int(
            self.re_Button.search(hand.handText).group('BUTTON'))

    def readHeroCards(self, hand):
        #    streets PREFLOP, PREDRAW, and THIRD are special cases beacause
        #    we need to grab hero's cards
        herocards = []
        for street in ('PREFLOP', 'DEAL'):
            if street in hand.streets.keys():
                m = self.re_HeroCards.finditer(hand.streets[street])
                for found in m:
                    #                    if m == None:
                    #                        hand.involved = False
                    #                    else:
                    hand.hero = self.playerNameFromSeatNo(
                        found.group('PSEAT'), hand)
                    cards = found.group('CARDS').split(',')
                    hand.addHoleCards(street,
                                      hand.hero,
                                      closed=cards,
                                      shown=False,
                                      mucked=False,
                                      dealt=True)

        for street in hand.holeStreets:
            if hand.streets.has_key(street):
                if not hand.streets[street] or street in (
                        'PREFLOP', 'DEAL') or hand.gametype['base'] == 'hold':
                    continue  # already done these
                m = self.re_HeroCards.finditer(hand.streets[street])
                for found in m:
                    player = self.playerNameFromSeatNo(found.group('PSEAT'),
                                                       hand)
                    if player in hand.stacks:
                        if found.group('CARDS') is None:
                            cards = []
                            newcards = []
                            oldcards = []
                        else:
                            if hand.gametype['base'] == 'stud':
                                cards = found.group('CARDS').replace(
                                    'null', '').split(',')
                                cards = [c for c in cards if c != '']
                                oldcards = cards[:-1]
                                newcards = [cards[-1]]
                            else:
                                cards = found.group('CARDS').split(',')
                                oldcards = cards
                                newcards = []
                        if street == 'THIRD' and len(
                                cards) == 3:  # hero in stud game
                            hand.hero = player
                            herocards = cards
                            hand.dealt.add(hand.hero)  # need this for stud??
                            hand.addHoleCards(street,
                                              player,
                                              closed=oldcards,
                                              open=newcards,
                                              shown=False,
                                              mucked=False,
                                              dealt=False)
                        elif (cards != herocards
                              and hand.gametype['base'] == 'stud'):
                            if hand.hero == player:
                                herocards = cards
                                hand.addHoleCards(street,
                                                  player,
                                                  closed=oldcards,
                                                  open=newcards,
                                                  shown=False,
                                                  mucked=False,
                                                  dealt=False)
                            elif (len(cards) < 5):
                                if street == 'SEVENTH':
                                    oldcards = []
                                    newcards = []
                                hand.addHoleCards(street,
                                                  player,
                                                  closed=oldcards,
                                                  open=newcards,
                                                  shown=False,
                                                  mucked=False,
                                                  dealt=False)
                            elif (len(cards) == 7):
                                for street in hand.holeStreets:
                                    hand.holecards[street][player] = [[], []]
                                hand.addHoleCards(street,
                                                  player,
                                                  closed=cards,
                                                  open=[],
                                                  shown=False,
                                                  mucked=False,
                                                  dealt=False)
                        elif (hand.gametype['base'] == 'draw'):
                            hand.addHoleCards(street,
                                              player,
                                              closed=oldcards,
                                              open=newcards,
                                              shown=False,
                                              mucked=False,
                                              dealt=False)

    def readAction(self, hand, street):
        #log.debug("readAction (%s)" % street)
        m = self.re_Action.finditer(hand.streets[street])
        for action in m:
            player = self.playerNameFromSeatNo(action.group('PSEAT'), hand)
            if player in hand.stacks:
                if action.group('ATYPE') in ('FOLD', 'SIT_OUT'):
                    hand.addFold(street, player)
                elif action.group('ATYPE') == 'CHECK':
                    hand.addCheck(street, player)
                elif action.group('ATYPE') == 'CALL':
                    hand.addCall(street, player, action.group('BET'))
                elif action.group('ATYPE') == 'RAISE':
                    if hand.startTime < hand.newFormat:
                        hand.addCallandRaise(street, player,
                                             action.group('BET'))
                    else:
                        hand.addRaiseTo(street, player, action.group('BET'))
                elif action.group('ATYPE') == 'BET':
                    hand.addBet(street, player, action.group('BET'))
                elif action.group('ATYPE') == 'ALL_IN':
                    hand.addAllIn(street, player, action.group('BET'))
                elif action.group('ATYPE') == 'DRAW':
                    hand.addDiscard(street, player, action.group('TXT'))
                elif action.group('ATYPE') == 'COMPLETE':
                    if hand.gametype['base'] != 'stud':
                        hand.addRaiseTo(street, player, action.group('BET'))
                    else:
                        hand.addComplete(street, player, action.group('BET'))
                else:
                    log.debug(
                        _("Unimplemented %s: '%s' '%s'") %
                        ("readAction", action.group('PSEAT'),
                         action.group('ATYPE')))

    def readShowdownActions(self, hand):
        pass

    def readCollectPot(self, hand):
        hand.setUncalledBets(True)
        for m in self.re_CollectPot.finditer(hand.handText):
            pname = self.playerNameFromSeatNo(m.group('PSEAT'), hand)
            pot = m.group('POT')
            hand.addCollectPot(player=pname, pot=pot)

    def readShownCards(self, hand):
        for m in self.re_ShownCards.finditer(hand.handText):
            if m.group('CARDS') is not None:
                cards = m.group('CARDS')
                cards = m.group('CARDS').split(',')

                (shown, mucked) = (False, False)
                if m.group('SHOWED') == "SHOWN": shown = True
                elif m.group('SHOWED') == "MUCKED": mucked = True

                #print "DEBUG: hand.addShownCards(%s, %s, %s, %s)" %(cards, m.group('PNAME'), shown, mucked)
                hand.addShownCards(cards=cards,
                                   player=self.playerNameFromSeatNo(
                                       m.group('PSEAT'), hand),
                                   shown=shown,
                                   mucked=mucked)

    def determineErrorType(self, hand, function):
        message = False
        m = self.re_Connection.search(hand.handText)
        if m:
            message = _("Found %s. Hand missing information.") % m.group(
                'TYPE')
        m = self.re_LeaveTable.search(hand.handText)
        if m:
            message = _("Found LEAVE. Player left table before hand completed")
        m = self.re_Cancelled.search(hand.handText)
        if m:
            message = _("Found CANCELLED")
        if message == False and function == "markStreets":
            message = _("Failed to identify all streets")
        if message == False and function == "readHandInfo":
            message = _("END_OF_HAND not found. No obvious reason")
        if message:
            raise FpdbHandPartial("Partial hand history: %s '%s' %s" %
                                  (function, hand.handid, message))

    @staticmethod
    def getTableTitleRe(type,
                        table_name=None,
                        tournament=None,
                        table_number=None):
        "Returns string to search in windows titles"
        regex = re.escape(str(table_name))
        if type == "tour":
            # Ignoring table number as it doesn't appear to be in the window title
            # "$200 Freeroll - NL Holdem - 20:00 (46302299) - Table 1" -- the table number doesn't matter, it seems to always be 1 in the HH.
            # "Fun Step 1 (4358174) - Table 1"
            regex = re.escape(str(tournament))
        log.info(
            "Merge.getTableTitleRe: table_name='%s' tournament='%s' table_number='%s'"
            % (table_name, tournament, table_number))
        log.info("Merge.getTableTitleRe: returns: '%s'" % (regex))
        return regex
    def readHandInfo(self, hand):
        info, m = {}, None
        if self.sitename in ('iPoker', 'Merge'):
            m3 = self.re_Tournament.search(hand.handText, re.DOTALL)
            if m3:
                m = self.re_HandInfo_Tour.search(hand.handText, re.DOTALL)
            else:
                m = self.re_HandInfo_Cash.search(hand.handText, re.DOTALL)
            m2 = self.re_GameInfo1.search(hand.handText)
        elif self.sitename == 'Everest':
            m2 = self.re_GameInfo2.search(hand.handText)
        elif self.sitename == 'Microgaming':
            m2 = self.re_GameInfo3.search(hand.handText)
        if (m is None and self.sitename
                not in ('Everest', 'Microgaming')) or m2 is None:
            tmp = hand.handText[0:200]
            print hand.handText
            log.error(_("PokerTrackerToFpdb.readHandInfo: '%s'") % tmp)
            raise FpdbParseError

        if self.sitename not in ('Everest', 'Microgaming'):
            info.update(m.groupdict())
        info.update(m2.groupdict())

        if self.sitename != 'Everest':
            hand.setUncalledBets(True)

        #print 'readHandInfo', info
        for key in info:
            if key == 'DATETIME':
                #2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET] # (both dates are parsed so ET date overrides the other)
                #2008/08/17 - 01:14:43 (ET)
                #2008/09/07 06:23:14 ET
                if self.sitename in ('iPoker', 'Microgaming'):
                    m1 = self.re_DateTime1.finditer(info[key])
                elif self.sitename == 'Merge':
                    m1 = self.re_DateTime2.finditer(info[key])
                elif self.sitename == 'Everest':
                    m1 = self.re_DateTime3.finditer(info[key])
                datetimestr = "2000/01/01 00:00:00"  # default used if time not found
                for a in m1:
                    datetimestr = "%s/%s/%s %s:%s:%s" % (
                        a.group('Y'), a.group('M'), a.group('D'), a.group('H'),
                        a.group('MIN'), a.group('S'))
                    #tz = a.group('TZ')  # just assume ET??
                    #print "   tz = ", tz, " datetime =", datetimestr
                hand.startTime = datetime.datetime.strptime(
                    datetimestr,
                    "%Y/%m/%d %H:%M:%S")  # also timezone at end, e.g. " ET"
                hand.startTime = HandHistoryConverter.changeTimezone(
                    hand.startTime, "ET", "UTC")
            if key == 'HID':
                if self.sitename == 'Merge':
                    hand.handid = info[key][:8] + info[key][9:]
                else:
                    hand.handid = info[key]
            if key == 'TOURNO':
                hand.tourNo = info[key]
            if key == 'BUYIN':
                if hand.tourNo != None:
                    tourneyname = ''
                    if self.sitename == 'Merge':
                        if self.Structures is None:
                            self.Structures = MergeStructures.MergeStructures()
                        tourneyname = re.split(",",
                                               m.group('TABLE'))[0].strip()
                        structure = self.Structures.lookupSnG(
                            tourneyname, hand.startTime)
                        if structure != None:
                            hand.buyin = int(100 * structure['buyIn'])
                            hand.fee = int(100 * structure['fee'])
                            hand.buyinCurrency = structure['currency']
                            hand.maxseats = structure['seats']
                            hand.isSng = True
                        else:
                            #print 'DEBUG', 'no match for tourney %s tourNo %s' % (tourneyname, hand.tourNo)
                            hand.buyin = 0
                            hand.fee = 0
                            hand.buyinCurrency = "NA"
                            hand.maxseats = None
                    if self.sitename != 'Merge' or hand.buyin == 0:
                        if info[key] == 'Freeroll' or 'Free' in tourneyname:
                            hand.buyin = 0
                            hand.fee = 0
                            hand.buyinCurrency = "FREE"
                        else:
                            if info[key].find("$") != -1:
                                hand.buyinCurrency = "USD"
                            elif info[key].find(u"£") != -1:
                                hand.buyinCurrency = "GBP"
                            elif info[key].find(u"€") != -1:
                                hand.buyinCurrency = "EUR"
                            elif re.match("^[0-9+]*$", info[key]):
                                hand.buyinCurrency = "play"
                            else:
                                #FIXME: handle other currencies, play money
                                log.error(
                                    _("PokerTrackerToFpdb.readHandInfo: Failed to detect currency."
                                      ) + " Hand ID: %s: '%s'" %
                                    (hand.handid, info[key]))
                                raise FpdbParseError

                            info['BIAMT'] = info['BIAMT'].strip(u'$€£')
                            info['BIRAKE'] = info['BIRAKE'].strip(u'$€£')

                            hand.buyin = int(100 * Decimal(info['BIAMT']))
                            hand.fee = int(100 * Decimal(info['BIRAKE']))
            if key == 'TABLE':
                if hand.gametype['type'] == 'tour':
                    hand.tablename = '0'
                elif hand.gametype[
                        'type'] == 'tour' and self.sitename == 'Microgaming':
                    hand.tablename = info[key]
                else:
                    hand.tablename = re.split(",", info[key])[0]
                    hand.tablename = hand.tablename.strip()
                if 'Blaze' in hand.tablename:
                    hand.gametype['fast'] = True
                if self.sitename == 'Microgaming':
                    m3 = self.re_Max.search(hand.tablename)
                    if m3 and m3.group('MAX'):
                        if m3.group('MAX') == 'HU':
                            hand.maxseats = 2
                        elif len(m3.group('MAX').split(' ')) == 2:
                            hand.maxseats = int(m3.group('MAX').split(' ')[0])
            if key == 'BUTTON':
                hand.buttonpos = info[key]
            if key == 'MAX' and info[key] != None:
                seats = int(info[key])
                if seats <= 10:
                    hand.maxseats = int(info[key])

            if key == 'PLAY' and info['PLAY'] is not None and info[
                    'PLAY'] == 'Play':
                #                hand.currency = 'play' # overrides previously set value
                hand.gametype['currency'] = 'play'

        if self.re_FastFold.search(hand.handText):
            hand.fastFold = True

        if self.re_Cancelled.search(hand.handText):
            raise FpdbHandPartial(_("Hand '%s' was cancelled.") % hand.handid)
Exemple #3
0
    def parseSummaryFromHH(self, mg):
        obj = getattr(MergeToFpdb, "Merge", None)
        hhc = obj(self.config,
                  in_path=self.in_path,
                  sitename=None,
                  autostart=False)
        update = False
        handsList = hhc.allHandsAsList()
        handsDict = {}
        Structures = MergeStructures.MergeStructures()
        for handText in handsList:
            m = self.re_HandInfoHH.search(handText)
            if m is None:
                tmp = self.summaryText[0:200]
                log.error(_("MergeSummary.readHandInfo: '%s'") % tmp)
                continue
            tourNo = re.split('-', m.group('TDATA'))[0]
            hands = handsDict.get(tourNo)
            if hands is None:
                handsDict[tourNo] = [handText]
            else:
                hands.append(handText)
        for tourNo, hands in handsDict.iteritems():
            self.resetInfo()
            self.db.resetBulkCache()
            m = self.re_GameTypeHH.search(hands[0])
            if m:
                mg = m.groupdict()

            if 'LIMIT' in mg:
                self.gametype['limitType'] = self.limits[mg['LIMIT']]
            if 'GAME' in mg:
                if mg['GAME'] == "HORSE":
                    log.error(
                        _("MergeSummary.determineGameType: HORSE found, unsupported"
                          ))
                    raise FpdbParseError
                    #(self.info['base'], self.info['category']) = self.Multigametypes[m2.group('MULTIGAMETYPE')]
                else:
                    self.gametype['category'] = self.games[mg['GAME']][1]
            if 'SEATS' in mg and mg['SEATS'] is not None:
                self.maxseats = int(mg['SEATS'])

            for handText in hands:
                m = self.re_HandInfoHH.search(handText)
                if m is None:
                    tmp = self.summaryText[0:200]
                    log.error(_("MergeSummary.readHandInfo: '%s'") % tmp)
                    continue
                    #raise FpdbParseError
                #print 'DEBUG:', m.groupdict()

                tourneyNameFull = m.group('TABLENAME').replace('  - ',
                                                               ' - ').strip()
                self.tourneyName = m.group('TABLENAME')[:40]
                self.tourNo = tourNo
                m1 = self.re_DateTimeHH.search(m.group('DATETIME'))
                if m1:
                    mg = m1.groupdict()
                    datetimestr = "%s/%s/%s %s:%s:%s" % (
                        mg['Y'], mg['M'], mg['D'], mg['H'], mg['MIN'], mg['S'])
                    #tz = a.group('TZ')  # just assume ET??
                    self.startTime = datetime.datetime.strptime(
                        datetimestr, "%Y/%m/%d %H:%M:%S"
                    )  # also timezone at end, e.g. " ET"
                else:
                    self.startTime = datetime.datetime.strptime(
                        m.group('DATETIME')[:14], '%Y%m%d%H%M%S')
                self.startTime = HandHistoryConverter.changeTimezone(
                    self.startTime, "ET", "UTC")

                if self.re_turboHH.match(tourneyNameFull):
                    if self.maxseats == 6:
                        tourneyNameFull += ' (6-max)'

                structure = Structures.lookupSnG(tourneyNameFull,
                                                 self.startTime)
                if structure is None:
                    log.error(
                        _("MergeSummary.determineGameType: No match in SnG_Structures"
                          ))
                    continue
                    raise FpdbParseError

                self.buyin = int(100 * structure['buyIn'])
                self.fee = int(100 * structure['fee'])
                if 'max' in structure:
                    self.entries = structure['max']
                else:
                    self.entries = structure['seats']
                self.buyinCurrency = structure['currency']
                self.currency = structure['payoutCurrency']
                self.maxseats = structure['seats']
                if 'speed' in structure:
                    self.speed = structure['speed']
                if 'doubleOrNothing' in structure:
                    self.isDoubleOrNothing = True
                if 'bounty' in structure:
                    self.isKO = True
                    self.koBounty = int(100 * structure['bounty'])

                self.prizepool = sum(structure['payouts'])
                payouts = len(structure['payouts'])
                self.isSng = True

                if structure['multi']:
                    log.error(
                        _("MergeSummary.determineGameType: Muli-table SnG found, unsupported"
                          ))
                    continue

                players, out, won = {}, [], []
                for m in hhc.re_PlayerOut.finditer(handText):
                    if m.group('PSEAT') != None:
                        out.append(m.group('PSEAT'))
                if out:
                    for m in hhc.re_PlayerInfo.finditer(handText):
                        players[m.group('SEAT')] = m.group('PNAME')
                    if not players: continue
                    for m in hhc.re_CollectPot.finditer(handText):
                        won.append(m.group('PSEAT'))

                    if self.isDoubleOrNothing:
                        if handText == hands[-1]:
                            won = [
                                w for w in players.keys()
                                if w not in out or w in won
                            ]
                            out = [p for p in players.keys()]
                    i = 0
                    for n in out:
                        winnings = 0
                        if n in won:
                            rank = 1
                            winnings = int(100 * structure['payouts'][0])
                        else:
                            rank = len(players) - i
                            if rank <= payouts:
                                winnings = int(100 *
                                               structure['payouts'][rank - 1])
                            i += 1
                        self.addPlayer(rank, players[n], winnings,
                                       self.currency, 0, 0, 0)
            self.insertOrUpdate()