From 95aa993903fe075cd89dc43a45c900f0ad19332e Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 15 Jan 2010 13:50:46 +0800 Subject: [PATCH 1/8] [NEWIMPORT] setPositions(), fix aggr function PFR now actually works --- pyfpdb/DerivedStats.py | 105 ++++++++++++++++++++++++++++------------- 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index b65b0d05..bd36f0fb 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -48,6 +48,7 @@ class DerivedStats(): self.handsplayers[player[1]]['sawShowdown'] = False self.handsplayers[player[1]]['wonAtSD'] = 0.0 self.handsplayers[player[1]]['startCards'] = 0 + self.handsplayers[player[1]]['position'] = 2 for i in range(5): self.handsplayers[player[1]]['street%dCalls' % i] = 0 self.handsplayers[player[1]]['street%dBets' % i] = 0 @@ -56,7 +57,6 @@ class DerivedStats(): self.handsplayers[player[1]]['street%dCBDone' %i] = False #FIXME - Everything below this point is incomplete. - self.handsplayers[player[1]]['position'] = 2 self.handsplayers[player[1]]['tourneyTypeId'] = 1 self.handsplayers[player[1]]['street0_3BChance'] = False self.handsplayers[player[1]]['street0_3BDone'] = False @@ -174,31 +174,41 @@ class DerivedStats(): self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card) self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1]) - # position, - #Stud 3rd street card test - # denny501: brings in for $0.02 - # s0rrow: calls $0.02 - # TomSludge: folds - # Soroka69: calls $0.02 - # rdiezchang: calls $0.02 (Seat 8) - # u.pressure: folds (Seat 1) - # 123smoothie: calls $0.02 - # gashpor: calls $0.02 - + self.setPositions(hand) # Additional stats # 3betSB, 3betBB # Squeeze, Ratchet? - def getPosition(hand, seat): - """Returns position value like 'B', 'S', 0, 1, ...""" - # Flop/Draw games with blinds - # Need a better system??? - # -2 BB - B (all) - # -1 SB - S (all) - # 0 Button - # 1 Cutoff - # 2 Hijack + def setPositions(self, hand): + """Sets the position for each player in HandsPlayers + any blinds are negative values, and the last person to act on the + first betting round is 0 + NOTE: HU, both values are negative for non-stud games + NOTE2: I've never seen a HU stud match""" + # The position calculation must be done differently for Stud and other games as + # Stud the 'blind' acts first - in all other games they act last. + # + #This function is going to get it wrong when there in situations where there + # is no small blind. I can live with that. + positions = [7, 6, 5, 4, 3, 2, 1, 0, 'S', 'B'] + actions = hand.actions[hand.holeStreets[0]] + players = self.pfbao(actions) + seats = len(players) + map = [] + if hand.gametype['base'] == 'stud': + # Could posibly change this to be either -2 or -1 depending if they complete or bring-in + # First player to act is -1, last player is 0 for 6 players it should look like: + # ['S', 4, 3, 2, 1, 0] + map = positions[-seats-1:-1] # Copy required positions from postions array anding in -1 + map = map[-1:] + map[0:-1] # and move the -1 to the start of that array + else: + # For 6 players is should look like: + # [3, 2, 1, 0, 'S', 'B'] + map = positions[-seats:] # Copy required positions from array ending in -2 + + for i, player in enumerate(players): + self.handsplayers[player]['position'] = map[i] def assembleHudCache(self, hand): pass @@ -293,11 +303,13 @@ class DerivedStats(): def aggr(self, hand, i): aggrers = set() - for act in hand.actions[hand.actionStreets[i]]: - if act[1] in ('completes', 'raises'): + # Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street + for act in hand.actions[hand.actionStreets[i+1]]: + if act[1] in ('completes', 'bets', 'raises'): aggrers.add(act[0]) for player in hand.players: + #print "DEBUG: actionStreet[%s]: %s" %(hand.actionStreets[i+1], i) if player[1] in aggrers: self.handsplayers[player[1]]['street%sAggr' % i] = True else: @@ -333,6 +345,44 @@ class DerivedStats(): players.add(action[0]) return players + def pfbao(self, actions, f=None, l=None, unique=True): + """Helper method. Returns set of PlayersFilteredByActionsOrdered + + f - forbidden actions + l - limited to actions + """ + # Note, this is an adaptation of function 5 from: + # http://www.peterbe.com/plog/uniqifiers-benchmark + seen = {} + players = [] + for action in actions: + if l is not None and action[1] not in l: continue + if f is not None and action[1] in f: continue + if action[0] in seen and unique: continue + seen[action[0]] = 1 + players.append(action[0]) + return players + + def firstsBetOrRaiser(self, actions): + """Returns player name that placed the first bet or raise. + + None if there were no bets or raises on that street + """ + for act in actions: + if act[1] in ('bets', 'raises'): + return act[0] + return None + + def lastBetOrRaiser(self, street): + """Returns player name that placed the last bet or raise for that street. + None if there were no bets or raises on that street""" + lastbet = None + for act in self.hand.actions[street]: + if act[1] in ('bets', 'raises'): + lastbet = act[0] + return lastbet + + def noBetsBefore(self, street, player): """Returns true if there were no bets before the specified players turn, false otherwise""" betOrRaise = False @@ -355,12 +405,3 @@ class DerivedStats(): break return betOrRaise - - def lastBetOrRaiser(self, street): - """Returns player name that placed the last bet or raise for that street. - None if there were no bets or raises on that street""" - lastbet = None - for act in self.hand.actions[street]: - if act[1] in ('bets', 'raises'): - lastbet = act[0] - return lastbet From 150b1891d63ad0d557add7f6beb891942c197453 Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 15 Jan 2010 13:50:46 +0800 Subject: [PATCH 2/8] [NEWIMPORT] setPositions(), fix aggr function PFR now actually works --- pyfpdb/DerivedStats.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index bd36f0fb..e064f6be 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -55,6 +55,8 @@ class DerivedStats(): for i in range(1,5): self.handsplayers[player[1]]['street%dCBChance' %i] = False self.handsplayers[player[1]]['street%dCBDone' %i] = False + self.handsplayers[player[1]]['street%dCheckCallRaiseChance' %i] = False + self.handsplayers[player[1]]['street%dCheckCallRaiseDone' %i] = False #FIXME - Everything below this point is incomplete. self.handsplayers[player[1]]['tourneyTypeId'] = 1 @@ -72,8 +74,6 @@ class DerivedStats(): self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False self.handsplayers[player[1]]['foldToStreet%dCBChance' %i] = False self.handsplayers[player[1]]['foldToStreet%dCBDone' %i] = False - self.handsplayers[player[1]]['street%dCheckCallRaiseChance' %i] = False - self.handsplayers[player[1]]['street%dCheckCallRaiseDone' %i] = False self.assembleHands(self.hand) self.assembleHandsPlayers(self.hand) @@ -175,6 +175,7 @@ class DerivedStats(): self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1]) self.setPositions(hand) + self.calcCheckCallRaise(hand) # Additional stats # 3betSB, 3betBB # Squeeze, Ratchet? @@ -290,6 +291,28 @@ class DerivedStats(): if chance == True: self.handsplayers[name]['street%dCBDone' % (i+1)] = self.betStreet(hand.actionStreets[i+2], name) + def calcCheckCallRaise(self, hand): + """Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone + + streetXCheckCallRaiseChance = got raise/bet after check + streetXCheckCallRaiseDone = checked. got raise/bet. didn't fold + + CG: CheckCall would be a much better name for this. + """ + for i, street in enumerate(hand.actionStreets[2:], start=1): + actions = hand.actions[hand.actionStreets[i]] + checkers = set() + initial_raiser = None + for action in actions: + pname, act = action[0], action[1] + if act in ('bets', 'raises') and initial_raiser is None: + initial_raiser = pname + elif act == 'checks' and initial_raiser is None: + checkers.add(pname) + elif initial_raiser is not None and pname in checkers: + self.handsplayers[pname]['street%dCheckCallRaiseChance' % i] = True + self.handsplayers[pname]['street%dCheckCallRaiseDone' % i] = act!='folds' + def seen(self, hand, i): pas = set() for act in hand.actions[hand.actionStreets[i+1]]: From 5c0d695055fcfbdbaf64a82e7cde2e62bbe5d065 Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 15 Jan 2010 19:42:24 +0800 Subject: [PATCH 3/8] [NEWIMPORT] Almost all remaining stats All conversions from Grigorij street0_3BChance street0_3BDone street0_4BChance street0_4BDone stealAttemptChance stealAttempted foldBbToStealChance foldBbToStealChance foldSbToStealChance foldedSbToSteal foldedBbToSteal 3Bet, 4Bet in Stud does appear to work. Unable to test steal in Stud games, all example hands in micros do not have a chance (I believe) --- pyfpdb/DerivedStats.py | 80 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 15 deletions(-) diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py index e064f6be..f96b6af7 100644 --- a/pyfpdb/DerivedStats.py +++ b/pyfpdb/DerivedStats.py @@ -49,6 +49,17 @@ class DerivedStats(): self.handsplayers[player[1]]['wonAtSD'] = 0.0 self.handsplayers[player[1]]['startCards'] = 0 self.handsplayers[player[1]]['position'] = 2 + self.handsplayers[player[1]]['street0_3BChance'] = False + self.handsplayers[player[1]]['street0_3BDone'] = False + self.handsplayers[player[1]]['street0_4BChance'] = False + self.handsplayers[player[1]]['street0_4BDone'] = False + self.handsplayers[player[1]]['stealAttemptChance'] = False + self.handsplayers[player[1]]['stealAttempted'] = False + self.handsplayers[player[1]]['foldBbToStealChance'] = False + self.handsplayers[player[1]]['foldBbToStealChance'] = False + self.handsplayers[player[1]]['foldSbToStealChance'] = False + self.handsplayers[player[1]]['foldedSbToSteal'] = False + self.handsplayers[player[1]]['foldedBbToSteal'] = False for i in range(5): self.handsplayers[player[1]]['street%dCalls' % i] = 0 self.handsplayers[player[1]]['street%dBets' % i] = 0 @@ -60,15 +71,6 @@ class DerivedStats(): #FIXME - Everything below this point is incomplete. self.handsplayers[player[1]]['tourneyTypeId'] = 1 - self.handsplayers[player[1]]['street0_3BChance'] = False - self.handsplayers[player[1]]['street0_3BDone'] = False - self.handsplayers[player[1]]['stealAttemptChance'] = False - self.handsplayers[player[1]]['stealAttempted'] = False - self.handsplayers[player[1]]['foldBbToStealChance'] = False - self.handsplayers[player[1]]['foldBbToStealChance'] = False - self.handsplayers[player[1]]['foldSbToStealChance'] = False - self.handsplayers[player[1]]['foldedSbToSteal'] = False - self.handsplayers[player[1]]['foldedBbToSteal'] = False for i in range(1,5): self.handsplayers[player[1]]['otherRaisedStreet%d' %i] = False self.handsplayers[player[1]]['foldToOtherRaisedStreet%d' %i] = False @@ -176,6 +178,8 @@ class DerivedStats(): self.setPositions(hand) self.calcCheckCallRaise(hand) + self.calc34BetStreet0(hand) + self.calcSteals(hand) # Additional stats # 3betSB, 3betBB # Squeeze, Ratchet? @@ -273,13 +277,59 @@ class DerivedStats(): for (i, street) in enumerate(hand.actionStreets[1:]): self.hands['street%dRaises' % i] = len(filter( lambda action: action[1] in ('raises','bets'), hand.actions[street])) - def calcCBets(self, hand): - # Continuation Bet chance, action: - # Had the last bet (initiative) on previous street, got called, close street action - # Then no bets before the player with initiatives first action on current street - # ie. if player on street-1 had initiative - # and no donkbets occurred + def calcSteals(self, hand): + """Fills stealAttempt(Chance|ed, fold(Bb|Sb)ToSteal(Chance|) + Steal attemp - open raise on positions 2 1 0 S - i.e. MP3, CO, BU, SB + Fold to steal - folding blind after steal attemp wo any other callers or raisers + """ + steal_attemp = False + steal_positions = ('2', '1', '0', 'S') + if hand.gametype['base'] == 'stud': + steal_positions = ('2', '1', '0') + for action in hand.actions[hand.actionStreets[1]]: + pname, act = action[0], action[1] + #print action[0], hp.position, steal_attemp, act + if self.handsplayers[pname]['position'] == 'B': + #NOTE: Stud games will never hit this section + self.handsplayers[pname]['foldBbToStealChance'] = steal_attemp + self.handsplayers[pname]['foldBbToSteal'] = self.handsplayers[pname]['foldBbToStealChance'] and act == 'folds' + break + elif self.handsplayers[pname]['position'] == 'S': + self.handsplayers[pname]['foldSbToStealChance'] = steal_attemp + self.handsplayers[pname]['foldSbToSteal'] = self.handsplayers[pname]['foldSbToStealChance'] and act == 'folds' + + if steal_attemp and act != 'folds': + break + + if self.handsplayers[pname]['position'] in steal_positions and not steal_attemp: + self.handsplayers[pname]['stealAttemptChance'] = True + if act in ('bets', 'raises'): + self.handsplayers[pname]['stealAttempted'] = True + steal_attemp = True + + def calc34BetStreet0(self, hand): + """Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0""" + bet_level = 1 # bet_level after 3-bet is equal to 3 + for action in hand.actions[hand.actionStreets[1]]: + # FIXME: fill other(3|4)BStreet0 - i have no idea what does it mean + pname, aggr = action[0], action[1] in ('raises', 'bets') + self.handsplayers[pname]['street0_3BChance'] = bet_level == 2 + self.handsplayers[pname]['street0_4BChance'] = bet_level == 3 + self.handsplayers[pname]['street0_3BDone'] = aggr and (self.handsplayers[pname]['street0_3BChance']) + self.handsplayers[pname]['street0_4BDone'] = aggr and (self.handsplayers[pname]['street0_4BChance']) + if aggr: + bet_level += 1 + + + def calcCBets(self, hand): + """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance + + Continuation Bet chance, action: + Had the last bet (initiative) on previous street, got called, close street action + Then no bets before the player with initiatives first action on current street + ie. if player on street-1 had initiative and no donkbets occurred + """ # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start' # came there #for i, street in enumerate(hand.actionStreets[2:], start=1): From 41877097c999079b4c58221df13d301cfcee04a5 Mon Sep 17 00:00:00 2001 From: sqlcoder Date: Tue, 12 Jan 2010 22:10:59 +0000 Subject: [PATCH 4/8] fix twostartcards to handle 22 and unknowns properly --- pyfpdb/Card.py | 35 ++++++++++++++++++++++++----------- pyfpdb/GuiPlayerStats.py | 4 ++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/pyfpdb/Card.py b/pyfpdb/Card.py index 8639fd35..22eab979 100755 --- a/pyfpdb/Card.py +++ b/pyfpdb/Card.py @@ -39,23 +39,36 @@ def calcStartCards(hand, player): def twoStartCards(value1, suit1, value2, suit2): """ Function to convert 2 value,suit pairs into a Holdem style starting hand e.g. AQo - Hand is stored as an int 13 * x + y where (x+2) represents rank of 1st card and + Incoming values should be ints 2-14 (2,3,...K,A), suits are 'd'/'h'/'c'/'s' + Hand is stored as an int 13 * x + y + 1 where (x+2) represents rank of 1st card and (y+2) represents rank of second card (2=2 .. 14=Ace) - If x > y then pair is suited, if x < y then unsuited""" - if value1 < 2 or value2 < 2: + If x > y then pair is suited, if x < y then unsuited + Examples: + 0 Unknown / Illegal cards + 1 22 + 2 32o + 3 42o + ... + 14 32s + 15 33 + 16 42o + ... + 170 AA + """ + if value1 is None or value1 < 2 or value1 > 14 or value2 is None or value2 < 2 or value2 > 14: ret = 0 - if value1 == value2: # pairs - ret = (13 * (value2-2) + (value2-2) ) + elif value1 == value2: # pairs + ret = (13 * (value2-2) + (value2-2) ) + 1 elif suit1 == suit2: if value1 > value2: - ret = 13 * (value1-2) + (value2-2) + ret = 13 * (value1-2) + (value2-2) + 1 else: - ret = 13 * (value2-2) + (value1-2) + ret = 13 * (value2-2) + (value1-2) + 1 else: if value1 > value2: - ret = 13 * (value2-2) + (value1-2) + ret = 13 * (value2-2) + (value1-2) + 1 else: - ret = 13 * (value1-2) + (value2-2) + ret = 13 * (value1-2) + (value2-2) + 1 # print "twoStartCards(", value1, suit1, value2, suit2, ")=", ret return ret @@ -66,8 +79,8 @@ def twoStartCardString(card): ret = 'xx' if card > 0: s = ('2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') - x = card / 13 - y = card - 13 * x + x = (card-1) / 13 + y = (card-1) - 13 * x if x == y: ret = s[x] + s[y] elif x > y: ret = s[x] + s[y] + 's' else: ret = s[y] + s[x] + 'o' diff --git a/pyfpdb/GuiPlayerStats.py b/pyfpdb/GuiPlayerStats.py index d65b9ab6..889988c5 100644 --- a/pyfpdb/GuiPlayerStats.py +++ b/pyfpdb/GuiPlayerStats.py @@ -516,8 +516,8 @@ class GuiPlayerStats (threading.Thread): if holecards: # re-use level variables for hole card query query = query.replace("", "hp.startcards") query = query.replace("" - , ",case when floor(hp.startcards/13) >= mod(hp.startcards,13) then hp.startcards + 0.1 " - + " else 13*mod(hp.startcards,13) + floor(hp.startcards/13) " + , ",case when floor((hp.startcards-1)/13) >= mod((hp.startcards-1),13) then hp.startcards + 0.1 " + + " else 13*mod((hp.startcards-1),13) + floor((hp.startcards-1)/13) + 1 " + " end desc ") else: query = query.replace("", "") From cd7177897562379fe799ac7238fb0e690bf4ccc4 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Tue, 19 Jan 2010 19:25:36 +0200 Subject: [PATCH 5/8] Fix name display in HUD popup The names are stored in UTF-8, so simply converting the name from UTF-8 to Configuration.LOCALE_ENCODING before putting the string in tooltip is enough. Neat. --- pyfpdb/Stats.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Stats.py b/pyfpdb/Stats.py index 86caaea5..18913eed 100755 --- a/pyfpdb/Stats.py +++ b/pyfpdb/Stats.py @@ -62,9 +62,13 @@ import Database re_Places = re.compile("_[0-9]$") re_Percent = re.compile("%$") +# String manipulation +import codecs +encoder = codecs.lookup(Configuration.LOCALE_ENCODING) def do_tip(widget, tip): - widget.set_tooltip_text(tip) + (_tip, _len) = encoder.encode(tip) + widget.set_tooltip_text(_tip) def do_stat(stat_dict, player = 24, stat = 'vpip'): match = re_Places.search(stat) From ef4f5289bb8ea8b8ce7fa301d2146d091b31b7e7 Mon Sep 17 00:00:00 2001 From: Gerko de Roo Date: Sun, 17 Jan 2010 19:54:40 +0100 Subject: [PATCH 6/8] Solved some merge issues --> Cards.py Issues Pokerstars when playing heads-up on ring games, being both on button and small blind now supported !!if not solved the winnings of the (button, small blind) is stored as rake!! Post both small and big blind when re-entering ring games solved --- pyfpdb/Card.py | 2 +- pyfpdb/PokerStarsToFpdb.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pyfpdb/Card.py b/pyfpdb/Card.py index 22eab979..f33f08ed 100755 --- a/pyfpdb/Card.py +++ b/pyfpdb/Card.py @@ -61,7 +61,7 @@ def twoStartCards(value1, suit1, value2, suit2): ret = (13 * (value2-2) + (value2-2) ) + 1 elif suit1 == suit2: if value1 > value2: - ret = 13 * (value1-2) + (value2-2) + 1 + ret = 13 * (value1-2) + (value2-2) + 1 else: ret = 13 * (value2-2) + (value1-2) + 1 else: diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 5e2f9c21..09bd0390 100755 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -81,6 +81,7 @@ class PokerStars(HandHistoryConverter): re_Board = re.compile(r"\[(?P.+)\]") # self.re_setHandInfoRegex('.*#(?P[0-9]+): Table (?P[ a-zA-Z]+) - \$?(?P[.0-9]+)/\$?(?P[.0-9]+) - (?P.*) - (?P
[0-9]+):(?P[0-9]+) ET - (?P[0-9]+)/(?P[0-9]+)/(?P[0-9]+)Table (?P
[ a-zA-Z]+)\nSeat (?P