diff --git a/packaging/windows/py2exeWalkthroughPython26.txt b/packaging/windows/py2exeWalkthroughPython26.txt index 7ad865c0..2762699e 100644 --- a/packaging/windows/py2exeWalkthroughPython26.txt +++ b/packaging/windows/py2exeWalkthroughPython26.txt @@ -33,6 +33,7 @@ psycopg2 ... http://www.stickpeople.com/projects/python/win-psycopg/psycopg2-2.2 (py)pokereval v138 ... http://sourceforge.net/projects/fpdb/files/fpdb/pypoker-eval-win32/pokereval-138.win32.exe/download (Note: There are no official windows builds, this installer is built from source. A walkthrough is in the same directory as this walkthrough. + 1.2/ MySQL Install the following file: @@ -67,6 +68,26 @@ with this line: 1.3.4/ Save and exit +1.4/ Patch py2exe to stop popup runtime error message + +see http://www.py2exe.org/index.cgi/StderrLog for technical info. + +1.4.1/ + +dos> write C:\Python26\Lib\site-packages\py2exe\boot_common.py + +replace: + atexit.register(alert, 0, + "See the logfile '%s' for details" % fname, + "Errors occurred") +with: + #atexit.register(alert, 0, + # "See the logfile '%s' for details" % fname, + # "Errors occurred") + +1.4.2/ save and exit + + Step 2 Setup GTK ----------------- diff --git a/pyfpdb/GuiRingPlayerStats.py b/pyfpdb/GuiRingPlayerStats.py index 86ba1aad..f061322e 100644 --- a/pyfpdb/GuiRingPlayerStats.py +++ b/pyfpdb/GuiRingPlayerStats.py @@ -58,7 +58,7 @@ onlinehelp = {'Game':_('Type of Game'), 'Saw_F':_('% Saw Flop vs hands dealt'), 'SawSD':_('Saw Show Down / River'), 'WtSDwsF':_('Went To Show Down When Saw Flop'), - 'W$SD':_('Amount Won when Show Down seen'), + 'W$SD':_('% Won some money at showdown'), 'FlAFq':_('Flop Aggression\n% Bet or Raise after seeing Flop'), 'TuAFq':_('Turn Aggression\n% Bet or Raise after seeing Turn'), 'RvAFq':_('River Aggression\n% Bet or Raise after seeing River'), diff --git a/pyfpdb/RushNotesAux.py b/pyfpdb/RushNotesAux.py new file mode 100644 index 00000000..312de6d5 --- /dev/null +++ b/pyfpdb/RushNotesAux.py @@ -0,0 +1,333 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""RushNotesAux.py + + EXPERIMENTAL - USE WITH CARE + +Auxilliary process to push HUD data into the FullTilt player notes XML +This will allow a rudimentary "HUD" in rush games + +The existing notes file will be altered by this function +""" +# Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net +# +#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. + +######################################################################## + +##########for each hand processed, attempts to create update for player notes in FullTilt +##########based upon the AW howto notes written by Ray E. Barker (nutomatic) at fpdb.sourceforge.net +##########Huge thanks to Ray for his guidance and encouragement to create this !! + +# +#debugmode will write logfiles for the __init__ and update_data methods +# writes into ./pyfpdb/~Rushdebug.* +# +debugmode = False + +# Standard Library modules +import os +import sys +from xml.dom import minidom +from datetime import datetime +from time import * + +# FreePokerDatabase modules +from Mucked import Aux_Window +from Mucked import Seat_Window +from Mucked import Aux_Seats +import Stats +import Card + +# +# overload minidom methods to fix bug where \n is parsed as " ". +# described here: http://bugs.python.org/issue7139 +# + +def _write_data(writer, data, isAttrib=False): + "Writes datachars to writer." + if isAttrib: + data = data.replace("\r", " ").replace("\n", " ") + data = data.replace("\t", " ") + writer.write(data) +minidom._write_data = _write_data + +def writexml(self, writer, indent="", addindent="", newl=""): + # indent = current indentation + # addindent = indentation to add to higher levels + # newl = newline string + writer.write(indent+"<" + self.tagName) + + attrs = self._get_attributes() + a_names = attrs.keys() + a_names.sort() + + for a_name in a_names: + writer.write(" %s=\"" % a_name) + _write_data(writer, attrs[a_name].value, isAttrib=True) + writer.write("\"") + if self.childNodes: + writer.write(">%s"%(newl)) + for node in self.childNodes: + node.writexml(writer,indent+addindent,addindent,newl) + writer.write("%s%s" % (indent,self.tagName,newl)) + else: + writer.write("/>%s"%(newl)) +# For an introduction to overriding instance methods, see +# http://irrepupavel.com/documents/python/instancemethod/ +instancemethod = type(minidom.Element.writexml) +minidom.Element.writexml = instancemethod( + writexml, None, minidom.Element) + + + +class RushNotes(Aux_Window): + + def __init__(self, hud, config, params): + + self.hud = hud + self.config = config + + # + # following line makes all the site params magically available (thanks Ray!) + # + site_params_dict = self.hud.config.get_site_parameters(self.hud.site) + + heroname = site_params_dict['screen_name'] + sitename = site_params_dict['site_name'] + 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") + + if not os.path.isfile(self.notefile): + self.active = False + return + else: + self.active = True + + # + # read in existing notefile and backup with date/time in name + # todo change to not use dom + # + now = datetime.now() + notefilebackup = self.notefile + ".backup." + now.strftime("%Y%m%d%H%M%S") + xmlnotefile = minidom.parse(self.notefile) + outputfile = open(notefilebackup, 'w') + xmlnotefile.writexml(outputfile) + outputfile.close() + xmlnotefile.unlink + + # + # Create a fresh queue file with skeleton XML + # + self.queuefile = self.notefile + ".queue" + queuedom = minidom.Document() + + pld=queuedom.createElement("PLAYERDATA") + queuedom.appendChild(pld) + + nts=queuedom.createElement("NOTES") + pld.appendChild(nts) + + 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.write("conf="+str(config)+"\n") + debugfile.write("spdi="+str(site_params_dict)+"\n") + debugfile.write("para="+str(params)+"\n") + debugfile.write("hero="+heroname+" "+str(self.heroid)+"\n") + debugfile.write("back="+notefilebackup+"\n") + 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 + # self.hud.stat_dict contains the stats information for this hand + + if not self.active: + return + + if (debugmode): + debugfile=open("~Rushdebug.data", "a") + debugfile.write(new_hand_id+"\n") + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+ " update_data begins"+ "\n") + debugfile.write("hero="+str(self.heroid)+"\n") + #debugfile.write(str(self.hud.stat_dict)+"\n") + debugfile.write("table="+self.hud.table.name+"\n") + debugfile.write("players="+str(self.hud.stat_dict.keys())+"\n") + debugfile.write("db="+str(db_connection)+"\n") + + if self.hud.table.name not in self.rushtables: + return + # + # Grab a list of player id's + # + handplayers = self.hud.stat_dict.keys() + + # + # build a dictionary of stats text for each player in the hand (excluding the hero) + # xmlqueuedict contains {playername : stats text} + # + xmlqueuedict = {} + for playerid in handplayers: + # ignore hero, no notes available for hero at Full Tilt + if playerid == self.heroid: continue + + playername=unicode(str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'playername')[1])) + # Use index[3] which is a short description + n=str(Stats.do_stat(self.hud.stat_dict, player = playerid, stat = 'n')[3] + " ") + 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] + " ") + 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] + " ") + + 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 + # sort by hand strength. Output will show position too, + # so KK.1 is KK from late posn etc. + # ignore non-rush hands (check against known rushtablenames) + # cards decoding is hard-coded for holdem, so that's tuff atm + # three categories of known hands are shown: + # agression preflop hands + # non-aggression preflop hands + # bigblind called to defend hands + # + # This isn't perfect, but it isn't too bad a starting point + # + + PFcall="PFcall" + PFaggr="PFaggr" + PFdefend="PFdefend" + + c = db_connection.get_cursor() + c.execute(("SELECT handId, position, startCards, street0Aggr, tableName " + + "FROM hands, handsPlayers " + + "WHERE handsplayers.handId = hands.id " + + "AND street0VPI = 1 " + + "AND startCards > 0 " + + "AND playerId = %d " + + "ORDER BY startCards DESC " + + ";") + % int(playerid)) + + for (qid, qposition, qstartcards, qstreet0Aggr, qtablename) in c.fetchall(): + if (debugmode): + debugfile.write("pid, hid, pos, cards, aggr, table player"+ + str(playerid)+"/"+str(qid)+"/"+str(qposition)+"/"+ + str(qstartcards)+"/"+str(qstreet0Aggr)+"/"+ + str(qtablename)+"/"+str(playername)+ + "\n") + + humancards = Card.decodeStartHandValue("holdem", qstartcards) + + if qtablename not in self.rushtables: + pass + elif qposition == "B" and qstreet0Aggr == False: + PFdefend=PFdefend+"/"+humancards + elif qstreet0Aggr == True: + PFaggr=PFaggr+"/"+humancards+"."+qposition + else: + PFcall=PFcall+"/"+humancards+"."+qposition + c.close + + # + # build up final text package (top/tail with ~fpdb~ ~ends~ + # for later search/replace by Merge module + # + xmlqueuedict[playername] = ("~fpdb~" + "\n" + + n + vpip + pfr + three_B + fbbsteal + "\n" + + steal + cbet + ffreq1 + "\n" + + agg_freq + BBper100 + "\n" + + PFcall+"\n"+PFaggr+"\n"+PFdefend +"\n" + "~ends~") + + if (debugmode): + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+" villain data has been processed" + "\n") + debugfile.write(str(xmlqueuedict)+"\n") + + # + # delaying processing of xml until now. Grab current queuefile contents and + # read each existing NOTE element in turn, if matched to a player in xmlqueuedict + # update their text in the xml and delete the dictionary item + # + xmlnotefile = minidom.parse(self.queuefile) + notelist = xmlnotefile.getElementsByTagName('NOTE') + + for noteentry in notelist: #for each note in turn + noteplayer = noteentry.getAttribute("PlayerId") #extract the playername from xml + if noteplayer in xmlqueuedict: # does that player exist in the queue? + noteentry.setAttribute("Text",xmlqueuedict[noteplayer]) + del xmlqueuedict[noteplayer] #remove from list, does not need to be added later on + + # + #create entries for new players (those remaining in the dictionary) + # + if len(xmlqueuedict) > 0: + playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list) + notesnode=playerdata.childNodes[0] #Find NOTES node + + for newplayer in xmlqueuedict: + newentry = xmlnotefile.createElement("NOTE") + newentry.setAttribute("PlayerId", newplayer) + newentry.setAttribute("Text", xmlqueuedict[newplayer]) + notesnode.insertBefore(newentry,None) + newentry = xmlnotefile.createTextNode("\n") + notesnode.insertBefore(newentry,None) + + if (debugmode): + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+" xml pre-processing complete"+ "\n") + + # + # OverWrite existing xml file with updated DOM and cleanup + # + updatednotes = open(self.queuefile, 'w') + xmlnotefile.writexml(updatednotes) + updatednotes.close() + + xmlnotefile.unlink + + if (debugmode): + now = datetime.now() + debugfile.write(now.strftime("%Y%m%d%H%M%S")+" dom written, process finished"+ "\n") + debugfile.close() + + + + + diff --git a/pyfpdb/RushNotesMerge.py b/pyfpdb/RushNotesMerge.py new file mode 100755 index 00000000..7bfec3df --- /dev/null +++ b/pyfpdb/RushNotesMerge.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""RushNotesMerge.py + + EXPERIMENTAL - USE WITH CARE + +Merge .queue file with hero's note to generate fresh .merge file + +normal usage +$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/heroname.xml" + +The generated file can then replace heroname.xml (if all is well). + + +""" +# Copyright 2010, "Gimick" of the FPDB project fpdb.sourceforge.net +# +#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. + +######################################################################## + + +# Standard Library modules +import os +import sys +from xml.dom import minidom + +# +# overload minidom methods to fix bug where \n is parsed as " ". +# described here: http://bugs.python.org/issue7139 +# + +def _write_data(writer, data, isAttrib=False): + "Writes datachars to writer." + if isAttrib: + data = data.replace("\r", " ").replace("\n", " ") + data = data.replace("\t", " ") + writer.write(data) +minidom._write_data = _write_data + +def writexml(self, writer, indent="", addindent="", newl=""): + # indent = current indentation + # addindent = indentation to add to higher levels + # newl = newline string + writer.write(indent+"<" + self.tagName) + + attrs = self._get_attributes() + a_names = attrs.keys() + a_names.sort() + + for a_name in a_names: + writer.write(" %s=\"" % a_name) + _write_data(writer, attrs[a_name].value, isAttrib=True) + writer.write("\"") + if self.childNodes: + writer.write(">%s"%(newl)) + for node in self.childNodes: + node.writexml(writer,indent+addindent,addindent,newl) + writer.write("%s%s" % (indent,self.tagName,newl)) + else: + writer.write("/>%s"%(newl)) +# For an introduction to overriding instance methods, see +# http://irrepupavel.com/documents/python/instancemethod/ +instancemethod = type(minidom.Element.writexml) +minidom.Element.writexml = instancemethod( + writexml, None, minidom.Element) + + + +statqueue=0 +statupdated=0 +statadded=0 + +def cleannote(textin): + if textin.find("~fpdb~") == -1: return textin + if textin.find("~ends~") == -1: return textin + if textin.find("~fpdb~") > textin.find("~ends~"): return textin + return textin[0:textin.find("~fpdb~")] + textin[textin.find("~ends~")+6:] +# get out now if parameter not passed +try: + sys.argv[1] <> "" +except: + print "A parameter is required, quitting now" + print "normal usage is something like:" + print '$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myhero.xml"' + quit() + +if not os.path.isfile(sys.argv[1]): + print "Hero notes file not found, quitting" + print "normal usage is something like:" + print '$> ./pyfpdb/RushNotesMerge.py "/home/foo/.wine/drive_c/Program Files/Full Tilt Poker/myhero.xml"' + quit() + +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 "***************************************************************" +print "IMPORTANT: *** Before running this merge: ***" +print "Closedown the FullTiltClient and wait for it to completely stop" +print "If FullTiltClient was running, run the merge again once it" +print "has stopped completely" +print "***************************************************************" +print +print "read from: ", sys.argv[1] +print "merge with: ", sys.argv[1]+".queue" + +#read queue and turn into a dict +queuedict = {} +xmlqueue = minidom.parse(sys.argv[1]+".queue") +notelist = xmlqueue.getElementsByTagName('NOTE') + +for noteentry in notelist: + noteplayer = noteentry.getAttribute("PlayerId") + notetext = noteentry.getAttribute("Text") + queuedict[noteplayer] = notetext + statqueue = statqueue + 1 + +#read existing player note file + +xmlnotefile = minidom.parse(sys.argv[1]) +notelist = xmlnotefile.getElementsByTagName('NOTE') + +# +#for existing players, empty out existing fpdbtext and refill +# +for noteentry in notelist: + noteplayer = noteentry.getAttribute("PlayerId") + if noteplayer in queuedict: + existingnote = noteentry.getAttribute("Text") + newnote=cleannote(existingnote) + newnote = newnote + queuedict[noteplayer] + noteentry.setAttribute("Text",newnote) + statupdated = statupdated + 1 + del queuedict[noteplayer] + +# +#create entries for new players (those remaining in the dictionary) +# +if len(queuedict) > 0: + playerdata=xmlnotefile.lastChild #move to the PLAYERDATA node (assume last one in the list) + notesnode=playerdata.childNodes[1] #Find NOTES node + +for newplayer in queuedict: + newentry = xmlnotefile.createElement("NOTE") + newentry.setAttribute("PlayerId", newplayer) + newentry.setAttribute("Text", queuedict[newplayer]) + notesnode.insertBefore(newentry,None) + newentry = xmlnotefile.createTextNode("\n") + notesnode.insertBefore(newentry,None) + statadded=statadded+1 + +#print xmlnotefile.toprettyxml() + +mergednotes = open(sys.argv[1]+".merged", 'w') +xmlnotefile.writexml(mergednotes) +mergednotes.close() + +xmlnotefile.unlink + +print "Merged file has been written to: ", sys.argv[1]+".merged" +print "" +print "number in queue: ", statqueue +print "existing players updated: ", statupdated +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 "2 Rename the .merged file to become the new notes file" diff --git a/pyfpdb/RushNotesReadMe.txt b/pyfpdb/RushNotesReadMe.txt new file mode 100644 index 00000000..43475536 --- /dev/null +++ b/pyfpdb/RushNotesReadMe.txt @@ -0,0 +1,194 @@ +aux to write fpdb data to player notes on Full Tilt +--------------------------------------------------- + +by Gimick 30th Dec 2010 + +RushNotesAux - auxillary processed attached to the full tilt hud + builds up fpdb notes "queue" for each villain met while the autoimport is running + uses HUD aggregation stats to do this + +RushNotesMerge - stand alone process to merge the existing ftp notes, together with queue + produced by Aux. + the output file can then be renamed to become the new ftp notes file + +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! + +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) + +The AW is hard-coded with just the table names of Micro Rush Poker, + and should ignore all other hands. + +What might not work? +-------------------- + +This isn't tested with Windows, and probably won't work, feedback welcome. +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! + +Getting started: +--------------- + +1. Set the Hero aggregation to alltime. hero_stat_range="A" + This overcomes a sqlite "bug" which has nothing to do with auxillary windows + not doing this will slow processing down to about 1 hand per minute. + +2. Set the site_path to be the folder containing the FTP notes xml file +(on wine this is normally site_path="/home/blah/.wine/Program Files/Full Tilt Poker/") + + +Wire-up the aux process: +----------------------- + + + + +or whatever works for you. + + +Play some poker +--------------- + +Start Autoimport, and rearrange the on-screen stats out of the way + (the full HUD must run, killing the HUD kills the AW updates) + +Play whatever you want + +Stop the autoimport + +Exit the Full tilt poker client (ensure it has fully stopped with ps -A) + +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) + + +Since the updates aren't real time, it would be ok to play the rush + session with fpdb inactive, but before quitting any of the tables, + start the HUD and wait for it to catch-up processing all the hands played. + + +Summary +------- + +This is very rough and ready, but it does what I set-out to achieve. + +All feedback welcome, and if this is useful as a basis for general notes + processing in future, then thats great. + +As I find bugs and make improvements, I will push to git. + + +Much more information below: +---------------------------- + +Background +---------- + +When playing rush poker, some sort of rudimentary HUD would answer simple questions +like "is this allin overbet being made by a nit, or a maniac". Although some +notes may have been made previously, some statistics would help to backup the decision. + +Currently fpdb cannot support rush because the HUD is always 1 hand or more +behind the current action. + +The only way to do this at the moment is to switch to GuiPlayerStats and make a quick +enquiry by player name. However, this effectively times you out of all other +action if multitabling. + +Full Tilt situation +------------------- + +Full Tilt notes are stored in xml format ("hero.xml"). Previously these could +be updated while the game was in progress, however, FullTilt now cache the +notes and write them out when the application exits. This makes it impossible +to use the notes as a real-time HUD, and therefore real-time huds are now +forced to screen-scrape or poke around in the client memory. + +Accepting this a limitation, this implementation updates the notes only once +the FullTilt client has been closed. Obviously, the villain HUD stats are only +as at the end of the last session, however, it is hoped this is significantly +better than having nothing at all. As the hero's hand history increases, the +notes should progressively mature in accuracy. + +Preamble +-------- + +Note that this implementation was written purely to be "good enough" to work +for the author, and is not intended as package or production quality. It +is contributed as a starting point for others, or for experimental use. + +Thanks to Ray Barker who gave a great deal of help throughout. + + +The implementation +------------------- + +RushNotesAux is an fpdb auxilliary process, and is called for every hand +processed by autoimport. Each villain has a note prepared based on the current +fpdb data, and this note (in XML format) is stored in a queue file. + +Auxilliary windows were chosen because +a) the author has limited fpdb and programming skill +b) the auxillary windows handler is well documented and supported +c) any code created has access to the full range of stats with little or no extra work +d) runs within the HUD, so the aggregation parameters are already available + + +Limitations +----------- + +The notes are only regenerated if a hand is played against the villain. The +process does not "bulk load" notes based upon all the player stats in FPDB. + +It is hoped that due to the relatively large hand volume and relatively small + player pools, this limitation will be largely overcome after a few sessions +although there will obviously be a number of players with no fpdb note. + +The aggregation parameters used for the notes are based upon the HUD parameters. + (with the exception of the hand-ranges, which uses its' own criteria (see source) + +Stopping and starting the HUD will erase the previously created notes queue file. + +The HUD must run, so the individual player popups need to be manually relocated. + +Although hard-coded for micro RUSH tablenames, the auxilliary window could +probably happily update notes of all cash and tournament players. + +Process overview +---------------- + +1/ The HUD process is started. +1.1/ when the first hand is received, h fresh holding file is created, 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 +and key stats are formatted in xml-style and written out to a holding file. +4/ At the end of the session, the HUD is stopped and the poker client closed + +5/ The user can then review the contents of the holding file. +6/ A process is begun to "merge" the holding file into the existing player notes +7/ A new "merged" file is created. The process attempts to preserve any +existing notes, but this cannot be guaranteed. +8/ The user can review this merged file, and if they are happy, +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. +