diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index dfffc9ce..ccad9b67 100644 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1514,6 +1514,7 @@ class Database: c.execute("INSERT INTO Sites (name,code) VALUES ('PKR', 'PK')") c.execute("INSERT INTO Sites (name,code) VALUES ('iPoker', 'IP')") c.execute("INSERT INTO Sites (name,code) VALUES ('Winamax', 'WM')") + c.execute("INSERT INTO Sites (name,code) VALUES ('Everest', 'EP')") #Fill Actions c.execute("INSERT INTO Actions (name,code) VALUES ('ante', 'A')") c.execute("INSERT INTO Actions (name,code) VALUES ('small blind', 'SB')") diff --git a/pyfpdb/Filters.py b/pyfpdb/Filters.py index a4660eb2..c1349367 100644 --- a/pyfpdb/Filters.py +++ b/pyfpdb/Filters.py @@ -420,12 +420,15 @@ class Filters(threading.Thread): self.limits[limit] = w.get_active() log.debug(_("self.limit[%s] set to %s") %(limit, self.limits[limit])) if limit.isdigit() or (len(limit) > 2 and (limit[-2:] == 'nl' or limit[-2:] == 'fl' or limit[-2:] == 'pl')): + # turning a leaf limit on with 'None' checked turns 'None' off if self.limits[limit]: if self.cbNoLimits is not None: self.cbNoLimits.set_active(False) + # turning a leaf limit off with 'All' checked turns 'All' off else: if self.cbAllLimits is not None: self.cbAllLimits.set_active(False) + # turning off a leaf limit turns off the corresponding fl. nl, or pl if not self.limits[limit]: if limit.isdigit(): if self.cbFL is not None: @@ -438,24 +441,28 @@ class Filters(threading.Thread): self.cbPL.set_active(False) elif limit == "all": if self.limits[limit]: - #for cb in self.cbLimits.values(): - # cb.set_active(True) - if self.cbFL is not None: - self.cbFL.set_active(True) - if self.cbNL is not None: - self.cbNL.set_active(True) - if self.cbPL is not None: - self.cbPL.set_active(True) + if self.num_limit_types == 1: + for cb in self.cbLimits.values(): + cb.set_active(True) + else: + if self.cbFL is not None: + self.cbFL.set_active(True) + if self.cbNL is not None: + self.cbNL.set_active(True) + if self.cbPL is not None: + self.cbPL.set_active(True) elif limit == "none": if self.limits[limit]: - for cb in self.cbLimits.values(): - cb.set_active(False) - if self.cbNL is not None: - self.cbNL.set_active(False) - if self.cbFL is not None: - self.cbFL.set_active(False) - if self.cbPL is not None: - self.cbPL.set_active(False) + if self.num_limit_types == 1: + for cb in self.cbLimits.values(): + cb.set_active(False) + else: + if self.cbNL is not None: + self.cbNL.set_active(False) + if self.cbFL is not None: + self.cbFL.set_active(False) + if self.cbPL is not None: + self.cbPL.set_active(False) elif limit == "fl": if not self.limits[limit]: # only toggle all fl limits off if they are all currently on @@ -765,11 +772,11 @@ class Filters(threading.Thread): dest = vbox3 # for ring/tour buttons if "LimitType" in display and display["LimitType"] == True: - num_limit_types = 0 - if self.found['fl']: num_limit_types = num_limit_types + 1 - if self.found['pl']: num_limit_types = num_limit_types + 1 - if self.found['nl']: num_limit_types = num_limit_types + 1 - if num_limit_types > 1: + self.num_limit_types = 0 + if self.found['fl']: self.num_limit_types = self.num_limit_types + 1 + if self.found['pl']: self.num_limit_types = self.num_limit_types + 1 + if self.found['nl']: self.num_limit_types = self.num_limit_types + 1 + if self.num_limit_types > 1: if self.found['fl']: hbox = gtk.HBox(False, 0) vbox3.pack_start(hbox, False, False, 0) diff --git a/pyfpdb/FulltiltToFpdb.py b/pyfpdb/FulltiltToFpdb.py index e1585240..30444353 100755 --- a/pyfpdb/FulltiltToFpdb.py +++ b/pyfpdb/FulltiltToFpdb.py @@ -49,6 +49,7 @@ class Fulltilt(HandHistoryConverter): '6.00': ('1.00', '3.00'), '6': ('1.00', '3.00'), '8.00': ('2.00', '4.00'), '8': ('2.00', '4.00'), '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'), + '16.00': ('4.00', '8.00'), '16': ('4.00', '8.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'), @@ -88,7 +89,7 @@ class Fulltilt(HandHistoryConverter): [%(LS)s]?(?P[.0-9]+\sCap\s)? (?P[-\da-zA-Z\/\'\s]+)\s-\s (?P.*$) - (?P\(partial\))?\n + (?P\(partial\))?\s (?:.*?\n(?PHand\s\#(?P=HID)\shas\sbeen\scanceled))? ''' % substitutions, re.MULTILINE|re.VERBOSE) re_TourneyExtraInfo = re.compile('''(((?P[^$]+)? @@ -147,7 +148,7 @@ class Fulltilt(HandHistoryConverter): re_Mixed = re.compile(r'\s\-\s(?PHA|HORSE|HOSE)\s\-\s', re.VERBOSE) re_Max = re.compile("(?P\d+)( max)?", re.MULTILINE) # NB: if we ever match "Full Tilt Poker" we should also match "FullTiltPoker", which PT Stud erroneously exports. - re_DateTime = re.compile("""((?P[0-9]+):(?P[0-9]+):(?P[0-9]+)\s(?P\w+)\s-\s(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})|(?P

[0-9]+):(?P[0-9]+)\s(?P\w+)\s-\s\w+\,\s(?P\w+)\s(?P\d+)\,\s(?P[0-9]{4}))""", re.MULTILINE) + re_DateTime = re.compile("""((?P[0-9]+):(?P[0-9]+):(?P[0-9]+)\s(?P\w+)\s-\s(?P[0-9]{4})\/(?P[0-9]{2})\/(?P[0-9]{2})|(?P

[0-9]+):(?P[0-9]+)\s(?P\w+)\s-\s\w+\,\s(?P\w+)\s(?P\d+)\,\s(?P[0-9]{4}))(?P\s\(partial\))?""", re.MULTILINE) @@ -283,10 +284,13 @@ class Fulltilt(HandHistoryConverter): datetimestr = "%s/%s/%s %s:%s" % (a.group('Y2'), a.group('M2'),a.group('D2'),a.group('H2'),a.group('MIN2')) timezone = a.group('TZ2') hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%B/%d %H:%M") + if a.group('PARTIAL'): + raise FpdbParseError(hid=m.group('HID')) hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, timezone, "UTC") if m.group("CANCELLED") or m.group("PARTIAL"): + # It would appear this can't be triggered as DATETIME is a bit greedy raise FpdbParseError(hid=m.group('HID')) if m.group('TABLEATTRIBUTES'): @@ -351,7 +355,7 @@ class Fulltilt(HandHistoryConverter): n = self.re_SummarySitout.finditer(post) for b in n: del plist[b.group('PNAME')] - print "DEBUG: Deleting '%s' from player dict" %(b.group('PNAME')) + #print "DEBUG: Deleting '%s' from player dict" %(b.group('PNAME')) # Add remaining players for a in plist: diff --git a/pyfpdb/GuiPositionalStats.py b/pyfpdb/GuiPositionalStats.py index 06d28883..fc7ab83c 100644 --- a/pyfpdb/GuiPositionalStats.py +++ b/pyfpdb/GuiPositionalStats.py @@ -15,6 +15,9 @@ #along with this program. If not, see . #In the "official" distribution you can find the license in agpl-3.0.txt. +import L10n +_ = L10n.get_translation() + import threading import pygtk pygtk.require('2.0') @@ -22,18 +25,6 @@ import gtk import os from time import time, strftime -import locale -lang=locale.getdefaultlocale()[0][0:2] -if lang=="en": - def _(string): return string -else: - import gettext - try: - trans = gettext.translation("fpdb", localedir="locale", languages=[lang]) - trans.install() - except IOError: - def _(string): return string - import fpdb_import import Database import Filters diff --git a/pyfpdb/GuiPrefs.py b/pyfpdb/GuiPrefs.py index 6c8c6210..166a2500 100755 --- a/pyfpdb/GuiPrefs.py +++ b/pyfpdb/GuiPrefs.py @@ -15,6 +15,9 @@ #along with this program. If not, see . #In the "official" distribution you can find the license in agpl-3.0.txt. +import L10n +_ = L10n.get_translation() + import xml.dom.minidom from xml.dom.minidom import Node @@ -23,18 +26,6 @@ pygtk.require('2.0') import gtk import gobject -import locale -lang=locale.getdefaultlocale()[0][0:2] -if lang=="en": - def _(string): return string -else: - import gettext - try: - trans = gettext.translation("fpdb", localedir="locale", languages=[lang]) - trans.install() - except IOError: - def _(string): return string - import Configuration rewrite = { 'general' : 'General', 'supported_databases' : 'Databases' diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index f061322e..9a64eac6 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -339,7 +339,7 @@ class GuiRingPlayerStats (GuiPlayerStats.GuiPlayerStats): #end def createStatsTable def reset_style_render_func(self, treeviewcolumn, cell, model, iter): - cell.set_property('foreground', 'black') + cell.set_property('foreground', None) #end def reset_style_render_func def ledger_style_render_func(self, tvcol, cell, model, iter): diff --git a/pyfpdb/GuiTourneyPlayerStats.py b/pyfpdb/GuiTourneyPlayerStats.py index 0b830547..672b4eb7 100644 --- a/pyfpdb/GuiTourneyPlayerStats.py +++ b/pyfpdb/GuiTourneyPlayerStats.py @@ -434,7 +434,7 @@ class GuiTourneyPlayerStats (GuiPlayerStats.GuiPlayerStats): #end def refreshStats def reset_style_render_func(self, treeviewcolumn, cell, model, iter): - cell.set_property('foreground', 'black') + cell.set_property('foreground', None) #end def reset_style_render_func def sortCols(self, col, nums): diff --git a/pyfpdb/HUD_main.pyw b/pyfpdb/HUD_main.pyw index d9a4ab73..721644bb 100755 --- a/pyfpdb/HUD_main.pyw +++ b/pyfpdb/HUD_main.pyw @@ -23,6 +23,9 @@ Main for FreePokerTools HUD. """ +import L10n +_ = L10n.get_translation() + # Standard Library modules import sys import os @@ -51,21 +54,6 @@ elif sys.platform == 'darwin': else: # This is bad--figure out the values for the various windows flavors import WinTables as Tables -import locale -lang = locale.getdefaultlocale()[0][0:2] -print "lang:", lang -if lang == "en": - def _(string): - return string -else: - import gettext - try: - trans = gettext.translation("fpdb", localedir="locale", languages=[lang]) - trans.install() - except IOError: - def _(string): - return string - # get config and set up logger c = Configuration.Config(file=options.config, dbname=options.dbname) log = Configuration.get_logger("logging.conf", "hud", log_dir=c.dir_log, log_file='HUD-log.txt') diff --git a/pyfpdb/Hand.py b/pyfpdb/Hand.py index fd781448..4eca45a8 100644 --- a/pyfpdb/Hand.py +++ b/pyfpdb/Hand.py @@ -1140,7 +1140,7 @@ class DrawHand(Hand): hhc.markStreets(self) # markStreets in Draw may match without dealing cards if self.streets['DEAL'] == None: - raise FpdbParseError(_("DrawHand.__init__: street 'DEAL' is empty. Hand cancelled?")) + raise FpdbParseError(_("DrawHand.__init__: street 'DEAL' is empty. Hand cancelled? '%s'" % self.handid)) hhc.readBlinds(self) hhc.readAntes(self) hhc.readButton(self) diff --git a/pyfpdb/OnGameToFpdb.py b/pyfpdb/OnGameToFpdb.py index cacd288a..d69170a6 100755 --- a/pyfpdb/OnGameToFpdb.py +++ b/pyfpdb/OnGameToFpdb.py @@ -49,7 +49,7 @@ class OnGame(HandHistoryConverter): } currencies = { u'\u20ac':'EUR', u'\xe2\x82\xac':'EUR', '$':'USD', '':'T$' } - limits = { 'NO_LIMIT':'nl', 'LIMIT':'fl'} + limits = { 'NO_LIMIT':'nl', 'POT_LIMIT':'pl', 'LIMIT':'fl'} games = { # base, category "TEXAS_HOLDEM" : ('hold','holdem'), @@ -73,7 +73,7 @@ class OnGame(HandHistoryConverter): Start\shand:\s(?P.*) Table:\s(?P[-\'\w\s]+)\s\[\d+\]\s\( ( - (?PNO_LIMIT|Limit|LIMIT|Pot\sLimit)\s + (?PNO_LIMIT|Limit|LIMIT|Pot\sLimit|POT_LIMIT)\s (?PTEXAS_HOLDEM|OMAHA_HI|SEVEN_CARD_STUD|SEVEN_CARD_STUD_HI_LO|RAZZ|FIVE_CARD_DRAW)\s (?P%(LS)s|)?(?P[.0-9]+)/ (%(LS)s)?(?P[.0-9]+) @@ -140,6 +140,7 @@ class OnGame(HandHistoryConverter): def readSupportedGames(self): return [ ["ring", "hold", "fl"], + ["ring", "hold", "pl"], ["ring", "hold", "nl"], ["ring", "stud", "fl"], ["ring", "draw", "fl"], @@ -158,6 +159,7 @@ class OnGame(HandHistoryConverter): raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp) mg = m.groupdict() + #print "DEBUG: mg: %s" % mg info['type'] = 'ring' if 'CURRENCY' in mg: @@ -314,9 +316,9 @@ class OnGame(HandHistoryConverter): m = self.re_Action.finditer(hand.streets[street]) for action in m: #acts = action.groupdict() - #log.debug("readaction: acts: %s" %acts) + #print "readaction: acts: %s" %acts if action.group('ATYPE') == ' raises': - hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') ) + hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) elif action.group('ATYPE') == ' calls': hand.addCall( street, action.group('PNAME'), action.group('BET') ) elif action.group('ATYPE') == ' bets': diff --git a/pyfpdb/PartyPokerToFpdb.py b/pyfpdb/PartyPokerToFpdb.py index c1f33295..2c07e138 100755 --- a/pyfpdb/PartyPokerToFpdb.py +++ b/pyfpdb/PartyPokerToFpdb.py @@ -49,7 +49,8 @@ class PartyPoker(HandHistoryConverter): currencies = {"\$": "USD", "$": "USD", u"\xe2\x82\xac": "EUR", u"\u20ac": "EUR", '': "T$"} substitutions = { 'LEGAL_ISO' : "USD|EUR", # legal ISO currency codes - 'LS' : "\$|\u20AC|\xe2\x82\xac|" # legal currency symbols - Euro(cp1252, utf-8) + 'LS' : u"\$|\u20ac|\xe2\x82\xac|", # Currency symbols - Euro(cp1252, utf-8) + 'NUM' : u".,\d", } # Static regexes @@ -81,7 +82,7 @@ class PartyPoker(HandHistoryConverter): re_PlayerInfo = re.compile(u""" Seat\s(?P\d+):\s (?P.*)\s - \(\s*[%(LS)s]?(?P[0-9,.]+)\s*(?:%(LEGAL_ISO)s|)\s*\) + \(\s*[%(LS)s]?(?P[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s|)\s*\) """ % substitutions, re.VERBOSE| re.UNICODE) re_HandInfo = re.compile(""" diff --git a/pyfpdb/PokerStarsToFpdb.py b/pyfpdb/PokerStarsToFpdb.py index b9e1cb3e..fdb6ca22 100644 --- a/pyfpdb/PokerStarsToFpdb.py +++ b/pyfpdb/PokerStarsToFpdb.py @@ -84,8 +84,8 @@ class PokerStars(HandHistoryConverter): # Static regexes re_GameInfo = re.compile(u""" - PokerStars\sGame\s\#(?P[0-9]+):\s+ - (Tournament\s\# # open paren of tournament info + PokerStars(\sHome)?\sGame\s\#(?P[0-9]+):\s+ + (\{.*\}\s+)?(Tournament\s\# # open paren of tournament info (?P\d+),\s # here's how I plan to use LS (?P(?P[%(LS)s\d\.]+)?\+?(?P[%(LS)s\d\.]+)?\+?(?P[%(LS)s\d\.]+)?\s?(?P%(LEGAL_ISO)s)?|Freeroll)\s+)? diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py index 312de6d5..fcc82dd4 100644 --- a/pyfpdb/RushNotesAux.py +++ b/pyfpdb/RushNotesAux.py @@ -109,9 +109,9 @@ class RushNotes(Aux_Window): notepath = site_params_dict['site_path'] # this is a temporary hijack of site-path self.heroid = self.hud.db_connection.get_player_id(self.config, sitename, heroname) self.notefile = notepath + "/" + heroname + ".xml" - self.rushtables = ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom") + self.rushtables = ("Mach 10", "Lightning", "Celerity", "Flash", "Zoom", "Apollo") - if not os.path.isfile(self.notefile): + if not (os.path.isfile(self.notefile)): self.active = False return else: @@ -130,29 +130,34 @@ class RushNotes(Aux_Window): xmlnotefile.unlink # - # Create a fresh queue file with skeleton XML + # if queue file does not exist create a fresh queue file with skeleton XML + # This is possibly not totally safe, if multiple threads arrive + # here at the same time, but the consequences are not serious # + self.queuefile = self.notefile + ".queue" - queuedom = minidom.Document() + if not (os.path.isfile(self.queuefile)): - pld=queuedom.createElement("PLAYERDATA") - queuedom.appendChild(pld) + queuedom = minidom.Document() - nts=queuedom.createElement("NOTES") - pld.appendChild(nts) + pld=queuedom.createElement("PLAYERDATA") + queuedom.appendChild(pld) - nte = queuedom.createElement("NOTE") - nte = queuedom.createTextNode("\n") - nts.insertBefore(nte,None) + nts=queuedom.createElement("NOTES") + pld.appendChild(nts) - outputfile = open(self.queuefile, 'w') - queuedom.writexml(outputfile) - outputfile.close() - queuedom.unlink + nte = queuedom.createElement("NOTE") + nte = queuedom.createTextNode("\n") + nts.insertBefore(nte,None) + + outputfile = open(self.queuefile, 'w') + queuedom.writexml(outputfile) + outputfile.close() + queuedom.unlink if (debugmode): #initialise logfiles - debugfile=open("~Rushdebug.init", "w") + debugfile=open("~Rushdebug.init", "a") debugfile.write("conf="+str(config)+"\n") debugfile.write("spdi="+str(site_params_dict)+"\n") debugfile.write("para="+str(params)+"\n") @@ -161,8 +166,6 @@ class RushNotes(Aux_Window): debugfile.write("queu="+self.queuefile+"\n") debugfile.close() - open("~Rushdebug.data", "w").close() - def update_data(self, new_hand_id, db_connection): #this method called once for every hand processed @@ -204,16 +207,19 @@ class RushNotes(Aux_Window): vpip=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'vpip')[3] + " ") pfr=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'pfr')[3] + " ") three_B=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'three_B')[3] + " ") + four_B=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'four_B')[3] + " ") cbet=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'cbet')[3] + " ") + fbbsteal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'f_BB_steal')[3] + " ") - + f_3bet=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'f_3bet')[3] + " ") + f_4bet=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'f_4bet')[3] + " ") + steal=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'steal')[3] + " ") ffreq1=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'ffreq1')[3] + " ") agg_freq=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'agg_freq')[3] + " ") BBper100=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'BBper100')[3]) if BBper100[6] == "-": BBper100=BBper100[0:6] + "(" + BBper100[7:] + ")" - # # grab villain known starting hands # only those where they VPIP'd, so limp in the BB will not be shown @@ -269,10 +275,13 @@ class RushNotes(Aux_Window): # for later search/replace by Merge module # xmlqueuedict[playername] = ("~fpdb~" + "\n" + - n + vpip + pfr + three_B + fbbsteal + "\n" + - steal + cbet + ffreq1 + "\n" + + n + vpip + pfr + "\n" + + steal + cbet + fbbsteal + ffreq1 + "\n" + + three_B + four_B + f_3bet + f_4bet + "\n" + agg_freq + BBper100 + "\n" + - PFcall+"\n"+PFaggr+"\n"+PFdefend +"\n" + PFcall+"\n"+ + PFaggr+"\n"+ + PFdefend +"\n"+ "~ends~") if (debugmode): diff --git a/pyfpdb/RushNotesMerge.py b/pyfpdb/RushNotesMerge.py index 433a9873..11069793 100755 --- a/pyfpdb/RushNotesMerge.py +++ b/pyfpdb/RushNotesMerge.py @@ -105,8 +105,6 @@ if not os.path.isfile(sys.argv[1]): if not os.path.isfile((sys.argv[1]+".queue")): print "Nothing found to merge, quitting" - print "Did the HUD not get started during the last session?" - print "Has the HUD been stopped and started without merging?" quit() print "***************************************************************" @@ -117,7 +115,7 @@ print "has stopped completely" print "***************************************************************" print print "read from: ", sys.argv[1] -print "merge with: ", sys.argv[1]+".queue" +print "updated with: ", sys.argv[1]+".queue" #read queue and turn into a dict queuedict = {} @@ -172,7 +170,7 @@ mergednotes.close() xmlnotefile.unlink -print "Merged file has been written to: ", sys.argv[1]+".merged" +print "written to: ", sys.argv[1]+".merged" print "" print "number in queue: ", statqueue print "existing players updated: ", statupdated @@ -180,5 +178,7 @@ print "new players added: ", statadded print "\n" print "Use a viewer to check the contents of the merge file." print "If you are happy, carry out the following steps:" -print "1 Rename or delete the existing notes file (normally .xml" +print "1 Rename or delete the existing notes file (normally .xml)" print "2 Rename the .merged file to become the new notes file" +print "3 Delete the .queue file (it will be created at the next rush autoimport)" + diff --git a/pyfpdb/RushNotesReadMe.txt b/pyfpdb/RushNotesReadMe.txt index 43475536..ef434708 100644 --- a/pyfpdb/RushNotesReadMe.txt +++ b/pyfpdb/RushNotesReadMe.txt @@ -15,11 +15,6 @@ Important info: The Merge process can only be run when ftp client is shutdown - otherwise ftp overwrites the xml on exit. -Restarting the autoimport will empty the notes"queue" so avoid restarting - autoimport until the previous notes "queue" has been merged. You will - lose all the queued notes, but these will be regenerated the next time - the villian is at your table, so it isn't the end of the world. - Existing ftp notes _SHOULD_ be preserved, but this isn't guaranteed, you have been warned! @@ -27,7 +22,8 @@ Existing colour codings should be preserved, this process does not change or set colourcodings. Copies of the live ftp notes file should be preserved everytime - RushNotesAux (i.e. the HUD is started) + RushNotesAux (i.e. the HUD is started). If you play at different + rush tables, the backup will be created several times. The AW is hard-coded with just the table names of Micro Rush Poker, and should ignore all other hands. @@ -35,7 +31,7 @@ The AW is hard-coded with just the table names of Micro Rush Poker, What might not work? -------------------- -This isn't tested with Windows, and probably won't work, feedback welcome. +This should work with windows sourcecode version, but will not work with the exe download. Hasn't been tested for co-existance with other sites, feedback welcome. Whenever FTP change their notes file format, this will all break rather spectacularly, you have been warned! @@ -77,7 +73,7 @@ execute the following: ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myname.xml" A revised notes file (blah.merge) should automagically appear in the full tilt root directory. -If you are happy with it, replace the existing (myname.xml file) +If you are happy with it, replace the existing (myname.xml file) and delete the .queue file. Since the updates aren't real time, it would be ok to play the rush @@ -176,7 +172,7 @@ Process overview ---------------- 1/ The HUD process is started. -1.1/ when the first hand is received, h fresh holding file is created, and +1.1/ when the first hand is received, a queue file is created if not already there, and a copy of the current live xml note file is created as a security backup. 2/ For every hand played, the auxillary window is called 3/ Based upon the players in the hand, fpdb will be interrogated @@ -191,4 +187,4 @@ existing notes, but this cannot be guaranteed. they replace the existing note file. 9/ Note that this process never updates the live notes file in situ, but there is a risk that something goes wrong, and that existing notes could be destroyed. - +10/ the queue file can be deleted to reduce re-processing next time. diff --git a/pyfpdb/SQL.py b/pyfpdb/SQL.py index 09c467c4..69b02d25 100644 --- a/pyfpdb/SQL.py +++ b/pyfpdb/SQL.py @@ -2294,7 +2294,7 @@ class Sql: end AS pofafq ,case when sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer)) = 0 then -999 else (sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) - /(sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) + /(0.0+sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) end AS aggfac ,100.0*(sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) / ((sum(cast(hp.foldToOtherRaisedStreet1 as integer))+ sum(cast(hp.foldToOtherRaisedStreet2 as integer))+ sum(cast(hp.foldToOtherRaisedStreet3 as integer))+ sum(cast(hp.foldToOtherRaisedStreet4 as integer))) + @@ -2415,7 +2415,7 @@ class Sql: end AS pofafq ,case when sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer)) = 0 then -999 else (sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) - /(sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) + /(0.0+sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) end AS aggfac ,100.0*(sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) / ((sum(cast(hp.foldToOtherRaisedStreet1 as integer))+ sum(cast(hp.foldToOtherRaisedStreet2 as integer))+ sum(cast(hp.foldToOtherRaisedStreet3 as integer))+ sum(cast(hp.foldToOtherRaisedStreet4 as integer))) + @@ -2537,7 +2537,7 @@ class Sql: end AS pofafq ,case when sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer)) = 0 then -999 else (sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) - /(sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) + /(0.0+sum(cast(hp.street1Calls as integer))+ sum(cast(hp.street2Calls as integer))+ sum(cast(hp.street3Calls as integer))+ sum(cast(hp.street4Calls as integer))) end AS aggfac ,100.0*(sum(cast(hp.street1Aggr as integer)) + sum(cast(hp.street2Aggr as integer)) + sum(cast(hp.street3Aggr as integer)) + sum(cast(hp.street4Aggr as integer))) / ((sum(cast(hp.foldToOtherRaisedStreet1 as integer))+ sum(cast(hp.foldToOtherRaisedStreet2 as integer))+ sum(cast(hp.foldToOtherRaisedStreet3 as integer))+ sum(cast(hp.foldToOtherRaisedStreet4 as integer))) + diff --git a/pyfpdb/Stove.py b/pyfpdb/Stove.py index 2d4d08b9..8600b255 100755 --- a/pyfpdb/Stove.py +++ b/pyfpdb/Stove.py @@ -15,10 +15,13 @@ import L10n _ = L10n.get_translation() import sys, random +import re import pokereval SUITS = ['h', 'd', 's', 'c'] +CONNECTORS = ['32', '43', '54', '65', '76', '87', '98', 'T9', 'JT', 'QJ', 'KQ', 'AK'] + ANY = 0 SUITED = 1 OFFSUIT = 2 @@ -65,14 +68,7 @@ class Stove: hands_in_range = string.strip().split(',') for h in hands_in_range: _h = h.strip() - if len(_h) > 3: - cc = _h.split() - r1 = cc[0] - r2 = cc[1] - vp = Cards(r1, r2) - h_range.add(vp) - else: - h_range.expand(expand_hands(_h, self.hand, self.board)) + h_range.expand(expand_hands(_h, self.hand, self.board)) self.h_range = h_range @@ -83,7 +79,7 @@ class Cards: self.c2 = c2 def get(self): - return [c1, c2] + return [self.c1, self.c2] class Board: def __init__(self, b1=None, b2=None, b3=None, b4=None, b5=None): @@ -127,7 +123,7 @@ class Range: def get(self): return sorted(self.__hands) - + class EV: def __init__(self, plays, win, tie, lose): @@ -175,6 +171,35 @@ def expand_hands(abbrev, hand, board): known_cards.update(set([hand.c2, hand.c2])) known_cards.update(set([board.b1, board.b2, board.b3, board.b4, board.b5])) + re.search('[2-9TJQKA]{2}(s|o)',abbrev) + + if re.search('^[2-9TJQKA]{2}(s|o)$',abbrev): #AKs or AKo + return standard_expand(abbrev, hand, known_cards) + elif re.search('^[2-9TJQKA]{2}(s|o)\+$',abbrev): #76s+ or 76o+ + return iterative_expand(abbrev, hand, known_cards) + #elif: AhXh + #elif: Ah6h+A + +def iterative_expand(abbrev, hand, known_cards): + r1 = abbrev[0] + r2 = abbrev[1] + + h_range = [] + considered = set() + + idx = CONNECTORS.index('%s%s' % (r1, r2)) + + ltr = abbrev[2] + + h_range = [] + for h in CONNECTORS[idx:]: + abr = "%s%s" % (h, ltr) + h_range += standard_expand(abr, hand, known_cards) + + return h_range + + +def standard_expand(abbrev, hand, known_cards): # Card ranks may be different r1 = abbrev[0] r2 = abbrev[1] @@ -228,7 +253,7 @@ def odds_for_hand(hand1, hand2, board, iterations): board = board, iterations = iterations ) - + plays = int(res['info'][0]) eval = res['eval'][0] diff --git a/pyfpdb/regression-test-files/cash/FTP/Draw/3-Draw-Limit-USD-20-40-201101.Partial.txt b/pyfpdb/regression-test-files/cash/FTP/Draw/3-Draw-Limit-USD-20-40-201101.Partial.txt new file mode 100644 index 00000000..da8be6ea Binary files /dev/null and b/pyfpdb/regression-test-files/cash/FTP/Draw/3-Draw-Limit-USD-20-40-201101.Partial.txt differ diff --git a/pyfpdb/regression-test-files/cash/OnGame/Flop/PLO-6max-EUR-1-1-2011002.Sample.txt b/pyfpdb/regression-test-files/cash/OnGame/Flop/PLO-6max-EUR-1-1-2011002.Sample.txt new file mode 100644 index 00000000..b9a68bb4 --- /dev/null +++ b/pyfpdb/regression-test-files/cash/OnGame/Flop/PLO-6max-EUR-1-1-2011002.Sample.txt @@ -0,0 +1,44 @@ +***** History for hand R5-21973400-50 ***** +Start hand: Mon Feb 7 15:22:00 GMT+0100 2011 +Table: Oullins [21973414] (POT_LIMIT OMAHA_HI €1/€1, Real money) +User: Hero +Button: seat 7 +Players in round: 6 +Seat 8: Player8 (€89) +Seat 10: Hero (€100) +Seat 2: Player2 (€50) +Seat 3: Player3 (€93.50) +Seat 5: Player5 (€86.15) +Seat 7: Player7 (€149.40) +Player8 posts small blind (€0.50) +Hero posts big blind (€1) +--- +Dealing pocket cards +Dealing to Hero: [3c, 7c, 4c, 5h] +Player2 calls €1 +Player3 raises €4.50 to €4.50 +Player5 folds +Player7 calls €4.50 +Player8 calls €4 +Hero calls €3.50 +Player2 folds +--- Dealing flop [7s, 4d, 3h] +Player8 checks +Hero bets €12 +Player3 calls €12 +Player7 folds +Player8 folds +--- Dealing turn [6d] +Hero bets €21.50 +Player3 folds +--- +Summary: +Main pot: €43 won by Hero (€40.85) +Rake taken: €2.15 +Seat 8: Player8 (€84.50), net: -€4.50 +Seat 10: Hero (€124.35), net: +€24.35 +Seat 2: Player2 (€49), net: -€1 +Seat 3: Player3 (€77), net: -€16.50 +Seat 5: Player5 (€86.15) +Seat 7: Player7 (€144.90), net: -€4.50 +***** End of hand R5-21973414-50 ***** diff --git a/pyfpdb/regression-test-files/summaries/FTP/NLHE-USD-SnG-6man-201101.Hero.cashes.txt b/pyfpdb/regression-test-files/summaries/FTP/NLHE-USD-SnG-6man-201101.Hero.cashes.txt new file mode 100644 index 00000000..439a0ec4 --- /dev/null +++ b/pyfpdb/regression-test-files/summaries/FTP/NLHE-USD-SnG-6man-201101.Hero.cashes.txt @@ -0,0 +1,16 @@ +Full Tilt Poker Tournament Summary $2 + $0.25 Sit & Go (214900008) Hold'em No Limit +Buy-In: $2 + $0.25 +Buy-In Chips: 1500 +6 Entries +Total Prize Pool: $12 +Tournament started: 2011/01/27 07:21:00 ET +Tournament finished: 2011/01/27 08:38:00 ET + +1: Hero, $7.80 +2: Player2, $4.20 +3: Player3 +4: Player4 +5: Player5 +6: Player6 +Hero finished in 1st place + diff --git a/pyfpdb/regression-test-files/summaries/FTP/NLHE-USD-SnG-9man-201101.Hero.cashes.txt b/pyfpdb/regression-test-files/summaries/FTP/NLHE-USD-SnG-9man-201101.Hero.cashes.txt new file mode 100644 index 00000000..2be04af1 --- /dev/null +++ b/pyfpdb/regression-test-files/summaries/FTP/NLHE-USD-SnG-9man-201101.Hero.cashes.txt @@ -0,0 +1,18 @@ +Full Tilt Poker Tournament Summary $2 + $0.25 Sit & Go (214976900) Hold'em No Limit +Buy-In: $2 + $0.25 +Buy-In Chips: 1500 +9 Entries +Total Prize Pool: $18 +Tournament started: 2011/01/27 14:35:00 ET +Tournament finished: 2011/01/27 15:25:00 ET + +1: Player1, $9 +2: Player2, $5.40 +3: Hero, $3.60 +4: Player4 +5: Player5 +6: Player6 +7: Player7 +8: Player8 +9: Player9 +Hero finished in 3rd place