From 3dbb0e83760dbbed2519c77ec2d2def3bf823968 Mon Sep 17 00:00:00 2001 From: Matt Turnbull Date: Sun, 14 Dec 2008 22:05:51 +0000 Subject: [PATCH] Everleaf uncalled bets; pot total; rake --- pyfpdb/EverleafToFpdb.py | 25 +++++------- pyfpdb/Hand.py | 74 ++++++++++++++++++++++++++++++---- pyfpdb/HandHistoryConverter.py | 9 +++-- 3 files changed, 83 insertions(+), 25 deletions(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index ce7bddd2..188e4ade 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -75,6 +75,7 @@ class Everleaf(HandHistoryConverter): self.rexx.setPlayerInfoRegex('Seat (?P[0-9]+): (?P.*) \(\s+(\$ (?P[.0-9]+) USD|new player|All-in) \)') self.rexx.setPostSbRegex('.*\n(?P.*): posts small blind \[\$? (?P[.0-9]+)') self.rexx.setPostBbRegex('.*\n(?P.*): posts big blind \[\$? (?P[.0-9]+)') + self.rexx.setPostBothRegex('.*\n(?P.*): posts small \& big blinds \[\$? (?P[.0-9]+)') # mct : what about posting small & big blinds simultaneously? self.rexx.setHeroCardsRegex('.*\nDealt\sto\s(?P.*)\s\[ (?P\S\S), (?P\S\S) \]') self.rexx.setActionStepRegex('.*\n(?P.*)(?P: bets| checks| raises| calls| folds)(\s\[\$ (?P[.\d]+) USD\])?') @@ -131,7 +132,8 @@ class Everleaf(HandHistoryConverter): r"(\*\* Dealing Turn \*\* \[ \S\S \](?P.+(?=\*\* Dealing River \*\*)|.+))?" r"(\*\* Dealing River \*\* \[ \S\S \](?P.+))?", hand.string,re.DOTALL) - hand.streets = m + hand.addStreets(m) + def readCommunityCards(self, hand): # currently regex in wrong place pls fix my brain's fried @@ -146,15 +148,13 @@ class Everleaf(HandHistoryConverter): def readBlinds(self, hand): try: m = self.rexx.small_blind_re.search(hand.string) - hand.addBlind(m.group('PNAME'), m.group('SB')) - #hand.posted = [m.group('PNAME')] - except: - hand.addBlind(None, 0) - #hand.posted = ["FpdbNBP"] - m = self.rexx.big_blind_re.finditer(hand.string) - for a in m: - hand.addBlind(a.group('PNAME'), a.group('BB')) - #hand.posted = hand.posted + [a.group('PNAME')] + hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB')) + except: # no small blind + hand.addBlind(None, None, None) + for a in self.rexx.big_blind_re.finditer(hand.string): + hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB')) + for a in self.rexx.both_blinds_re.finditer(hand.string): + hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB')) def readHeroCards(self, hand): m = self.rexx.hero_cards_re.search(hand.string) @@ -167,7 +167,6 @@ class Everleaf(HandHistoryConverter): def readAction(self, hand, street): m = self.rexx.action_re.finditer(hand.streets.group(street)) - hand.actions[street] = [] for action in m: if action.group('ATYPE') == ' raises': hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) @@ -182,6 +181,7 @@ class Everleaf(HandHistoryConverter): else: print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),) #hand.actions[street] += [[action.group('PNAME'), action.group('ATYPE')]] + # TODO: Everleaf does not record uncalled bets. def readShowdownActions(self, hand): @@ -201,9 +201,6 @@ class Everleaf(HandHistoryConverter): hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) - def getRake(self, hand): - hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right - if __name__ == "__main__": c = Configuration.Config() e = Everleaf(c, "regression-test-files/everleaf/Speed_Kuala_full.txt") diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 5f5d0fd3..809c1be7 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -88,6 +88,7 @@ class Hand: self.action = [] self.totalpot = None self.totalcollected = None + self.rake = None self.bets = {} @@ -110,6 +111,17 @@ If a player has None chips he won't be added.""" self.bets[street][name] = [] + def addStreets(self, match): + # go through m and initialise actions to empty list for each street. + if match is not None: + self.streets = match + for street in match.groupdict(): + if match.group(street) is not None: + self.actions[street] = [] + + else: + print "empty markStreets match" # better to raise exception and put process hand in a try block + def addHoleCards(self, cards, player): """\ Assigns observed holecards to a player. @@ -162,12 +174,18 @@ For when a player shows cards for any reason (for showdown or out of choice). c = c.replace(k,v) return c - def addBlind(self, player, amount): + def addBlind(self, player, blindtype, amount): # if player is None, it's a missing small blind. if player is not None: self.bets['PREFLOP'][player].append(Decimal(amount)) - self.lastBet['PREFLOP'] = Decimal(amount) + self.actions['PREFLOP'] += [(player, 'posts', blindtype, amount)] + if blindtype == 'big blind': + self.lastBet['PREFLOP'] = Decimal(amount) + elif blindtype == 'small & big blinds': + # extra small blind is 'dead' + self.lastBet['PREFLOP'] = Decimal(self.bb) self.posted += [player] + def addCall(self, street, player=None, amount=None): @@ -176,7 +194,7 @@ For when a player shows cards for any reason (for showdown or out of choice). if amount is not None: self.bets[street][player].append(Decimal(amount)) #self.lastBet[street] = Decimal(amount) - self.actions[street] += [[player, 'calls', amount]] + self.actions[street] += [(player, 'calls', amount)] def addRaiseTo(self, street, player, amountTo): """\ @@ -193,21 +211,22 @@ Add a raise on [street] by [player] to [amountTo] self.lastBet[street] = Decimal(amountTo) amountBy = Decimal(amountTo) - amountToCall self.bets[street][player].append(amountBy+amountToCall) - self.actions[street] += [[player, 'raises', amountBy, amountTo]] + self.actions[street] += [(player, 'raises', amountBy, amountTo, amountToCall)] def addBet(self, street, player, amount): self.checkPlayerExists(player) self.bets[street][player].append(Decimal(amount)) - self.actions[street] += [[player, 'bets', amount]] + self.actions[street] += [(player, 'bets', amount)] + self.lastBet[street] = Decimal(amount) def addFold(self, street, player): self.checkPlayerExists(player) self.folded.add(player) - self.actions[street] += [[player, 'folds']] + self.actions[street] += [(player, 'folds')] def addCheck(self, street, player): self.checkPlayerExists(player) - self.actions[street] += [[player, 'checks']] + self.actions[street] += [(player, 'checks')] def addCollectPot(self,player, pot): self.checkPlayerExists(player) @@ -231,12 +250,50 @@ Known bug: doesn't take into account side pots""" for street in self.streetList: #print street, self.bets[street][player] self.totalpot += reduce(operator.add, self.bets[street][player], 0) + print "conventional totalpot:", self.totalpot + self.totalpot = 0 + + print self.actions + for street in self.actions: + uncalled = 0 + calls = [0] + for act in self.actions[street]: + if act[1] == 'bets': # [name, 'bets', amount] + self.totalpot += Decimal(act[2]) + uncalled = Decimal(act[2]) # only the last bet or raise can be uncalled + calls = [0] + print "uncalled: ", uncalled + elif act[1] == 'raises': # [name, 'raises', amountby, amountto, amountcalled] + print "calls %s and raises %s to %s" % (act[4],act[2],act[3]) + self.totalpot += Decimal(act[2]) + Decimal(act[4]) + calls = [0] + uncalled = Decimal(act[2]) + print "uncalled: ", uncalled + elif act[1] == 'calls': # [name, 'calls', amount] + self.totalpot += Decimal(act[2]) + calls = calls + [Decimal(act[2])] + print "calls:", calls + if act[1] == ('posts'): + self.totalpot += Decimal(act[3]) + uncalled = Decimal(act[3]) + if uncalled > 0 and max(calls+[0]) < uncalled: + + print "returning some bet, calls:", calls + print "returned: %.2f from %.2f" % ((uncalled - max(calls)), self.totalpot,) + self.totalpot -= (uncalled - max(calls)) + print "new totalpot:", self.totalpot if self.totalcollected is None: self.totalcollected = 0; for amount in self.collected.values(): self.totalcollected += Decimal(amount) + # TODO: Some sites (Everleaf) don't record uncalled bets. Figure out if a bet is uncalled and subtract it from self.totalcollected. + # remember that portions of bets may be uncalled, so: + # bet followed by no call is an uncalled bet + # bet x followed by call y where y < x has x-y uncalled (and second player all in) + + def getGameTypeAsString(self): """\ @@ -316,7 +373,8 @@ Map the tuple self.gametype onto the pokerstars string describing it print "what do they show" print "*** SUMMARY ***" - print "Total pot $%s | Rake $%.2f" % (self.totalcollected, self.rake) # TODO side pots + print "Total pot $%s | Rake $%.2f" % (self.totalcollected, self.rake) # TODO: side pots + board = [] for s in self.board.values(): board += s diff --git a/pyfpdb/HandHistoryConverter.py b/pyfpdb/HandHistoryConverter.py index a00df74d..68d00e96 100644 --- a/pyfpdb/HandHistoryConverter.py +++ b/pyfpdb/HandHistoryConverter.py @@ -161,7 +161,8 @@ class HandHistoryConverter: def readPlayerStacks(self, hand): abstract # Needs to return a MatchObject with group names identifying the streets into the Hand object - # that is, pulls the chunks of preflop, flop, turn and river text into hand.streets MatchObject. + # so groups are called by street names 'PREFLOP', 'FLOP', 'STREET2' etc + # blinds are done seperately def markStreets(self, hand): abstract #Needs to return a list in the format @@ -173,8 +174,10 @@ class HandHistoryConverter: def readCollectPot(self, hand): abstract # Some sites don't report the rake. This will be called at the end of the hand after the pot total has been calculated - # so that an inheriting class can calculate it for the specific site if need be. - def getRake(self, hand): abstract + # an inheriting class can calculate it for the specific site if need be. + def getRake(self, hand): + hand.rake = hand.totalpot - hand.totalcollected # * Decimal('0.05') # probably not quite right + def sanityCheck(self): sane = False