rushnotes: initial commit (for testing and eval only)

This commit is contained in:
gimick 2010-12-30 23:49:39 +00:00
parent 744e5292ef
commit 36e2ef3f35
3 changed files with 504 additions and 0 deletions

216
pyfpdb/RushNotesAux.py Normal file
View File

@ -0,0 +1,216 @@
#!/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 <http://www.gnu.org/licenses/>.
#In the "official" distribution you can find the license in agpl-3.0.txt.
########################################################################
##########for each hand processed, attempts to update hero.xml player notes for FullTilt
##########based upon the AW howto notes written by Ray E. Barker (nutomatic) at fpdb.sourceforge.net
#to do
### think about seeding
### multiple huds firing at the same xml
### same player / two levels / only one xml
### http://www.faqs.org/docs/diveintopython/kgp_search.html
#debugmode will write logfiles for the __init__ and update_data methods
debugmode = False
# Standard Library modules
import os
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
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
notepath = r"/home/steve/.wine/drive_c/Program Files/Full Tilt Poker/"
self.heroid = self.hud.db_connection.get_player_id(self.config, sitename, heroname)
self.notefile = notepath + "/" + heroname + ".xml"
#
# 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()
# Create the minidom document
# Create the <wml> base element
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 (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(self.hud.table.name+"\n")
debugfile.write(str(self.hud.stat_dict.keys())+"\n")
if self.hud.table.name not in {"Mach 10", "Lightning", "Celerity", "Flash", "Zoom"}
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] + " ")
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] + " ")
xmlqueuedict[playername] = "~fpdb~" + n + vpip + pfr + three_B + cbet + steal + ffreq1 + agg_freq + BBper100 + "~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()

122
pyfpdb/RushNotesMerge.py Executable file
View File

@ -0,0 +1,122 @@
#!/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/steve/.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 <http://www.gnu.org/licenses/>.
#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
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"
quit()
if not os.path.isfile(sys.argv[1]):
print "Hero notes file not found, quitting"
quit()
if not os.path.isfile((sys.argv[1]+".queue")):
print "Nothing queued, quitting"
quit()
print "reading from: ", sys.argv[1]
print "merging 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 "new file has been written to: ", sys.argv[1]+".merged"
print "number in queue: ", statqueue
print "existing players updated: ", statupdated
print "new players added: ", statadded

166
pyfpdb/RushNotesReadMe.txt Normal file
View File

@ -0,0 +1,166 @@
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.
Existing ftp notes _SHOULD_ be preserved, but this isn't guaranteed, you have been warned
Existing colour codings should be preserved, this process should not change colourcodings.
Copies of the live ftp notes file are preserved everytime RushNotesAux is started, just in case.
The AW is hard-coded with just the table names of Micro Rush Poker, and should ignore all other hands.
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:
-----------------------
<aw class="RushNotes" module="RushNotesAux" name="Rush1"> </aw>
<game aux="Rush1" cols="3" db="fpdb" game_name="holdem" rows="3">
or whatever works for you.
Start Autoimport, and rearrange the on-screen stats out of the way
(killing the HUD kills the AW updates)
Play some poker
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)
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, then thats great.
As I find bugs and make improvements, I will push to the git branch.
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.
Stopping and starting the HUD will erase the previously created notes holding 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 will
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.