From 04b43e3b955c13621544f26768f73c9874236a2d Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 19:48:34 +0900 Subject: [PATCH 1/7] Add preliminary Badugi support Done to flesh out what DrawHand will look like --- pyfpdb/Hand.py | 199 ++++++++++++++++++++++++++++++++++--- pyfpdb/PokerStarsToFpdb.py | 86 ++++++++-------- 2 files changed, 226 insertions(+), 59 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index fc97d615..49517ab7 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -58,9 +58,9 @@ class Hand: self.actions[street] = [] self.board[street] = [] - # Collections indexed by player names self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards + self.discards = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards self.stacks = {} self.collected = [] #list of ? self.collectees = {} # dict from player names to amounts collected (?) @@ -89,10 +89,12 @@ If a player has None chips he won't be added.""" self.players.append([seat, name, chips]) self.stacks[name] = Decimal(chips) self.holecards[name] = [] + self.discards[name] = [] self.pot.addPlayer(name) for street in self.streetList: self.bets[street][name] = [] self.holecards[name] = {} # dict from street names. + self.discards[name] = {} # dict from street names. def addStreets(self, match): @@ -137,8 +139,8 @@ If a player has None chips he won't be added.""" # - this is a bet of 1 sb, as yet uncalled. # Player in the big blind posts # - this is a call of 1 sb and a raise to 1 bb - # - + # + logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) if player is not None: self.bets['PREFLOP'][player].append(Decimal(amount)) @@ -148,7 +150,7 @@ If a player has None chips he won't be added.""" self.actions['BLINDSANTES'].append(act) self.pot.addMoney(player, Decimal(amount)) if blindtype == 'big blind': - self.lastBet['PREFLOP'] = Decimal(amount) + self.lastBet['PREFLOP'] = Decimal(amount) elif blindtype == 'both': # extra small blind is 'dead' self.lastBet['PREFLOP'] = Decimal(self.bb) @@ -245,6 +247,20 @@ Add a raise on [street] by [player] to [amountTo] self.actions[street].append(act) self.lastBet[street] = Decimal(amount) self.pot.addMoney(player, Decimal(amount)) + + def addDiscard(self, street, player, num, cards): + self.checkPlayerExists(player) + if cards: + act = (player, 'discards', num, cards) + self.discardDrawHoleCards(cards, player, street) + else: + act = (player, 'discards', num) + self.actions[street].append(act) + + def addStandsPat(self, street, player): + self.checkPlayerExists(player) + act = (player, 'stands pat') + self.actions[street].append(act) def addFold(self, street, player): @@ -303,7 +319,7 @@ Map the tuple self.gametype onto the pokerstars string describing it "fivedraw" : "5 Card Draw", "27_1draw" : "FIXME", "27_3draw" : "Triple Draw 2-7 Lowball", - "badugi" : "FIXME" + "badugi" : "Badugi" } ls = {"nl" : "No Limit", "pl" : "Pot Limit", @@ -346,12 +362,18 @@ Map the tuple self.gametype onto the pokerstars string describing it print >>fh, _("%s: posts small & big blinds $%s" %(act[0], act[3])) elif act[1] == 'bringin': print >>fh, _("%s: brings in for $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else '')) + elif act[1] == 'discards': + print >>fh, _("%s: discards %s %s%s" %(act[0], act[2], 'card' if act[2] == 1 else 'cards' , " [" + " ".join(self.discards[act[0]]['DRAWONE']) + "]" if self.hero == act[0] else '')) + elif act[1] == 'stands pat': + print >>fh, _("%s: stands pat" %(act[0])) + + class HoldemOmahaHand(Hand): def __init__(self, hhc, sitename, gametype, handText): if gametype['base'] != 'hold': pass # or indeed don't pass and complain instead logging.debug("HoldemOmahaHand") - self.streetList = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order + self.streetList = ['BLINDSANTES', 'DEAL', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order self.communityStreets = ['FLOP', 'TURN', 'RIVER'] self.actionStreets = ['PREFLOP','FLOP','TURN','RIVER'] Hand.__init__(self, sitename, gametype, handText) @@ -508,16 +530,167 @@ class DrawHand(Hand): def __init__(self, hhc, sitename, gametype, handText): if gametype['base'] != 'draw': pass # or indeed don't pass and complain instead - - def discardHoleCards(self, cards, player): + self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] + self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] + self.actionStreets = ['PREDEAL', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] + Hand.__init__(self, sitename, gametype, handText) + self.sb = gametype['sb'] + self.bb = gametype['bb'] + # Populate the draw hand. + hhc.readHandInfo(self) + hhc.readPlayerStacks(self) + hhc.compilePlayerRegexs(self) + hhc.markStreets(self) + hhc.readBlinds(self) + hhc.readButton(self) + hhc.readShowdownActions(self) + # Read actions in street order + for street in self.streetList: + if self.streets[street]: + # hhc.readCommunityCards(self, street) + hhc.readDrawCards(self, street) + hhc.readAction(self, street) + hhc.readCollectPot(self) + hhc.readShownCards(self) + self.totalPot() # finalise it (total the pot) + hhc.getRake(self) + + + # Draw games (at least Badugi has blinds - override default Holdem addBlind + def addBlind(self, player, blindtype, amount): + # if player is None, it's a missing small blind. + # The situation we need to cover are: + # Player in small blind posts + # - this is a bet of 1 sb, as yet uncalled. + # Player in the big blind posts + # - this is a call of 1 sb and a raise to 1 bb + # + + logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) + if player is not None: + self.bets['DEAL'][player].append(Decimal(amount)) + self.stacks[player] -= Decimal(amount) + #print "DEBUG %s posts, stack %s" % (player, self.stacks[player]) + act = (player, 'posts', blindtype, amount, self.stacks[player]==0) + self.actions['BLINDSANTES'].append(act) + self.pot.addMoney(player, Decimal(amount)) + if blindtype == 'big blind': + self.lastBet['DEAL'] = Decimal(amount) + elif blindtype == 'both': + # extra small blind is 'dead' + self.lastBet['DEAL'] = Decimal(self.bb) + self.posted = self.posted + [[player,blindtype]] + #print "DEBUG: self.posted: %s" %(self.posted) + + + + def addDrawHoleCards(self, newcards, oldcards, player, street, shown=False): + """\ +Assigns observed holecards to a player. +cards list of card bigrams e.g. ['2h','Jc'] +player (string) name of player +""" try: self.checkPlayerExists(player) - for card in cards: - self.holecards[player].remove(card) +# if shown and len(cardset) > 0: +# self.shown.add(player) + self.holecards[player][street] = (newcards,oldcards) except FpdbParseError, e: - pass - except ValueError: - print "[ERROR] discardHoleCard tried to discard a card %s didn't have" % (player,) + print "[ERROR] Tried to add holecards for unknown player: %s" % (player,) + + def discardDrawHoleCards(self, cards, player, street): + logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street)) + self.discards[player][street] = set([cards]) + + def addShownCards(self, cards, player, holeandboard=None): + """\ +For when a player shows cards for any reason (for showdown or out of choice). +Card ranks will be uppercased +""" + logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard)) +# if cards is not None: +# self.shown.add(player) +# self.addHoleCards(cards,player) +# elif holeandboard is not None: +# holeandboard = set([self.card(c) for c in holeandboard]) +# board = set([c for s in self.board.values() for c in s]) +# self.addHoleCards(holeandboard.difference(board),player,shown=True) + + def writeHand(self, fh=sys.__stdout__): + # PokerStars format. + print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime))) + print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)) + + players_who_act_ondeal = set(([x[0] for x in self.actions['DEAL']]+[x[0] for x in self.actions['BLINDSANTES']])) + + for player in [x for x in self.players if x[1] in players_who_act_ondeal]: + #Only print stacks of players who do something on deal + print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2])) + + if 'BLINDSANTES' in self.actions: + for act in self.actions['BLINDSANTES']: + print >>fh, _("%s: %s %s $%s" %(act[0], act[1], act[2], act[3])) + + if 'DEAL' in self.actions: + print >>fh, _("*** DEALING HANDS ***") + for player in [x[1] for x in self.players if x[1] in players_who_act_ondeal]: + if 'DEAL' in self.holecards[player]: + (nc,oc) = self.holecards[player]['DEAL'] + print >>fh, _("Dealt to %s: [%s]") % (player, " ".join(nc)) + for act in self.actions['DEAL']: + self.printActionLine(act, fh) + + if 'DRAWONE' in self.actions: + print >>fh, _("*** FIRST DRAW ***") + for act in self.actions['DRAWONE']: + self.printActionLine(act, fh) + if act[0] == self.hero and act[1] == 'discards': + (nc,oc) = self.holecards[act[0]]['DRAWONE'] + dc = self.discards[act[0]]['DRAWONE'] + kc = oc - dc + print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc))) + + if 'DRAWTWO' in self.actions: + print >>fh, _("*** SECOND DRAW ***") + for act in self.actions['DRAWTWO']: + self.printActionLine(act, fh) + if act[0] == self.hero and act[1] == 'discards': + (nc,oc) = self.holecards[act[0]]['DRAWTWO'] + dc = self.discards[act[0]]['DRAWTWO'] + kc = oc - dc + print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc))) + + if 'DRAWTHREE' in self.actions: + print >>fh, _("*** THIRD DRAW ***") + for act in self.actions['DRAWTHREE']: + self.printActionLine(act, fh) + if act[0] == self.hero and act[1] == 'discards': + (nc,oc) = self.holecards[act[0]]['DRAWTHREE'] + dc = self.discards[act[0]]['DRAWTHREE'] + kc = oc - dc + print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc))) + + if 'SHOWDOWN' in self.actions: + print >>fh, _("*** SHOW DOWN ***") + #TODO: Complete SHOWDOWN + + # Current PS format has the lines: + # Uncalled bet ($111.25) returned to s0rrow + # s0rrow collected $5.15 from side pot + # stervels: shows [Ks Qs] (two pair, Kings and Queens) + # stervels collected $45.35 from main pot + # Immediately before the summary. + # The current importer uses those lines for importing winning rather than the summary + for name in self.pot.returned: + print >>fh, _("Uncalled bet ($%s) returned to %s" %(self.pot.returned[name],name)) + for entry in self.collected: + print >>fh, _("%s collected $%s from x pot" %(entry[0], entry[1])) + + print >>fh, _("*** SUMMARY ***") + print >>fh, "%s | Rake $%.2f" % (self.pot, self.rake) + print >>fh, "\n\n" + + class StudHand(Hand): def __init__(self, hhc, sitename, gametype, handText): diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 8c2e4884..3d5ff83d 100755 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -23,49 +23,6 @@ from HandHistoryConverter import * # PokerStars HH Format -#PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET -#Table 'Gianfar IV' 6-max Seat #1 is the button -#Seat 1: ZeKGB ($224 in chips) -#Seat 2: quimboavida ($107.75 in chips) -#Seat 3: tropical100 ($190 in chips) -#Seat 4: jackhama33 ($54.95 in chips) -#Seat 5: Olubanu ($196 in chips) -#Seat 6: LSgambler ($205.35 in chips) -#quimboavida: posts small blind $1 -#tropical100: posts big blind $2 -#*** HOLE CARDS *** -#jackhama33: folds -#Olubanu: folds -#LSgambler: folds -#ZeKGB: folds -#quimboavida: calls $1 -#tropical100: raises $5 to $7 -#quimboavida: calls $5 -#*** FLOP *** [3d Qs Kd] -#quimboavida: bets $10 -#tropical100: calls $10 -#*** TURN *** [3d Qs Kd] [Ah] -#quimboavida: checks -#tropical100: checks -#*** RIVER *** [3d Qs Kd Ah] [7c] -#quimboavida: bets $30 -#tropical100: folds -#quimboavida collected $32.35 from pot -#*** SUMMARY *** -#Total pot $34 | Rake $1.65 -#Board [3d Qs Kd Ah 7c] -#Seat 1: ZeKGB (button) folded before Flop (didn't bet) -#Seat 2: quimboavida (small blind) collected ($32.35) -#Seat 3: tropical100 (big blind) folded on the River -#Seat 4: jackhama33 folded before Flop (didn't bet) -#Seat 5: Olubanu folded before Flop (didn't bet) -#Seat 6: LSgambler folded before Flop (didn't bet) - - -#PokerStars Game #25381215423: HORSE (Razz Limit, $0.10/$0.20) - 2009/02/26 15:20:19 ET -#Table 'Natalie V' 8-max - - class PokerStars(HandHistoryConverter): # Static regexes @@ -103,7 +60,7 @@ follow : whether to tail -f the input""" self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P[.0-9]+)" % player_re, re.MULTILINE) self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P[.0-9]+)" % player_re, re.MULTILINE) self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P.+?)\])?( \[(?P.+?)\])" % player_re, re.MULTILINE) - self.re_Action = re.compile(r"^%s:(?P bets| checks| raises| calls| folds)( \$(?P[.\d]+))?( to \$(?P[.\d]+))?" % player_re, re.MULTILINE) + self.re_Action = re.compile(r"^%s:(?P bets| checks| raises| calls| folds| discards| stands pat)( \$(?P[.\d]+))?( to \$(?P[.\d]+))?( (?P\d) cards?( \[(?P.+?)\])?)?" % player_re, re.MULTILINE) self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P.*)\]" % player_re, re.MULTILINE) self.re_CollectPot = re.compile(r"Seat (?P[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P[.\d]+)\)(, mucked| with.*|)" % player_re, re.MULTILINE) self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE) @@ -111,7 +68,13 @@ follow : whether to tail -f the input""" def readSupportedGames(self): - return [] + return [["ring", "hold", "nl"], + ["ring", "hold", "pl"], + ["ring", "hold", "fl"], + ["ring", "stud", "fl"], + ["ring", "draw", "fl"], + ["ring", "omaha", "pl"] + ] def determineGameType(self, handText): info = {'type':'ring'} @@ -200,6 +163,12 @@ follow : whether to tail -f the input""" r"(\*\*\* 5th STREET \*\*\*(?P.+(?=\*\*\* 6th STREET \*\*\*)|.+))?" r"(\*\*\* 6th STREET \*\*\*(?P.+(?=\*\*\* RIVER \*\*\*)|.+))?" r"(\*\*\* RIVER \*\*\*(?P.+))?", hand.handText,re.DOTALL) + elif hand.gametype['base'] in ("draw"): + m = re.search(r"(?P.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)" + r"(\*\*\* DEALING HANDS \*\*\*(?P.+(?=\*\*\* FIRST DRAW \*\*\*)|.+))?" + r"(\*\*\* FIRST DRAW \*\*\*(?P.+(?=\*\*\* SECOND DRAW \*\*\*)|.+))?" + r"(\*\*\* SECOND DRAW \*\*\*(?P.+(?=\*\*\* THIRD DRAW \*\*\*)|.+))?" + r"(\*\*\* THIRD DRAW \*\*\*(?P.+))?", hand.handText,re.DOTALL) hand.addStreets(m) def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand @@ -245,6 +214,27 @@ follow : whether to tail -f the input""" cards = set(cards.split(' ')) hand.addHoleCards(cards, m.group('PNAME')) + def readDrawCards(self, hand, street): + logging.debug("readDrawCards") + m = self.re_HeroCards.finditer(hand.streets[street]) + if m == None: + hand.involved = False + else: + for player in m: + hand.hero = player.group('PNAME') # Only really need to do this once + newcards = player.group('NEWCARDS') + oldcards = player.group('OLDCARDS') + if newcards == None: + newcards = set() + else: + newcards = set(newcards.split(' ')) + if oldcards == None: + oldcards = set() + else: + oldcards = set(oldcards.split(' ')) + hand.addDrawHoleCards(newcards, oldcards, player.group('PNAME'), street) + + def readStudPlayerCards(self, hand, street): # See comments of reference implementation in FullTiltToFpdb.py logging.debug("readStudPlayerCards") @@ -293,8 +283,12 @@ follow : whether to tail -f the input""" hand.addFold( street, action.group('PNAME')) elif action.group('ATYPE') == ' checks': hand.addCheck( street, action.group('PNAME')) + elif action.group('ATYPE') == ' discards': + hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED')) + elif action.group('ATYPE') == ' stands pat': + hand.addStandsPat( street, action.group('PNAME')) else: - print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),) + print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),) def readShowdownActions(self, hand): From 311e79c6e6da4eeec28466f5c45bc6b3ead29945 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 20:03:28 +0900 Subject: [PATCH 2/7] Quick note on test file. Intend on keeping better notes on test files so we can verify in testing at some point --- pyfpdb/test_PokerStars.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpdb/test_PokerStars.py b/pyfpdb/test_PokerStars.py index 06de1078..e2d3925d 100644 --- a/pyfpdb/test_PokerStars.py +++ b/pyfpdb/test_PokerStars.py @@ -2,6 +2,9 @@ import PokerStarsToFpdb import py +#regression-test-files/stars/badugi/ring-fl-badugi.txt +# s0rrow: start $30.00 end: $22.65 total: ($7.35) + def checkGameInfo(hhc, header, info): assert hhc.determineGameType(header) == info From d212d81e03e1a342cf8e48ac39327cf6cb131b1b Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 20:40:27 +0900 Subject: [PATCH 3/7] Add variables for most HudCache stats and add optional arg to Hand Will almost certainly need to change all of the data structures in the variables as they are only for a single player --- pyfpdb/DerivedStats.py | 91 ++++++++++++++++++++++++++++++++++++++++++ pyfpdb/Hand.py | 13 +++--- 2 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 pyfpdb/DerivedStats.py diff --git a/pyfpdb/DerivedStats.py b/pyfpdb/DerivedStats.py new file mode 100644 index 00000000..b526632a --- /dev/null +++ b/pyfpdb/DerivedStats.py @@ -0,0 +1,91 @@ +#!/usr/bin/python + +#Copyright 2008 Carl Gherardi +#This program is free software: you can redistribute it and/or modify +#it under the terms of the GNU Affero General Public License as published by +#the Free Software Foundation, version 3 of the License. +# +#This program is distributed in the hope that it will be useful, +#but WITHOUT ANY WARRANTY; without even the implied warranty of +#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#GNU General Public License for more details. +# +#You should have received a copy of the GNU Affero General Public License +#along with this program. If not, see . +#In the "official" distribution you can find the license in +#agpl-3.0.txt in the docs folder of the package. + +class DerivedStats(): + def __init__(self, hand): + self.hand = hand + + self.activeSeats = 0 + self.position = 0 + self.tourneyTypeId = 0 + + self.HDs = 0 + self.street0VPI = 0 + self.street0Aggr = 0 + self.street0_3B4BChance = 0 + self.street0_3B4BDone = 0 + + self.street1Seen = 0 + self.street2Seen = 0 + self.street3Seen = 0 + self.street4Seen = 0 + self.sawShowdown = 0 + + self.street1Aggr = 0 + self.street2Aggr = 0 + self.street3Aggr = 0 + self.street4Aggr = 0 + + self.otherRaisedStreet1 = 0 + self.otherRaisedStreet2 = 0 + self.otherRaisedStreet3 = 0 + self.otherRaisedStreet4 = 0 + self.foldToOtherRaisedStreet1 = 0 + self.foldToOtherRaisedStreet2 = 0 + self.foldToOtherRaisedStreet3 = 0 + self.foldToOtherRaisedStreet4 = 0 + self.wonWhenSeenStreet1 = 0 + self.wonAtSD = 0 + + self.stealAttemptChance = 0 + self.stealAttempted = 0 + self.foldBbToStealChance = 0 + self.foldedBbToSteal = 0 + self.foldSbToStealChance = 0 + self.foldedSbToSteal = 0 + + self.street1CBChance = 0 + self.street1CBDone = 0 + self.street2CBChance = 0 + self.street2CBDone = 0 + self.street3CBChance = 0 + self.street3CBDone = 0 + self.street4CBChance = 0 + self.street4CBDone = 0 + + self.foldToStreet1CBChance = 0 + self.foldToStreet1CBDone = 0 + self.foldToStreet2CBChance = 0 + self.foldToStreet2CBDone = 0 + self.foldToStreet3CBChance = 0 + self.foldToStreet3CBDone = 0 + self.foldToStreet4CBChance = 0 + self.foldToStreet4CBDone = 0 + + self.totalProfit = 0 + + self.street1CheckCallRaiseChance = 0 + self.street1CheckCallRaiseDone = 0 + self.street2CheckCallRaiseChance = 0 + self.street2CheckCallRaiseDone = 0 + self.street3CheckCallRaiseChance = 0 + self.street3CheckCallRaiseDone = 0 + self.street4CheckCallRaiseChance = 0 + self.street4CheckCallRaiseDone = 0 + + def getStats(): + pass diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 49517ab7..bbd6455f 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -27,11 +27,14 @@ import time from copy import deepcopy from Exceptions import * +import DerivedStats + class Hand: UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} LCS = {'H':'h', 'D':'d', 'C':'c', 'S':'s'} - def __init__(self, sitename, gametype, handText): + def __init__(self, sitename, gametype, handText, builtFrom = "HHC"): self.sitename = sitename + self.stats = DerivedStats.DerivedStats(self) self.gametype = gametype self.handText = handText self.handid = 0 @@ -369,14 +372,14 @@ Map the tuple self.gametype onto the pokerstars string describing it class HoldemOmahaHand(Hand): - def __init__(self, hhc, sitename, gametype, handText): + def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"): if gametype['base'] != 'hold': pass # or indeed don't pass and complain instead logging.debug("HoldemOmahaHand") self.streetList = ['BLINDSANTES', 'DEAL', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order self.communityStreets = ['FLOP', 'TURN', 'RIVER'] self.actionStreets = ['PREFLOP','FLOP','TURN','RIVER'] - Hand.__init__(self, sitename, gametype, handText) + Hand.__init__(self, sitename, gametype, handText, builtFrom = "HHC") self.sb = gametype['sb'] self.bb = gametype['bb'] @@ -527,7 +530,7 @@ Card ranks will be uppercased print >>fh, "\n\n" class DrawHand(Hand): - def __init__(self, hhc, sitename, gametype, handText): + def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"): if gametype['base'] != 'draw': pass # or indeed don't pass and complain instead self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE'] @@ -693,7 +696,7 @@ Card ranks will be uppercased class StudHand(Hand): - def __init__(self, hhc, sitename, gametype, handText): + def __init__(self, hhc, sitename, gametype, handText, builtFrom = "HHC"): if gametype['base'] != 'stud': pass # or indeed don't pass and complain instead self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order From b41afcd0762d675a7e1e6e3a38143e572cc2686a Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 21:19:20 +0900 Subject: [PATCH 4/7] Add stub insert and select methods to Hand class --- pyfpdb/Hand.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 49517ab7..b1615c8f 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -77,6 +77,27 @@ class Hand: self.rake = None + def insert(self, db): + """ Function to insert Hand into database +Should not commit, and do minimal selects. Callers may want to cache commits +db: a connected fpdb_db object""" + # TODO: + # Players - base playerid and siteid tuple + # HudCache data to come from DerivedStats class + # HandsActions - all actions for all players for all streets - self.actions + # BoardCards - ? + # Hands - Summary information of hand indexed by handId - gameinfo + # HandsPlayers - ? ... Do we fix winnings? + # Tourneys ? + # TourneysPlayers + + pass + + def select(self, handId): + """ Function to create Hand object from database """ + pass + + def addPlayer(self, seat, name, chips): """\ Adds a player to the hand, and initialises data structures indexed by player. From a5ceeece3a9686357ddf3e1a1219b7b7a2f75ed7 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 22:18:30 +0900 Subject: [PATCH 5/7] Added note about renamed test file Also changed name of Bike test file so I can tell what it is from ls --- pyfpdb/test_FullTilt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyfpdb/test_FullTilt.py b/pyfpdb/test_FullTilt.py index f53b3b43..81ea1d12 100644 --- a/pyfpdb/test_FullTilt.py +++ b/pyfpdb/test_FullTilt.py @@ -2,6 +2,8 @@ import FulltiltToFpdb import py +# regression-test-files/fulltilt/nlhe/NLHE-6max-1.txt +# Sorrowful: start: $8.85 end: $14.70 total: $5.85 def checkGameInfo(hhc, header, info): assert hhc.determineGameType(header) == info From d847a71c6e39ecc179f644c758941f11a8f59415 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 22:23:30 +0900 Subject: [PATCH 6/7] Move addDiscard from upper class to specialised DrawHand --- pyfpdb/Hand.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index 3971fa3a..b7bd5a4c 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -272,14 +272,6 @@ Add a raise on [street] by [player] to [amountTo] self.lastBet[street] = Decimal(amount) self.pot.addMoney(player, Decimal(amount)) - def addDiscard(self, street, player, num, cards): - self.checkPlayerExists(player) - if cards: - act = (player, 'discards', num, cards) - self.discardDrawHoleCards(cards, player, street) - else: - act = (player, 'discards', num) - self.actions[street].append(act) def addStandsPat(self, street, player): self.checkPlayerExists(player) @@ -622,10 +614,22 @@ player (string) name of player except FpdbParseError, e: print "[ERROR] Tried to add holecards for unknown player: %s" % (player,) + def discardDrawHoleCards(self, cards, player, street): logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street)) self.discards[player][street] = set([cards]) + + def addDiscard(self, street, player, num, cards): + self.checkPlayerExists(player) + if cards: + act = (player, 'discards', num, cards) + self.discardDrawHoleCards(cards, player, street) + else: + act = (player, 'discards', num) + self.actions[street].append(act) + + def addShownCards(self, cards, player, holeandboard=None): """\ For when a player shows cards for any reason (for showdown or out of choice). @@ -640,6 +644,7 @@ Card ranks will be uppercased # board = set([c for s in self.board.values() for c in s]) # self.addHoleCards(holeandboard.difference(board),player,shown=True) + def writeHand(self, fh=sys.__stdout__): # PokerStars format. print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime))) From faf19215e84f5c3198afef97a3f3a76330ab4b64 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 14 Mar 2009 23:01:40 +0900 Subject: [PATCH 7/7] Make Hand initable without a hhc Proof of concept code in test_PokerStars.py --- pyfpdb/Hand.py | 118 ++++++++++++++++++++----------------- pyfpdb/PokerStarsToFpdb.py | 2 +- pyfpdb/test_PokerStars.py | 11 ++++ 3 files changed, 75 insertions(+), 56 deletions(-) diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index b7bd5a4c..65d0e175 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -399,25 +399,28 @@ class HoldemOmahaHand(Hand): #Populate a HoldemOmahaHand #Generally, we call 'read' methods here, which get the info according to the particular filter (hhc) # which then invokes a 'addXXX' callback - hhc.readHandInfo(self) - hhc.readPlayerStacks(self) - hhc.compilePlayerRegexs(self) - hhc.markStreets(self) - hhc.readBlinds(self) - hhc.readButton(self) - hhc.readHeroCards(self) - hhc.readShowdownActions(self) - # Read actions in street order - for street in self.communityStreets: - if self.streets[street]: - hhc.readCommunityCards(self, street) - for street in self.actionStreets: - if self.streets[street]: - hhc.readAction(self, street) - hhc.readCollectPot(self) - hhc.readShownCards(self) - self.totalPot() # finalise it (total the pot) - hhc.getRake(self) + if builtFrom == "HHC": + hhc.readHandInfo(self) + hhc.readPlayerStacks(self) + hhc.compilePlayerRegexs(self) + hhc.markStreets(self) + hhc.readBlinds(self) + hhc.readButton(self) + hhc.readHeroCards(self) + hhc.readShowdownActions(self) + # Read actions in street order + for street in self.communityStreets: + if self.streets[street]: + hhc.readCommunityCards(self, street) + for street in self.actionStreets: + if self.streets[street]: + hhc.readAction(self, street) + hhc.readCollectPot(self) + hhc.readShownCards(self) + self.totalPot() # finalise it (total the pot) + hhc.getRake(self) + elif builtFrom == "DB": + self.select("dummy") # Will need a handId def addHoleCards(self, cards, player, shown=False): """\ @@ -553,24 +556,26 @@ class DrawHand(Hand): self.sb = gametype['sb'] self.bb = gametype['bb'] # Populate the draw hand. - hhc.readHandInfo(self) - hhc.readPlayerStacks(self) - hhc.compilePlayerRegexs(self) - hhc.markStreets(self) - hhc.readBlinds(self) - hhc.readButton(self) - hhc.readShowdownActions(self) - # Read actions in street order - for street in self.streetList: - if self.streets[street]: - # hhc.readCommunityCards(self, street) - hhc.readDrawCards(self, street) - hhc.readAction(self, street) - hhc.readCollectPot(self) - hhc.readShownCards(self) - self.totalPot() # finalise it (total the pot) - hhc.getRake(self) - + if builtFrom == "HHC": + hhc.readHandInfo(self) + hhc.readPlayerStacks(self) + hhc.compilePlayerRegexs(self) + hhc.markStreets(self) + hhc.readBlinds(self) + hhc.readButton(self) + hhc.readShowdownActions(self) + # Read actions in street order + for street in self.streetList: + if self.streets[street]: + # hhc.readCommunityCards(self, street) + hhc.readDrawCards(self, street) + hhc.readAction(self, street) + hhc.readCollectPot(self) + hhc.readShownCards(self) + self.totalPot() # finalise it (total the pot) + hhc.getRake(self) + elif builtFrom == "DB": + self.select("dummy") # Will need a handId # Draw games (at least Badugi has blinds - override default Holdem addBlind def addBlind(self, player, blindtype, amount): @@ -733,24 +738,27 @@ class StudHand(Hand): #Populate the StudHand #Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc) # which then invokes a 'addXXX' callback - hhc.readHandInfo(self) - hhc.readPlayerStacks(self) - hhc.compilePlayerRegexs(self) - hhc.markStreets(self) - hhc.readAntes(self) - hhc.readBringIn(self) -# hhc.readShowdownActions(self) # not done yet - # Read actions in street order - for street in self.streetList: - if self.streets[street]: - logging.debug(street) - logging.debug(self.streets[street]) - hhc.readStudPlayerCards(self, street) - hhc.readAction(self, street) - hhc.readCollectPot(self) -# hhc.readShownCards(self) # not done yet - self.totalPot() # finalise it (total the pot) - hhc.getRake(self) + if builtFrom == "HHC": + hhc.readHandInfo(self) + hhc.readPlayerStacks(self) + hhc.compilePlayerRegexs(self) + hhc.markStreets(self) + hhc.readAntes(self) + hhc.readBringIn(self) + #hhc.readShowdownActions(self) # not done yet + # Read actions in street order + for street in self.streetList: + if self.streets[street]: + logging.debug(street) + logging.debug(self.streets[street]) + hhc.readStudPlayerCards(self, street) + hhc.readAction(self, street) + hhc.readCollectPot(self) + #hhc.readShownCards(self) # not done yet + self.totalPot() # finalise it (total the pot) + hhc.getRake(self) + elif builtFrom == "DB": + self.select("dummy") # Will need a handId def addPlayerCards(self, player, street, open=[], closed=[]): """\ diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index 3d5ff83d..1915bb42 100755 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -46,7 +46,7 @@ follow : whether to tail -f the input""" if autostart: self.start() - + def compilePlayerRegexs(self, hand): players = set([player[1] for player in hand.players]) if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' diff --git a/pyfpdb/test_PokerStars.py b/pyfpdb/test_PokerStars.py index e2d3925d..85107e6f 100644 --- a/pyfpdb/test_PokerStars.py +++ b/pyfpdb/test_PokerStars.py @@ -1,10 +1,21 @@ # -*- coding: utf-8 -*- import PokerStarsToFpdb +from Hand import * import py #regression-test-files/stars/badugi/ring-fl-badugi.txt # s0rrow: start $30.00 end: $22.65 total: ($7.35) +gametype = {'type':'ring', 'base':'draw', 'category':'badugi', 'limitType':'fl', 'sb':'0.25', 'bb':'0.50','currency':'USD'} +text = "" + +hhc = PokerStarsToFpdb.PokerStars(autostart=False) + +h = HoldemOmahaHand(None, "ASite", gametype, text, builtFrom = "Test") +h.addPlayer("1", "s0rrow", "100000") + +hhc.compilePlayerRegexs(h) + def checkGameInfo(hhc, header, info): assert hhc.determineGameType(header) == info