commit 276f6ba262e36ded2728a0bb4eff849d36b2633b Author: Ray Date: Tue Sep 23 10:17:53 2008 -0400 Initial load of git repo. diff --git a/Configuration.py b/Configuration.py new file mode 100644 index 00000000..55b8ed2b --- /dev/null +++ b/Configuration.py @@ -0,0 +1,278 @@ +#!/usr/bin/env python +"""Configuration.py + +Handles HUD configuration files. +""" +# Copyright 2008, Ray E. Barker + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import shutil +import xml.dom.minidom +from xml.dom.minidom import Node + +class Layout: + def __init__(self, max): + self.max = int(max) + self.location = [] + for i in range(self.max + 1): self.location.append(None) + + def __str__(self): + temp = " Layout = %d max, width= %d, height = %d, fav_seat = %d\n" % (self.max, self.width, self.height, self.fav_seat) + temp = temp + " Locations = " + for i in range(1, len(self.location)): + temp = temp + "(%d,%d)" % self.location[i] + + return temp + "\n" + +class Site: + def __init__(self, node): + self.site_name = node.getAttribute("site_name") + self.table_finder = node.getAttribute("table_finder") + self.screen_name = node.getAttribute("screen_name") + self.site_path = node.getAttribute("site_path") + self.HH_path = node.getAttribute("HH_path") + self.decoder = node.getAttribute("decoder") + self.layout = {} + + for layout_node in node.getElementsByTagName('layout'): + max = int( layout_node.getAttribute('max') ) + lo = Layout(max) + lo.fav_seat = int( layout_node.getAttribute('fav_seat') ) + lo.width = int( layout_node.getAttribute('width') ) + lo.height = int( layout_node.getAttribute('height') ) + + for location_node in layout_node.getElementsByTagName('location'): + lo.location[int( location_node.getAttribute('seat') )] = (int( location_node.getAttribute('x') ), int( location_node.getAttribute('y'))) + + self.layout[lo.max] = lo + + def __str__(self): + temp = "Site = " + self.site_name + "\n" + for key in dir(self): + if key.startswith('__'): continue + if key == 'layout': continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + + for layout in self.layout: + temp = temp + "%s" % self.layout[layout] + + return temp + +class Stat: + def __init__(self): + pass + + def __str__(self): + temp = " stat_name = %s, row = %d, col = %d, tip = %s, click = %s, popup = %s\n" % (self.stat_name, self.row, self.col, self.tip, self.click, self.popup) + return temp + +class Game: + def __init__(self, node): + self.game_name = node.getAttribute("game_name") + self.db = node.getAttribute("db") + self.rows = int( node.getAttribute("rows") ) + self.cols = int( node.getAttribute("cols") ) + + self.stats = {} + for stat_node in node.getElementsByTagName('stat'): + stat = Stat() + stat.stat_name = stat_node.getAttribute("stat_name") + stat.row = int( stat_node.getAttribute("row") ) + stat.col = int( stat_node.getAttribute("col") ) + stat.tip = stat_node.getAttribute("tip") + stat.click = stat_node.getAttribute("click") + stat.popup = stat_node.getAttribute("popup") + + self.stats[stat.stat_name] = stat + + def __str__(self): + temp = "Game = " + self.game_name + "\n" + temp = temp + " db = %s\n" % self.db + temp = temp + " rows = %d\n" % self.rows + temp = temp + " cols = %d\n" % self.cols + + for stat in self.stats.keys(): + temp = temp + "%s" % self.stats[stat] + + return temp + +class Database: + def __init__(self, node): + self.db_name = node.getAttribute("db_name") + self.db_server = node.getAttribute("db_server") + self.db_ip = node.getAttribute("db_ip") + self.db_user = node.getAttribute("db_user") + self.db_type = node.getAttribute("db_type") + self.db_pass = node.getAttribute("db_pass") + + def __str__(self): + temp = 'Database = ' + self.db_name + '\n' + for key in dir(self): + if key.startswith('__'): continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + return temp + +class Mucked: + def __init__(self, node): + self.name = node.getAttribute("mw_name") + self.cards = node.getAttribute("deck") + self.card_wd = node.getAttribute("card_wd") + self.card_ht = node.getAttribute("card_ht") + self.rows = node.getAttribute("rows") + self.cols = node.getAttribute("cols") + self.format = node.getAttribute("stud") + + def __str__(self): + temp = 'Mucked = ' + self.name + "\n" + for key in dir(self): + if key.startswith('__'): continue + value = getattr(self, key) + if callable(value): continue + temp = temp + ' ' + key + " = " + value + "\n" + return temp + +class Popup: + def __init__(self, node): + self.name = node.getAttribute("pu_name") + self.pu_stats = [] + for stat_node in node.getElementsByTagName('pu_stat'): + self.pu_stats.append(stat_node.getAttribute("pu_stat_name")) + + def __str__(self): + temp = "Popup = " + self.name + "\n" + for stat in self.pu_stats: + temp = temp + " " + stat + return temp + "\n" + +class Config: + def __init__(self, file = 'HUD_config.xml'): + + doc = xml.dom.minidom.parse(file) + + self.doc = doc + self.file = file + self.supported_sites = {} + self.supported_games = {} + self.supported_databases = {} + self.mucked_windows = {} + self.popup_windows = {} + +# s_sites = doc.getElementsByTagName("supported_sites") + for site_node in doc.getElementsByTagName("site"): + site = Site(node = site_node) + self.supported_sites[site.site_name] = site + + s_games = doc.getElementsByTagName("supported_games") + for game_node in doc.getElementsByTagName("game"): + game = Game(node = game_node) + self.supported_games[game.game_name] = game + + s_dbs = doc.getElementsByTagName("supported_databases") + for db_node in doc.getElementsByTagName("database"): + db = Database(node = db_node) + self.supported_databases[db.db_name] = db + + s_dbs = doc.getElementsByTagName("mucked_windows") + for mw_node in doc.getElementsByTagName("mw"): + mw = Mucked(node = mw_node) + self.mucked_windows[mw.name] = mw + + s_dbs = doc.getElementsByTagName("popup_windows") + for pu_node in doc.getElementsByTagName("pu"): + pu = Popup(node = pu_node) + self.popup_windows[pu.name] = pu + + def get_site_node(self, site): + for site_node in self.doc.getElementsByTagName("site"): + if site_node.getAttribute("site_name") == site: + return site_node + + def get_layout_node(self, site_node, layout): + for layout_node in site_node.getElementsByTagName("layout"): + if int( layout_node.getAttribute("max") ) == int( layout ): + return layout_node + + def get_location_node(self, layout_node, seat): + for location_node in layout_node.getElementsByTagName("location"): + if int( location_node.getAttribute("seat") ) == int( seat ): + return location_node + + def save(self, file = None): + if not file == None: + f = open(file, 'w') + self.doc.writexml(f) + f.close() + else: + shutil.move(self.file, self.file+".backup") + f = open(self.file, 'w') + self.doc.writexml(f) + f.close + + def edit_layout(self, site_name, max, width = None, height = None, + fav_seat = None, locations = None): + site_node = self.get_site_node(site_name) + layout_node = self.get_layout_node(site_node, max) + for i in range(1, max + 1): + location_node = self.get_location_node(layout_node, i) + location_node.setAttribute("x", str( locations[i-1][0] )) + location_node.setAttribute("y", str( locations[i-1][1] )) + self.supported_sites[site_name].layout[max].location[i] = ( locations[i-1][0], locations[i-1][1] ) + +if __name__== "__main__": + c = Config() + + print "\n----------- SUPPORTED SITES -----------" + for s in c.supported_sites.keys(): + print c.supported_sites[s] + + print "----------- END SUPPORTED SITES -----------" + + + print "\n----------- SUPPORTED GAMES -----------" + for game in c.supported_games.keys(): + print c.supported_games[game] + + print "----------- END SUPPORTED GAMES -----------" + + + print "\n----------- SUPPORTED DATABASES -----------" + for db in c.supported_databases.keys(): + print c.supported_databases[db] + + print "----------- END SUPPORTED DATABASES -----------" + + print "\n----------- MUCKED WINDOW FORMATS -----------" + for w in c.mucked_windows.keys(): + print c.mucked_windows[w] + + print "----------- END MUCKED WINDOW FORMATS -----------" + + print "\n----------- POPUP WINDOW FORMATS -----------" + for w in c.popup_windows.keys(): + print c.popup_windows[w] + + print "----------- END MUCKED WINDOW FORMATS -----------" + + c.edit_layout("PokerStars", 6, locations=( (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6) )) + c.save(file="testout.xml") \ No newline at end of file diff --git a/Database.py b/Database.py new file mode 100644 index 00000000..d634e134 --- /dev/null +++ b/Database.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python +"""Database.py + +Create and manage the database objects. +""" +# Copyright 2008, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# postmaster -D /var/lib/pgsql/data + +# Standard Library modules +import sys + +# pyGTK modules + +# FreePokerTools modules +import Configuration +import SQL + +# pgdb database module for posgres via DB-API +import psycopg2 +# pgdb uses pyformat. is that fixed or an option? + +# mysql bindings +import MySQLdb + +class Database: + def __init__(self, c, db_name, game): + if c.supported_databases[db_name].db_server == 'postgresql': + self.connection = psycopg2.connect(host = c.supported_databases[db_name].db_ip, + user = c.supported_databases[db_name].db_user, + password = c.supported_databases[db_name].db_pass, + database = c.supported_databases[db_name].db_name) + + elif c.supported_databases[db_name].db_server == 'mysql': + self.connection = MySQLdb.connect(host = c.supported_databases[db_name].db_ip, + user = c.supported_databases[db_name].db_user, + passwd = c.supported_databases[db_name].db_pass, + db = c.supported_databases[db_name].db_name) + + else: + print "Database not recognized." + return(0) + + self.type = c.supported_databases[db_name].db_type + self.sql = SQL.Sql(game = game, type = self.type) + + def close_connection(self): + self.connection.close() + + def get_table_name(self, hand_id): + c = self.connection.cursor() + c.execute(self.sql.query['get_table_name'], (hand_id, )) + row = c.fetchone() + return row + + def get_last_hand(self): + c = self.connection.cursor() + c.execute(self.sql.query['get_last_hand']) + row = c.fetchone() + return row[0] + + def get_xml(self, hand_id): + c = self.connection.cursor() + c.execute(self.sql.query['get_xml'], (hand_id)) + row = c.fetchone() + return row[0] + + def get_recent_hands(self, last_hand): + c = self.connection.cursor() + c.execute(self.sql.query['get_recent_hands'], {'last_hand': last_hand}) + return c.fetchall() + + def get_hand_info(self, new_hand_id): + c = self.connection.cursor() + c.execute(self.sql.query['get_hand_info'], new_hand_id) + return c.fetchall() + +# def get_cards(self, hand): +# this version is for the PTrackSv2 db +# c = self.connection.cursor() +# c.execute(self.sql.query['get_cards'], hand) +# colnames = [desc[0] for desc in c.description] +# cards = {} +# for row in c.fetchall(): +# s_dict = {} +# for name, val in zip(colnames, row): +# s_dict[name] = val +# cards[s_dict['seat_number']] = s_dict +# return (cards) + + def get_cards(self, hand): +# this version is for the fpdb db + c = self.connection.cursor() + c.execute(self.sql.query['get_cards'], hand) + colnames = [desc[0] for desc in c.description] + cards = {} + for row in c.fetchall(): + s_dict = {} + for name, val in zip(colnames, row): + s_dict[name] = val + cards[s_dict['seat_number']] = s_dict + return (cards) + + def get_stats_from_hand(self, hand, player_id = False): + c = self.connection.cursor() + + if not player_id: player_id = "%" +# get the players in the hand and their seats +# c.execute(self.sql.query['get_players_from_hand'], (hand, player_id)) + c.execute(self.sql.query['get_players_from_hand'], (hand, )) + names = {} + seats = {} + for row in c.fetchall(): + names[row[0]] = row[2] + seats[row[0]] = row[1] + +# now get the stats +# c.execute(self.sql.query['get_stats_from_hand'], (hand, hand, player_id)) + c.execute(self.sql.query['get_stats_from_hand'], (hand, hand)) + colnames = [desc[0] for desc in c.description] + stat_dict = {} + for row in c.fetchall(): + t_dict = {} + for name, val in zip(colnames, row): + t_dict[name] = val +# print t_dict + t_dict['screen_name'] = names[t_dict['player_id']] + t_dict['seat'] = seats[t_dict['player_id']] + stat_dict[t_dict['player_id']] = t_dict + return stat_dict + + def get_player_id(self, config, site, player_name): + print "site = %s, player name = %s" % (site, player_name) + c = self.connection.cursor() + c.execute(self.sql.query['get_player_id'], {'player': player_name, 'site': site}) + row = c.fetchone() + return row[0] + +if __name__=="__main__": + c = Configuration.Config() + +# db_connection = Database(c, 'fpdb', 'holdem') # mysql fpdb holdem + db_connection = Database(c, 'fpdb-p', 'test') # mysql fpdb holdem +# db_connection = Database(c, 'PTrackSv2', 'razz') # mysql razz +# db_connection = Database(c, 'ptracks', 'razz') # postgres + print "database connection object = ", db_connection.connection + print "database type = ", db_connection.type + + h = db_connection.get_last_hand() + print "last hand = ", h + + hero = db_connection.get_player_id(c, 'PokerStars', 'nutOmatic') + print "nutOmatic is id_player = %d" % hero + + stat_dict = db_connection.get_stats_from_hand(h) + for p in stat_dict.keys(): + print p, " ", stat_dict[p] + + print "nutOmatics stats:" + stat_dict = db_connection.get_stats_from_hand(h, hero) + for p in stat_dict.keys(): + print p, " ", stat_dict[p] + + db_connection.close_connection + + print "press enter to continue" + sys.stdin.readline() diff --git a/HUD_config.xml b/HUD_config.xml new file mode 100644 index 00000000..518e2af6 --- /dev/null +++ b/HUD_config.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HUD_main.py b/HUD_main.py new file mode 100755 index 00000000..15bbd034 --- /dev/null +++ b/HUD_main.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python + +"""Hud_main.py + +Main for FreePokerTools HUD. +""" +# Copyright 2008, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# to do kill window on my seat +# to do adjust for preferred seat +# to do allow window resizing +# to do hud to echo, but ignore non numbers +# to do no hud window for hero +# to do things to add to config.xml +# to do font and size +# to do bg and fg color +# to do opacity + +# Standard Library modules +import sys +import os +import thread +import Queue + +# pyGTK modules +import pygtk +import gtk +import gobject + +# FreePokerTools modules +import Configuration +import Database +import Tables +import Hud + +# global dict for keeping the huds +hud_dict = {} + +db_connection = 0; +config = 0; + +def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + +def process_new_hand(new_hand_id, db_name): +# there is a new hand_id to be processed +# read the hand_id from stdin and strip whitespace + global hud_dict + + for h in hud_dict.keys(): + if hud_dict[h].deleted: + del(hud_dict[h]) + + db_connection = Database.Database(config, db_name, 'temp') + (table_name, max, poker_game) = db_connection.get_table_name(new_hand_id) +# if a hud for this table exists, just update it + if hud_dict.has_key(table_name): + hud_dict[table_name].update(new_hand_id, db_connection, config) +# otherwise create a new hud + else: + table_windows = Tables.discover(config) + for t in table_windows.keys(): + if table_windows[t].name == table_name: + hud_dict[table_name] = Hud.Hud(table_windows[t], max, poker_game, config, db_name) + hud_dict[table_name].create(new_hand_id, config) + hud_dict[table_name].update(new_hand_id, db_connection, config) + break +# print "table name \"%s\" not identified, no hud created" % (table_name) + db_connection.close_connection() + return(1) + +def check_stdin(db_name): + try: + hand_no = dataQueue.get(block=False) + process_new_hand(hand_no, db_name) + except: + pass + + return True + +def read_stdin(source, condition, db_name): + new_hand_id = sys.stdin.readline() + process_new_hand(new_hand_id, db_name) + return True + +def producer(): # This is the thread function + while True: + hand_no = sys.stdin.readline() # reads stdin + dataQueue.put(hand_no) # and puts result on the queue + +if __name__== "__main__": + print "HUD_main starting" + + try: + db_name = sys.argv[1] + except: + db_name = 'fpdb-p' + print "Using db name = ", db_name + + config = Configuration.Config() +# db_connection = Database.Database(config, 'fpdb', 'holdem') + + if os.name == 'posix': + s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, read_stdin, db_name) + elif os.name == 'nt': + dataQueue = Queue.Queue() # shared global. infinite size + gobject.threads_init() # this is required + thread.start_new_thread(producer, ()) # starts the thread + gobject.timeout_add(1000, check_stdin, db_name) + else: + print "Sorry your operating system is not supported." + sys.exit() + + main_window = gtk.Window() + main_window.connect("destroy", destroy) + label = gtk.Label('Closing this window will exit from the HUD.') + main_window.add(label) + main_window.set_title("HUD Main Window") + main_window.show_all() + + gtk.main() diff --git a/HandHistory.py b/HandHistory.py new file mode 100644 index 00000000..dd2fa427 --- /dev/null +++ b/HandHistory.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +"""HandHistory.py + +Parses HandHistory xml files and returns requested objects. +""" +# Copyright 2008, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## +# Standard Library modules +import xml.dom.minidom +from xml.dom.minidom import Node + +class HandHistory: + def __init__(self, xml_string, elements = ('ALL')): + + doc = xml.dom.minidom.parseString(xml_string) + if elements == ('ALL'): + elements = ('BETTING', 'AWARDS', 'POSTS', 'PLAYERS', 'GAME') + + if 'BETTING' in elements: + self.BETTING = Betting(doc.getElementsByTagName('BETTING')[0]) + if 'AWARDS' in elements: + self.AWARDS = Awards (doc.getElementsByTagName('AWARDS')[0]) + if 'POSTS' in elements: + self.POSTS = Posts (doc.getElementsByTagName('POSTS')[0]) + if 'GAME' in elements: + self.GAME = Game (doc.getElementsByTagName('GAME')[0]) + if 'PLAYERS' in elements: + self.PLAYERS = {} + p_n = doc.getElementsByTagName('PLAYERS')[0] + for p in p_n.getElementsByTagName('PLAYER'): + a_player = Player(p) + self.PLAYERS[a_player.name] = a_player + +class Player: + def __init__(self, node): + self.name = node.getAttribute('NAME') + self.seat = node.getAttribute('SEAT') + self.stack = node.getAttribute('STACK') + self.showed_hand = node.getAttribute('SHOWED_HAND') + self.cards = node.getAttribute('CARDS') + self.allin = node.getAttribute('ALLIN') + self.sitting_out = node.getAttribute('SITTING_OUT') + self.hand = node.getAttribute('HAND') + self.start_cards = node.getAttribute('START_CARDS') + + if self.allin == '' or \ + self.allin == '0' or \ + self.allin.upper() == 'FALSE': self.allin = False + else: self.allin = True + + if self.sitting_out == '' or \ + self.sitting_out == '0' or \ + self.sitting_out.upper() == 'FALSE': self.sitting_out = False + else: self.sitting_out = True + + def __str__(self): + temp = "%s\n seat = %s\n stack = %s\n cards = %s\n" % \ + (self.name, self.seat, self.stack, self.cards) + temp = temp + " showed_hand = %s\n allin = %s\n" % \ + (self.showed_hand, self.allin) + temp = temp + " hand = %s\n start_cards = %s\n" % \ + (self.hand, self.start_cards) + return temp + +class Awards: + def __init__(self, node): + self.awards = [] # just an array of award objects + for a in node.getElementsByTagName('AWARD'): + self.awards.append(Award(a)) + + def __str__(self): + temp = "" + for a in self.awards: + temp = temp + "%s\n" % (a) + return temp + +class Award: + def __init__(self, node): + self.player = node.getAttribute('PLAYER') + self.amount = node.getAttribute('AMOUNT') + self.pot = node.getAttribute('POT') + + def __str__(self): + return self.player + " won " + self.amount + " from " + self.pot + +class Game: + def __init__(self, node): + print node + self.tags = {} + for tag in ( ('GAME_NAME', 'game_name'), ('MAX', 'max'), ('HIGHLOW', 'high_low'), + ('STRUCTURE', 'structure'), ('MIXED', 'mixed') ): + L = node.getElementsByTagName(tag[0]) + if (not L): continue + print L + for node2 in L: + title = "" + for node3 in node2.childNodes: + if (node3.nodeType == Node.TEXT_NODE): + title +=node3.data + self.tags[tag[1]] = title + + def __str__(self): + return "%s %s %s, (%s max), %s" % (self.tags['structure'], + self.tags['game_name'], + self.tags['game_name'], + self.tags['max'], + self.tags['game_name']) + +class Posts: + def __init__(self, node): + self.posts = [] # just an array of post objects + for p in node.getElementsByTagName('POST'): + self.posts.append(Post(p)) + + def __str__(self): + temp = "" + for p in self.posts: + temp = temp + "%s\n" % (p) + return temp + +class Post: + def __init__(self, node): + self.player = node.getAttribute('PLAYER') + self.amount = node.getAttribute('AMOUNT') + self.posted = node.getAttribute('POSTED') + self.live = node.getAttribute('LIVE') + + def __str__(self): + return ("%s posted %s %s %s") % (self.player, self.amount, self.posted, self.live) + +class Betting: + def __init__(self, node): + self.rounds = [] # a Betting object is just an array of rounds + for r in node.getElementsByTagName('ROUND'): + self.rounds.append(Round(r)) + + def __str__(self): + temp = "" + for r in self.rounds: + temp = temp + "%s\n" % (r) + return temp + +class Round: + def __init__(self, node): + self.name = node.getAttribute('ROUND_NAME') + self.action = [] + for a in node.getElementsByTagName('ACTION'): + self.action.append(Action(a)) + + def __str__(self): + temp = self.name + "\n" + for a in self.action: + temp = temp + " %s\n" % (a) + return temp + +class Action: + def __init__(self, node): + self.player = node.getAttribute('PLAYER') + self.action = node.getAttribute('ACT') + self.amount = node.getAttribute('AMOUNT') + self.allin = node.getAttribute('ALLIN') + + def __str__(self): + return self.player + " " + self.action + " " + self.amount + " " + self.allin + +if __name__== "__main__": + file = open('test.xml', 'r') + xml_string = file.read() + file.close() + + print xml_string + "\n\n\n" + h = HandHistory(xml_string, ('ALL')) + print h.GAME + print h.POSTS + print h.BETTING + print h.AWARDS + + for p in h.PLAYERS.keys(): + print h.PLAYERS[p] \ No newline at end of file diff --git a/Hud.py b/Hud.py new file mode 100755 index 00000000..debfaa67 --- /dev/null +++ b/Hud.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python +"""Hud.py + +Create and manage the hud overlays. +""" +# Copyright 2008, Ray E. Barker + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## +# Standard Library modules +import os + +# pyGTK modules +import pygtk +import gtk +import pango +import gobject + +# win32 modules -- only imported on windows systems +if os.name == 'nt': + import win32gui + import win32con + +# FreePokerTools modules +import Tables # needed for testing only +import Configuration +import Stats +import Mucked +import Database +import HUD_main + +class Hud: + + def __init__(self, table, max, poker_game, config, db_name): + self.table = table + self.config = config + self.poker_game = poker_game + self.max = max + self.db_name = db_name + self.deleted = False + + self.stat_windows = {} + self.popup_windows = {} + self.font = pango.FontDescription("Sans 8") + +# Set up a main window for this this instance of the HUD + self.main_window = gtk.Window() +# self.window.set_decorated(0) + self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC) + self.main_window.set_keep_above(1) + self.main_window.set_title(table.name) + self.main_window.connect("destroy", self.kill_hud) + + self.ebox = gtk.EventBox() + self.label = gtk.Label("Close this window to\nkill the HUD for\n %s" % (table.name)) + self.main_window.add(self.ebox) + self.ebox.add(self.label) + self.main_window.move(self.table.x, self.table.y) + +# A popup window for the main window + self.menu = gtk.Menu() + self.item1 = gtk.MenuItem('Kill this HUD') + self.menu.append(self.item1) + self.item1.connect("activate", self.kill_hud) + self.item1.show() + self.item2 = gtk.MenuItem('Save Layout') + self.menu.append(self.item2) + self.item2.connect("activate", self.save_layout) + self.item2.show() + self.ebox.connect_object("button-press-event", self.on_button_press, self.menu) + + self.main_window.show_all() +# set_keep_above(1) for windows + if os.name == 'nt': self.topify_window(self.main_window) + + def on_button_press(self, widget, event): + if event.button == 3: + widget.popup(None, None, None, event.button, event.time) + return True + return False + + def kill_hud(self, args): + for k in self.stat_windows.keys(): + self.stat_windows[k].window.destroy() + self.main_window.destroy() + self.deleted = True + + def save_layout(self, *args): + new_layout = [] + for sw in self.stat_windows: + loc = self.stat_windows[sw].window.get_position() + new_loc = (loc[0] - self.table.x, loc[1] - self.table.y) + new_layout.append(new_loc) + print new_layout + self.config.edit_layout(self.table.site, self.table.max, locations = new_layout) + self.config.save() + + def create(self, hand, config): +# update this hud, to the stats and players as of "hand" +# hand is the hand id of the most recent hand played at this table +# +# this method also manages the creating and destruction of stat +# windows via calls to the Stat_Window class + for i in range(1, self.max + 1): + (x, y) = config.supported_sites[self.table.site].layout[self.max].location[i] + self.stat_windows[i] = Stat_Window(game = config.supported_games[self.poker_game], + parent = self, + table = self.table, + x = x, + y = y, + seat = i, + player_id = 'fake', + font = self.font) + + self.stats = [] + for i in range(0, config.supported_games[self.poker_game].rows + 1): + row_list = [''] * config.supported_games[self.poker_game].cols + self.stats.append(row_list) + for stat in config.supported_games[self.poker_game].stats.keys(): + self.stats[config.supported_games[self.poker_game].stats[stat].row] \ + [config.supported_games[self.poker_game].stats[stat].col] = \ + config.supported_games[self.poker_game].stats[stat].stat_name + +# self.mucked_window = gtk.Window() +# self.m = Mucked.Mucked(self.mucked_window, self.db_connection) +# self.mucked_window.show_all() + + def update(self, hand, db, config): + self.hand = hand # this is the last hand, so it is available later + stat_dict = db.get_stats_from_hand(hand) + for s in stat_dict.keys(): + self.stat_windows[stat_dict[s]['seat']].player_id = stat_dict[s]['player_id'] + for r in range(0, config.supported_games[self.poker_game].rows): + for c in range(0, config.supported_games[self.poker_game].cols): + number = Stats.do_stat(stat_dict, player = stat_dict[s]['player_id'], stat = self.stats[r][c]) + self.stat_windows[stat_dict[s]['seat']].label[r][c].set_text(number[1]) + tip = stat_dict[s]['screen_name'] + "\n" + number[5] + "\n" + \ + number[3] + ", " + number[4] + Stats.do_tip(self.stat_windows[stat_dict[s]['seat']].e_box[r][c], tip) +# self.m.update(hand) + + def topify_window(self, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: + win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) +# notify_id = (w[0], +# 0, +# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, +# win32con.WM_USER+20, +# 0, +# '') +# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) +# + window.set_title(real_name) + +class Stat_Window: + + def button_press_cb(self, widget, event, *args): +# This handles all callbacks from button presses on the event boxes in +# the stat windows. There is a bit of an ugly kludge to separate single- +# and double-clicks. + if event.button == 1: # left button event + if event.type == gtk.gdk.BUTTON_PRESS: # left button single click + if self.sb_click > 0: return + self.sb_click = gobject.timeout_add(250, self.single_click, widget) + elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click + if self.sb_click > 0: + gobject.source_remove(self.sb_click) + self.sb_click = 0 + self.double_click(widget, event, *args) + + if event.button == 2: # middle button event + pass +# print "middle button clicked" + + if event.button == 3: # right button event + pass +# print "right button clicked" + + def single_click(self, widget): +# Callback from the timeout in the single-click finding part of the +# button press call back. This needs to be modified to get all the +# arguments from the call. +# print "left button clicked" + self.sb_click = 0 + Popup_window(widget, self) + return False + + def double_click(self, widget, event, *args): + self.toggle_decorated(widget) + + def toggle_decorated(self, widget): + top = widget.get_toplevel() + (x, y) = top.get_position() + + if top.get_decorated(): + top.set_decorated(0) + top.move(x, y) + else: + top.set_decorated(1) + top.move(x, y) + + def __init__(self, parent, game, table, seat, x, y, player_id, font): + self.parent = parent # Hud object that this stat window belongs to + self.game = game # Configuration object for the curren + self.table = table # Table object where this is going + self.x = x + table.x # table.x and y are the location of the table + self.y = y + table.y # x and y are the location relative to table.x & y + self.player_id = player_id # looks like this isn't used ;) + self.sb_click = 0 # used to figure out button clicks + + self.window = gtk.Window() + self.window.set_decorated(0) + self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) + self.window.set_keep_above(1) + self.window.set_title("%s" % seat) + self.window.set_property("skip-taskbar-hint", True) + + self.grid = gtk.Table(rows = self.game.rows, columns = self.game.cols, homogeneous = False) + self.window.add(self.grid) + + self.e_box = [] + self.frame = [] + self.label = [] + for r in range(self.game.rows): + self.e_box.append([]) + self.label.append([]) + for c in range(self.game.cols): + self.e_box[r].append( gtk.EventBox() ) + Stats.do_tip(self.e_box[r][c], 'farts') + self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) + self.label[r].append( gtk.Label('xxx') ) + self.e_box[r][c].add(self.label[r][c]) + self.e_box[r][c].connect("button_press_event", self.button_press_cb) +# font = pango.FontDescription("Sans 8") + self.label[r][c].modify_font(font) + self.window.realize + self.window.move(self.x, self.y) + self.window.show_all() +# set_keep_above(1) for windows + if os.name == 'nt': self.topify_window(self.window) + + def topify_window(self, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: + win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) +# notify_id = (w[0], +# 0, +# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, +# win32con.WM_USER+20, +# 0, +# '') +# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) +# + window.set_title(real_name) + +def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() + +class Popup_window: + def __init__(self, parent, stat_window): + self.sb_click = 0 + +# create the popup window + self.window = gtk.Window() + self.window.set_decorated(0) + self.window.set_gravity(gtk.gdk.GRAVITY_STATIC) + self.window.set_keep_above(1) + self.window.set_title("popup") + self.window.set_property("skip-taskbar-hint", True) + self.window.set_transient_for(parent.get_toplevel()) + self.window.set_position(gtk.WIN_POS_CENTER_ON_PARENT) + + self.ebox = gtk.EventBox() + self.ebox.connect("button_press_event", self.button_press_cb) + self.lab = gtk.Label("stuff\nstuff\nstuff") + +# need an event box so we can respond to clicks + self.window.add(self.ebox) + self.ebox.add(self.lab) + self.window.realize + +# figure out the row, col address of the click that activated the popup + row = 0 + col = 0 + for r in range(0, stat_window.game.rows): + for c in range(0, stat_window.game.cols): + if stat_window.e_box[r][c] == parent: + row = r + col = c + break + +# figure out what popup format we're using + popup_format = "default" + for stat in stat_window.game.stats.keys(): + if stat_window.game.stats[stat].row == row and stat_window.game.stats[stat].col == col: + popup_format = stat_window.game.stats[stat].popup + break + +# get the list of stats to be presented from the config + stat_list = [] + for w in stat_window.parent.config.popup_windows.keys(): + if w == popup_format: + stat_list = stat_window.parent.config.popup_windows[w].pu_stats + break + +# get a database connection + db_connection = Database.Database(stat_window.parent.config, stat_window.parent.db_name, 'temp') + +# calculate the stat_dict and then create the text for the pu +# stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand, stat_window.player_id) + stat_dict = db_connection.get_stats_from_hand(stat_window.parent.hand) + db_connection.close_connection() + + pu_text = "" + for s in stat_list: + number = Stats.do_stat(stat_dict, player = int(stat_window.player_id), stat = s) + pu_text += number[3] + "\n" + + self.lab.set_text(pu_text) + self.window.show_all() +# set_keep_above(1) for windows + if os.name == 'nt': self.topify_window(self.window) + + def button_press_cb(self, widget, event, *args): +# This handles all callbacks from button presses on the event boxes in +# the popup windows. There is a bit of an ugly kludge to separate single- +# and double-clicks. This is the same code as in the Stat_window class + if event.button == 1: # left button event + if event.type == gtk.gdk.BUTTON_PRESS: # left button single click + if self.sb_click > 0: return + self.sb_click = gobject.timeout_add(250, self.single_click, widget) + elif event.type == gtk.gdk._2BUTTON_PRESS: # left button double click + if self.sb_click > 0: + gobject.source_remove(self.sb_click) + self.sb_click = 0 + self.double_click(widget, event, *args) + + if event.button == 2: # middle button event + pass +# print "middle button clicked" + + if event.button == 3: # right button event + pass +# print "right button clicked" + + def single_click(self, widget): +# Callback from the timeout in the single-click finding part of the +# button press call back. This needs to be modified to get all the +# arguments from the call. + self.sb_click = 0 + self.window.destroy() + return False + + def double_click(self, widget, event, *args): + self.toggle_decorated(widget) + + def toggle_decorated(self, widget): + top = widget.get_toplevel() + (x, y) = top.get_position() + + if top.get_decorated(): + top.set_decorated(0) + top.move(x, y) + else: + top.set_decorated(1) + top.move(x, y) + + def topify_window(self, window): + """Set the specified gtk window to stayontop in MS Windows.""" + + def windowEnumerationHandler(hwnd, resultList): + '''Callback for win32gui.EnumWindows() to generate list of window handles.''' + resultList.append((hwnd, win32gui.GetWindowText(hwnd))) + + unique_name = 'unique name for finding this window' + real_name = window.get_title() + window.set_title(unique_name) + tl_windows = [] + win32gui.EnumWindows(windowEnumerationHandler, tl_windows) + + for w in tl_windows: + if w[1] == unique_name: + win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) +# notify_id = (w[0], +# 0, +# win32gui.NIF_ICON | win32gui.NIF_MESSAGE | win32gui.NIF_TIP, +# win32con.WM_USER+20, +# 0, +# '') +# win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, notify_id) +# + window.set_title(real_name) + +if __name__== "__main__": + main_window = gtk.Window() + main_window.connect("destroy", destroy) + label = gtk.Label('Fake main window, blah blah, blah\nblah, blah') + main_window.add(label) + main_window.show_all() + + c = Configuration.Config() + tables = Tables.discover(c) + db = Database.Database(c, 'fpdb', 'holdem') + + for t in tables: + win = Hud(t, 8, c, db) +# t.get_details() + win.update(8300, db, c) + + gtk.main() diff --git a/Mucked.py b/Mucked.py new file mode 100644 index 00000000..0af06c65 --- /dev/null +++ b/Mucked.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python +"""Mucked.py + +Mucked cards display for FreePokerTools HUD. +""" +# Copyright 2008, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# to do +# problem with hand 30586 + +# Standard Library modules +import sys +import os +import string +import xml.dom.minidom +from xml.dom.minidom import Node + +# pyGTK modules +import pygtk +import gtk +import gobject + +# FreePokerTools modules +import Configuration +import Database +import Tables +import Hud +import Mucked +import HandHistory + +class Mucked: + def __init__(self, parent, db_connection): + + self.parent = parent #this is the parent of the mucked cards widget + self.db_connection = db_connection + + self.vbox = gtk.VBox() + self.parent.add(self.vbox) + + self.mucked_list = MuckedList (self.vbox, db_connection) + self.mucked_cards = MuckedCards(self.vbox, db_connection) + self.mucked_list.mucked_cards = self.mucked_cards + + def update(self, new_hand_id): + self.mucked_list.update(new_hand_id) + +class MuckedList: + def __init__(self, parent, db_connection): + + self.parent = parent + self.db_connection = db_connection + +# set up a scrolled window to hold the listbox + self.scrolled_window = gtk.ScrolledWindow() + self.scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + self.parent.add(self.scrolled_window) + +# create a ListStore to use as the model + self.liststore = gtk.ListStore(str, str, str) + self.treeview = gtk.TreeView(self.liststore) + self.tvcolumn0 = gtk.TreeViewColumn('HandID') + self.tvcolumn1 = gtk.TreeViewColumn('Cards') + self.tvcolumn2 = gtk.TreeViewColumn('Net') + +# add tvcolumn to treeview + self.treeview.append_column(self.tvcolumn0) + self.treeview.append_column(self.tvcolumn1) + self.treeview.append_column(self.tvcolumn2) + +# create a CellRendererText to render the data + self.cell = gtk.CellRendererText() + + # add the cell to the tvcolumn and allow it to expand + self.tvcolumn0.pack_start(self.cell, True) + self.tvcolumn1.pack_start(self.cell, True) + self.tvcolumn2.pack_start(self.cell, True) + self.tvcolumn0.add_attribute(self.cell, 'text', 0) + self.tvcolumn1.add_attribute(self.cell, 'text', 1) + self.tvcolumn2.add_attribute(self.cell, 'text', 2) +# resize the cols if nec + self.tvcolumn0.set_resizable(True) + self.treeview.connect("row-activated", self.activated_event) + + self.scrolled_window.add_with_viewport(self.treeview) + + def activated_event(self, path, column, data=None): + sel = self.treeview.get_selection() + (model, iter) = sel.get_selected() + self.mucked_cards.update(model.get_value(iter, 0)) + + def update(self, new_hand_id): +# info_row = self.db_connection.get_hand_info(new_hand_id) + info_row = ((new_hand_id, "xxxx", 0), ) + iter = self.liststore.append(info_row[0]) + sel = self.treeview.get_selection() + sel.select_iter(iter) + + vadj = self.scrolled_window.get_vadjustment() + vadj.set_value(vadj.upper) + self.mucked_cards.update(new_hand_id) + +class MuckedCards: + def __init__(self, parent, db_connection): + + self.parent = parent #this is the parent of the mucked cards widget + self.db_connection = db_connection + + self.card_images = self.get_card_images() + self.seen_cards = {} + self.grid_contents = {} + self.eb = {} + + self.rows = 8 + self.cols = 7 + self.grid = gtk.Table(self.rows, self.cols + 4, homogeneous = False) + + for r in range(0, self.rows): + for c in range(0, self.cols): + self.seen_cards[(c, r)] = gtk.image_new_from_pixbuf(self.card_images[('B', 'S')]) + self.eb[(c, r)]= gtk.EventBox() + +# set up the contents for the cells + for r in range(0, self.rows): + self.grid_contents[( 0, r)] = gtk.Label("%d" % (r + 1)) + self.grid_contents[( 1, r)] = gtk.Label("player %d" % (r + 1)) + self.grid_contents[( 4, r)] = gtk.Label("-") + self.grid_contents[( 9, r)] = gtk.Label("-") + self.grid_contents[( 2, r)] = self.eb[( 0, r)] + self.grid_contents[( 3, r)] = self.eb[( 1, r)] + self.grid_contents[( 5, r)] = self.eb[( 2, r)] + self.grid_contents[( 6, r)] = self.eb[( 3, r)] + self.grid_contents[( 7, r)] = self.eb[( 4, r)] + self.grid_contents[( 8, r)] = self.eb[( 5, r)] + self.grid_contents[(10, r)] = self.eb[( 6, r)] + for c in range(0, self.cols): + self.eb[(c, r)].add(self.seen_cards[(c, r)]) + +# add the cell contents to the table + for c in range(0, self.cols + 4): + for r in range(0, self.rows): + self.grid.attach(self.grid_contents[(c, r)], c, c+1, r, r+1, xpadding = 1, ypadding = 1) + + self.parent.add(self.grid) + + def translate_cards(self, old_cards): + pass + + def update(self, new_hand_id): + cards = self.db_connection.get_cards(new_hand_id) + self.clear() + + cards = self.translate_cards(cards) + for c in cards.keys(): + self.grid_contents[(1, cards[c]['seat_number'] - 1)].set_text(cards[c]['screen_name']) + + for i in ((0, 'hole_card_1'), (1, 'hole_card_2'), (2, 'hole_card_3'), (3, 'hole_card_4'), + (4, 'hole_card_5'), (5, 'hole_card_6'), (6, 'hole_card_7')): + if not cards[c][i[1]] == "": + self.seen_cards[(i[0], cards[c]['seat_number'] - 1)]. \ + set_from_pixbuf(self.card_images[self.split_cards(cards[c][i[1]])]) + + xml_text = self.db_connection.get_xml(new_hand_id) + hh = HandHistory.HandHistory(xml_text, ('BETTING')) + +# action in tool tips for 3rd street cards + tip = "%s" % hh.BETTING.rounds[0] + for c in (0, 1, 2): + for r in range(0, self.rows): + self.eb[(c, r)].set_tooltip_text(tip) + +# action in tools tips for later streets + round_to_col = (0, 3, 4, 5, 6) + for round in range(1, len(hh.BETTING.rounds)): + tip = "%s" % hh.BETTING.rounds[round] + for r in range(0, self.rows): + self.eb[(round_to_col[round], r)].set_tooltip_text(tip) + + def split_cards(self, card): + return (card[0], card[1].upper()) + + def clear(self): + for r in range(0, self.rows): + self.grid_contents[(1, r)].set_text(" ") + for c in range(0, 7): + self.seen_cards[(c, r)].set_from_pixbuf(self.card_images[('B', 'S')]) + self.eb[(c, r)].set_tooltip_text('') + def get_card_images(self): + card_images = {} + suits = ('S', 'H', 'D', 'C') + ranks = ('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'B') + pb = gtk.gdk.pixbuf_new_from_file("Cards01.png") + + for j in range(0, 14): + for i in range(0, 4): + temp_pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, pb.get_has_alpha(), pb.get_bits_per_sample(), 30, 42) + pb.copy_area(30*j, 42*i, 30, 42, temp_pb, 0, 0) + card_images[(ranks[j], suits[i])] = temp_pb + return(card_images) + +# cards are 30 wide x 42 high + +if __name__== "__main__": + + def destroy(*args): # call back for terminating the main eventloop + gtk.main_quit() # used only for testing + + def process_new_hand(source, condition): #callback from stdin watch -- testing only +# there is a new hand_id to be processed +# just read it and pass it to update + new_hand_id = sys.stdin.readline() + new_hand_id = new_hand_id.rstrip() # remove trailing whitespace + m.update(new_hand_id) + return(True) + + config = Configuration.Config() + db_connection = Database.Database(config, 'fpdb', '') + + main_window = gtk.Window() + main_window.set_keep_above(True) + main_window.connect("destroy", destroy) + + m = Mucked(main_window, db_connection) + main_window.show_all() + + s_id = gobject.io_add_watch(sys.stdin, gobject.IO_IN, process_new_hand) + + gtk.main() diff --git a/SQL.py b/SQL.py new file mode 100644 index 00000000..072d478e --- /dev/null +++ b/SQL.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +"""SQL.py + +Set up all of the SQL statements for a given game and database type. +""" +# Copyright 2008, Ray E. Barker +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules + +# pyGTK modules + +# FreePokerTools modules + +class Sql: + + def __init__(self, game = 'holdem', type = 'PT3'): + self.query = {} + +############################################################################ +# +# Support for the ptracks database, a cut down PT2 stud database. +# You can safely ignore this unless you are me. +# + if game == 'razz' and type == 'ptracks': + + self.query['get_table_name'] = "select table_name from game where game_id = %s" + + self.query['get_last_hand'] = "select max(game_id) from game" + + self.query['get_recent_hands'] = "select game_id from game where game_id > %(last_hand)d" + + self.query['get_xml'] = "select xml from hand_history where game_id = %s" + + self.query['get_player_id'] = """ + select player_id from players + where screen_name = %(player)s + """ + + self.query['get_hand_info'] = """ + SELECT + game_id, + CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand, + total_won-total_bet AS net + FROM game_players + WHERE game_id = %s AND player_id = 3 + """ + + self.query['get_cards'] = """ + select + seat_number, + screen_name, + hole_card_1, + hole_card_2, + hole_card_3, + hole_card_4, + hole_card_5, + hole_card_6, + hole_card_7 + from game_players, players + where game_id = %s and game_players.player_id = players.player_id + order by seat_number + """ + + self.query['get_stats_from_hand'] = """ + SELECT player_id, + count(*) AS n, + sum(pre_fourth_raise_n) AS pfr, + sum(fourth_raise_n) AS raise_n_2, + sum(fourth_ck_raise_n) AS cr_n_2, + sum(fifth_bet_raise_n) AS br_n_3, + sum(fifth_bet_ck_raise_n) AS cr_n_3, + sum(sixth_bet_raise_n) AS br_n_4, + sum(sixth_bet_ck_raise_n) AS cr_n_4, + sum(river_bet_raise_n) AS br_n_5, + sum(river_bet_ck_raise_n) AS cr_n_5, + sum(went_to_showdown_n) AS sd, + sum(saw_fourth_n) AS saw_f, + sum(raised_first_pf) AS first_pfr, + sum(vol_put_money_in_pot) AS vpip, + sum(limp_with_prev_callers) AS limp_w_callers, + + sum(ppossible_actions) AS poss_a_pf, + sum(pfold) AS fold_pf, + sum(pcheck) AS check_pf, + sum(praise) AS raise_pf, + sum(pcall) AS raise_pf, + sum(limp_call_reraise_pf) AS limp_call_pf, + + sum(pfr_check) AS check_after_raise, + sum(pfr_call) AS call_after_raise, + sum(pfr_fold) AS fold_after_raise, + sum(pfr_bet) AS bet_after_raise, + sum(pfr_raise) AS raise_after_raise, + sum(folded_to_river_bet) AS fold_to_r_bet, + + sum(fpossible_actions) AS poss_a_2, + sum(ffold) AS fold_2, + sum(fcheck) AS check_2, + sum(fbet) AS bet_2, + sum(fraise) AS raise_2, + sum(fcall) AS raise_2, + + sum(fifpossible_actions) AS poss_a_3, + sum(fiffold) AS fold_3, + sum(fifcheck) AS check_3, + sum(fifbet) AS bet_3, + sum(fifraise) AS raise_3, + sum(fifcall) AS call_3, + + sum(spossible_actions) AS poss_a_4, + sum(sfold) AS fold_4, + sum(scheck) AS check_4, + sum(sbet) AS bet_4, + sum(sraise) AS raise_4, + sum(scall) AS call_4, + + sum(rpossible_actions) AS poss_a_5, + sum(rfold) AS fold_5, + sum(rcheck) AS check_5, + sum(rbet) AS bet_5, + sum(rraise) AS raise_5, + sum(rcall) AS call_5, + + sum(cold_call_pf) AS cc_pf, + sum(saw_fifth_n) AS saw_3, + sum(saw_sixth_n) AS saw_4, + sum(saw_river_n) AS saw_5 + FROM game_players + WHERE player_id in + (SELECT player_id FROM game_players + WHERE game_id = %s AND NOT player_id = %s) + GROUP BY player_id + """ +# alternate form of WHERE for above +# WHERE game_id = %(hand)d AND NOT player_id = %(hero)d) +# WHERE game_id = %s AND NOT player_id = %s) + + self.query['get_players_from_hand'] = """ + SELECT game_players.player_id, seat_number, screen_name + FROM game_players INNER JOIN players ON (game_players.player_id = players.player_id) + WHERE game_id = %s + """ + +###############################################################################3 +# Support for the Free Poker DataBase = fpdb http://fpdb.sourceforge.net/ +# + if type == 'fpdb': + + self.query['get_last_hand'] = "select max(id) from Hands" + + self.query['get_player_id'] = """ + select Players.id AS player_id from Players, Sites + where Players.name = %(player)s + and Sites.name = %(site)s + and Players.SiteId = Sites.id + """ + + self.query['get_stats_from_hand'] = """ + SELECT HudCache.playerId AS player_id, + sum(HDs) AS n, + sum(street0VPI) AS vpip, + sum(street0Aggr) AS pfr, + sum(street0_3B4BChance) AS TB_opp_0, + sum(street0_3B4BDone) AS TB_0, + sum(street1Seen) AS saw_f, + sum(street1Seen) AS saw_1, + sum(street2Seen) AS saw_2, + sum(street3Seen) AS saw_3, + sum(street4Seen) AS saw_4, + sum(sawShowdown) AS sd, + sum(street1Aggr) AS aggr_1, + sum(street2Aggr) AS aggr_2, + sum(street3Aggr) AS aggr_3, + sum(street4Aggr) AS aggr_4, + sum(otherRaisedStreet1) AS was_raised_1, + sum(otherRaisedStreet2) AS was_raised_2, + sum(otherRaisedStreet3) AS was_raised_3, + sum(otherRaisedStreet4) AS was_raised_4, + sum(foldToOtherRaisedStreet1) AS f_freq_1, + sum(foldToOtherRaisedStreet2) AS f_freq_2, + sum(foldToOtherRaisedStreet3) AS f_freq_3, + sum(foldToOtherRaisedStreet4) AS f_freq_4, + sum(wonWhenSeenStreet1) AS w_w_s_1, + sum(wonAtSD) AS wmsd, + sum(stealAttemptChance) AS steal_opp, + sum(stealAttempted) AS steal, + sum(foldBbToStealChance) AS SBstolen, + sum(foldedBbToSteal) AS BBnotDef, + sum(foldBbToStealChance) AS BBstolen, + sum(foldedSbToSteal) AS SBnotDef, + sum(street1CBChance) AS CB_opp_1, + sum(street1CBDone) AS CB_1, + sum(street2CBChance) AS CB_opp_2, + sum(street2CBDone) AS CB_2, + sum(street3CBChance) AS CB_opp_3, + sum(street3CBDone) AS CB_3, + sum(street4CBChance) AS CB_opp_4, + sum(street4CBDone) AS CB_4, + sum(foldToStreet1CBChance) AS f_cb_opp_1, + sum(foldToStreet1CBDone) AS f_cb_1, + sum(foldToStreet2CBChance) AS f_cb_opp_2, + sum(foldToStreet2CBDone) AS f_cb_2, + sum(foldToStreet3CBChance) AS f_cb_opp_3, + sum(foldToStreet3CBDone) AS f_cb_3, + sum(foldToStreet4CBChance) AS f_cb_opp_4, + sum(foldToStreet4CBDone) AS f_cb_4, + sum(totalProfit) AS net, + sum(street1CheckCallRaiseChance) AS ccr_opp_1, + sum(street1CheckCallRaiseDone) AS ccr_1, + sum(street2CheckCallRaiseChance) AS ccr_opp_2, + sum(street2CheckCallRaiseDone) AS ccr_2, + sum(street3CheckCallRaiseChance) AS ccr_opp_3, + sum(street3CheckCallRaiseDone) AS ccr_3, + sum(street4CheckCallRaiseChance) AS ccr_opp_4, + sum(street4CheckCallRaiseDone) AS ccr_4 + FROM HudCache, Hands + WHERE HudCache.PlayerId in + (SELECT PlayerId FROM HandsPlayers + WHERE handId = %s) + AND Hands.id = %s + AND Hands.gametypeId = HudCache.gametypeId + GROUP BY HudCache.PlayerId + """ +# AND PlayerId LIKE %s +# HudCache.gametypeId AS gametypeId, +# activeSeats AS n_active, +# position AS position, +# HudCache.tourneyTypeId AS tourneyTypeId, + + self.query['get_players_from_hand'] = """ + SELECT HandsPlayers.playerId, seatNo, name + FROM HandsPlayers INNER JOIN Players ON (HandsPlayers.playerId = Players.id) + WHERE handId = %s + """ +# WHERE handId = %s AND Players.id LIKE %s + + self.query['get_table_name'] = """ + select tableName, maxSeats, category + from Hands,Gametypes + where Hands.id = %s + and Gametypes.id = Hands.gametypeId + """ + + self.query['get_cards'] = """ + select + seatNo AS seat_number, + name AS screen_name, + card1Value, card1Suit, + card2Value, card2Suit, + card3Value, card3Suit, + card4Value, card4Suit, + card5Value, card5Suit, + card6Value, card6Suit, + card7Value, card7Suit + from HandsPlayers, Players + where handID = %s and HandsPlayers.playerId = Players.id + order by seatNo + """ + +# self.query['get_hand_info'] = """ +# SELECT +# game_id, +# CONCAT(hole_card_1, hole_card_2, hole_card_3, hole_card_4, hole_card_5, hole_card_6, hole_card_7) AS hand, +# total_won-total_bet AS net +# FROM game_players +# WHERE game_id = %s AND player_id = 3 +# """ + +if __name__== "__main__": +# just print the default queries and exit + s = Sql(game = 'razz', type = 'ptracks') + for key in s.query: + print "For query " + key + ", sql =" + print s.query[key] diff --git a/Stats.py b/Stats.py new file mode 100644 index 00000000..3531c017 --- /dev/null +++ b/Stats.py @@ -0,0 +1,618 @@ +#!/usr/bin/env python + +"""Manage collecting and formatting of stats and tooltips. +""" +# Copyright 2008, Ray E. Barker + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# How to write a new stat: +# 1 You can see a listing of all the raw stats (e.g., from the HudCache table) +# by running Database.py as a stand along program. You need to combine +# those raw stats to get stats to present to the HUD. If you need more +# information than is in the HudCache table, then you have to write SQL. +# 2 The raw stats seen when you run Database.py are available in the Stats.py +# in the stat_dict dict. For example the number of vpips would be +# stat_dict[player]['vpip']. So the % vpip is +# float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']). You can see how the +# keys of stat_dict relate to the column names in HudCache by inspecting +# the proper section of the SQL.py module. +# 3 You have to write a small function for each stat you want to add. See +# the vpip() function for example. This function has to be protected from +# exceptions, using something like the try:/except: paragraphs in vpip. +# 4 The name of the function has to be the same as the of the stat used +# in the config file. +# 5 The stat functions have a peculiar return value, which is outlined in +# the do_stat function. This format is useful for tool tips and maybe +# other stuff. +# 6 For each stat you make add a line to the __main__ function to test it. + +# Standard Library modules +#import sys + +# pyGTK modules +import pygtk +import gtk + +# FreePokerTools modules +import Configuration +import Database + +def do_tip(widget, tip): + widget.set_tooltip_text(tip) + +def do_stat(stat_dict, player = 24, stat = 'vpip'): + return eval("%(stat)s(stat_dict, %(player)d)" % {'stat': stat, 'player': player}) +# OK, for reference the tuple returned by the stat is: +# 0 - The stat, raw, no formating, eg 0.33333333 +# 1 - formatted stat with appropriate precision and punctuation, eg 33% +# 2 - formatted stat with appropriate precision, punctuation and a hint, eg v=33% +# 3 - same as #2 except name of stat instead of hint, eg vpip=33% +# 4 - the calculation that got the stat, eg 9/27 +# 5 - the name of the stat, useful for a tooltip, eg vpip + +########################################### +# functions that return individual stats +def vpip(stat_dict, player): + """ Voluntarily put $ in the pot.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['vpip'])/float(stat_dict[player]['n']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'v=%3.1f' % (100*stat) + '%', + 'vpip=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['vpip'], stat_dict[player]['n']), + 'vpip' + ) + except: return (stat, + '%3.1f' % (0) + '%', + 'w=%3.1f' % (0) + '%', + 'wtsd=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'wtsd' + ) + +def pfr(stat_dict, player): + """ Preflop (3rd street) raise.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['pfr'])/float(stat_dict[player]['n']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'p=%3.1f' % (100*stat) + '%', + 'pfr=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['pfr'], stat_dict[player]['n']), + 'pfr' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'p=%3.1f' % (0) + '%', + 'pfr=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'pfr' + ) + +def wtsd(stat_dict, player): + """ Went to SD when saw flop/4th.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['sd'])/float(stat_dict[player]['saw_f']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'w=%3.1f' % (100*stat) + '%', + 'wtsd=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['sd'], stat_dict[player]['saw_f']), + '% went to showdown' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'w=%3.1f' % (0) + '%', + 'wtsd=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% went to showdown' + ) + +def wmsd(stat_dict, player): + """ Won $ at showdown.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['wmsd'])/float(stat_dict[player]['sd']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'w=%3.1f' % (100*stat) + '%', + 'wmsd=%3.1f' % (100*stat) + '%', + '(%f5.0/%d)' % (stat_dict[player]['wmsd'], stat_dict[player]['sd']), + '% won money at showdown' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'w=%3.1f' % (0) + '%', + 'wmsd=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% won money at showdown' + ) + +def saw_f(stat_dict, player): + """ Saw flop/4th.""" + try: + num = float(stat_dict[player]['saw_f']) + den = float(stat_dict[player]['n']) + stat = num/den + return (stat, + '%3.1f' % (100*stat) + '%', + 'sf=%3.1f' % (100*stat) + '%', + 'saw_f=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['saw_f'], stat_dict[player]['n']), + 'saw_f' + ) + except: + stat = 0.0 + num = 0 + den = 0 + return (stat, + '%3.1f' % (stat) + '%', + 'sf=%3.1f' % (stat) + '%', + 'saw_f=%3.1f' % (stat) + '%', + '(%d/%d)' % (num, den), + 'saw_f' + ) + +def n(stat_dict, player): + """ Number of hands played.""" + try: + return (stat_dict[player]['n'], + '%d' % (stat_dict[player]['n']), + 'n=%d' % (stat_dict[player]['n']), + 'n=%d' % (stat_dict[player]['n']), + '(%d)' % (stat_dict[player]['n']), + 'number hands seen' + ) + except: + return (0, + '%d' % (0), + 'n=%d' % (0), + 'n=%d' % (0), + '(%d)' % (0), + 'number hands seen' + ) + +def fold_f(stat_dict, player): + """ Folded flop/4th.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['fold_2'])/fold(stat_dict[player]['saw_f']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'ff=%3.1f' % (100*stat) + '%', + 'fold_f=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['fold_2'], stat_dict[player]['saw_f']), + 'folded flop/4th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'ff=%3.1f' % (0) + '%', + 'fold_f=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'folded flop/4th' + ) + +def steal(stat_dict, player): + """ Steal %.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['steal'])/float(stat_dict[player]['steal_opp']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'st=%3.1f' % (100*stat) + '%', + 'steal=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['steal'], stat_dict[player]['steal_opp']), + '% steal attempted' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'st=%3.1f' % (0) + '%', + 'steal=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% steal attempted' + ) + +def f_SB_steal(stat_dict, player): + """ Folded SB to steal.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['SBnotDef'])/float(stat_dict[player]['SBstolen']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'fSB=%3.1f' % (100*stat) + '%', + 'fSB_s=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['SBnotDef'], stat_dict[player]['SBstolen']), + '% folded SB to steal' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'fSB=%3.1f' % (0) + '%', + 'fSB_s=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% folded SB to steal' + ) + +def f_BB_steal(stat_dict, player): + """ Folded BB to steal.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['BBnotDef'])/float(stat_dict[player]['BBstolen']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'fBB=%3.1f' % (100*stat) + '%', + 'fBB_s=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['BBnotDef'], stat_dict[player]['BBstolen']), + '% folded BB to steal' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'fBB=%3.1f' % (0) + '%', + 'fBB_s=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% folded BB to steal' + ) + +def three_B_0(stat_dict, player): + """ Three bet preflop/3rd.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['TB_0'])/float(stat_dict[player]['TB_opp_0']) + return (stat, + '%3.1f' % (100*stat) + '%', + '3B=%3.1f' % (100*stat) + '%', + '3B_pf=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['TB_0'], stat_dict[player]['TB_opp_0']), + '% 3/4 Bet preflop/3rd' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + '3B=%3.1f' % (0) + '%', + '3B_pf=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% 3/4 Bet preflop/3rd' + ) + +def WMsF(stat_dict, player): + """ Won $ when saw flop/4th.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['w_w_s_1'])/float(stat_dict[player]['saw_1']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'wf=%3.1f' % (100*stat) + '%', + 'w_w_f=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['w_w_s_1'], stat_dict[player]['saw_f']), + '% won$/saw flop/4th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'wf=%3.1f' % (0) + '%', + 'w_w_f=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% won$/saw flop/4th' + ) + +def a_freq_1(stat_dict, player): + """ Flop/4th aggression frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['aggr_1'])/float(stat_dict[player]['saw_f']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'a1=%3.1f' % (100*stat) + '%', + 'a_fq_1=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['aggr_1'], stat_dict[player]['saw_f']), + 'Aggression Freq flop/4th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'a1=%3.1f' % (0) + '%', + 'a_fq_1=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'Aggression Freq flop/4th' + ) + +def a_freq_2(stat_dict, player): + """ Turn/5th aggression frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['aggr_2'])/float(stat_dict[player]['saw_2']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'a2=%3.1f' % (100*stat) + '%', + 'a_fq_2=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['aggr_2'], stat_dict[player]['saw_2']), + 'Aggression Freq turn/5th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'a2=%3.1f' % (0) + '%', + 'a_fq_2=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'Aggression Freq turn/5th' + ) + +def a_freq_3(stat_dict, player): + """ River/6th aggression frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['aggr_3'])/float(stat_dict[player]['saw_3']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'a3=%3.1f' % (100*stat) + '%', + 'a_fq_3=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['aggr_1'], stat_dict[player]['saw_1']), + 'Aggression Freq river/6th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'a3=%3.1f' % (0) + '%', + 'a_fq_3=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'Aggression Freq river/6th' + ) + +def a_freq_4(stat_dict, player): + """ 7th street aggression frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['aggr_4'])/float(stat_dict[player]['saw_4']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'a4=%3.1f' % (100*stat) + '%', + 'a_fq_4=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['aggr_4'], stat_dict[player]['saw_4']), + 'Aggression Freq 7th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'a4=%3.1f' % (0) + '%', + 'a_fq_4=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + 'Aggression Freq flop/4th' + ) + +def cb_1(stat_dict, player): + """ Flop continuation bet.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['CB_1'])/float(stat_dict[player]['CB_opp_1']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'cb1=%3.1f' % (100*stat) + '%', + 'cb_1=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['CB_1'], stat_dict[player]['CB_opp_1']), + '% continuation bet flop/4th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'cb1=%3.1f' % (0) + '%', + 'cb_1=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% continuation bet flop/4th' + ) + +def cb_2(stat_dict, player): + """ Turn continuation bet.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['CB_2'])/float(stat_dict[player]['CB_opp_2']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'cb2=%3.1f' % (100*stat) + '%', + 'cb_2=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['CB_2'], stat_dict[player]['CB_opp_2']), + '% continuation bet turn/5th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'cb2=%3.1f' % (0) + '%', + 'cb_2=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% continuation bet turn/5th' + ) + +def cb_3(stat_dict, player): + """ River continuation bet.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['CB_3'])/float(stat_dict[player]['CB_opp_3']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'cb3=%3.1f' % (100*stat) + '%', + 'cb_3=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['CB_3'], stat_dict[player]['CB_opp_3']), + '% continuation bet river/6th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'cb3=%3.1f' % (0) + '%', + 'cb_3=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% continuation bet river/6th' + ) + +def cb_4(stat_dict, player): + """ 7th street continuation bet.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['CB_4'])/float(stat_dict[player]['CB_opp_4']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'cb4=%3.1f' % (100*stat) + '%', + 'cb_4=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['CB_4'], stat_dict[player]['CB_opp_4']), + '% continuation bet 7th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'cb4=%3.1f' % (0) + '%', + 'cb_4=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% continuation bet 7th' + ) + +def ffreq_1(stat_dict, player): + """ Flop/4th fold frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['f_freq_1'])/float(stat_dict[player]['was_raised_1']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'ff1=%3.1f' % (100*stat) + '%', + 'ff_1=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['f_freq_1'], stat_dict[player]['was_raised_1']), + '% fold frequency flop/4th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'ff1=%3.1f' % (0) + '%', + 'ff_1=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% fold frequency flop/4th' + ) + +def ffreq_2(stat_dict, player): + """ Turn/5th fold frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['f_freq_2'])/float(stat_dict[player]['was_raised_2']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'ff2=%3.1f' % (100*stat) + '%', + 'ff_2=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['f_freq_2'], stat_dict[player]['was_raised_2']), + '% fold frequency turn/5th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'ff2=%3.1f' % (0) + '%', + 'ff_2=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% fold frequency turn/5th' + ) + +def ffreq_3(stat_dict, player): + """ River/6th fold frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['f_freq_3'])/float(stat_dict[player]['was_raised_3']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'ff3=%3.1f' % (100*stat) + '%', + 'ff_3=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['f_freq_3'], stat_dict[player]['was_raised_3']), + '% fold frequency river/6th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'ff3=%3.1f' % (0) + '%', + 'ff_3=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% fold frequency river/6th' + ) + +def ffreq_4(stat_dict, player): + """ 7th fold frequency.""" + stat = 0.0 + try: + stat = float(stat_dict[player]['f_freq_4'])/float(stat_dict[player]['was_raised_4']) + return (stat, + '%3.1f' % (100*stat) + '%', + 'ff4=%3.1f' % (100*stat) + '%', + 'ff_4=%3.1f' % (100*stat) + '%', + '(%d/%d)' % (stat_dict[player]['f_freq_4'], stat_dict[player]['was_raised_4']), + '% fold frequency 7th' + ) + except: + return (stat, + '%3.1f' % (0) + '%', + 'ff4=%3.1f' % (0) + '%', + 'ff_4=%3.1f' % (0) + '%', + '(%d/%d)' % (0, 0), + '% fold frequency 7th' + ) + +if __name__== "__main__": + c = Configuration.Config() + db_connection = Database.Database(c, 'fpdb', 'holdem') + h = db_connection.get_last_hand() + stat_dict = db_connection.get_stats_from_hand(h) + + for player in stat_dict.keys(): + print "player = ", player, do_stat(stat_dict, player = player, stat = 'vpip') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'pfr') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'wtsd') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'saw_f') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'n') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'fold_f') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'wmsd') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'steal') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_SB_steal') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'f_BB_steal') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'three_B_0') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'WMsF') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_1') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_2') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_3') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'a_freq_4') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_1') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_2') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_3') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'cb_4') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_1') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_2') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_3') + print "player = ", player, do_stat(stat_dict, player = player, stat = 'ffreq_4') + + print "\n\nLegal stats:" + for attr in dir(): + if attr.startswith('__'): continue + if attr in ("Configuration", "Database", "GInitiallyUnowned", "gtk", "pygtk", + "player", "c", "db_connection", "do_stat", "do_tip", "stat_dict", + "h"): continue + print attr, eval("%s.__doc__" % (attr)) +# print " " % (attr) + + db_connection.close + diff --git a/Tables.py b/Tables.py new file mode 100644 index 00000000..d7d9533d --- /dev/null +++ b/Tables.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python +"""Discover_Tables.py + +Inspects the currently open windows and finds those of interest to us--that is +poker table windows from supported sites. Returns a list +of Table_Window objects representing the windows found. +""" +# Copyright 2008, Ray E. Barker + +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# 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 General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +######################################################################## + +# Standard Library modules +import os +import sys +import re + +# Win32 modules + +if os.name == 'nt': + import win32gui + +# FreePokerTools modules +import Configuration + +class Table_Window: + def __str__(self): +# __str__ method for testing + temp = 'TableWindow object\n' + temp = temp + " name = %s\n site = %s\n number = %s\n title = %s\n" % (self.name, self.site, self.number, self.title) + temp = temp + " game = %s\n structure = %s\n max = %s\n" % (self.game, self.structure, self.max) + temp = temp + " width = %d\n height = %d\n x = %d\n y = %d\n" % (self.width, self.height, self.x, self.y) + if getattr(self, 'tournament', 0): + temp = temp + " tournament = %d\n table = %d" % (self.tournament, self.table) + return temp + + def get_details(table): + table.game = 'razz' + table.max = 8 + table.struture = 'limit' + table.tournament = 0 + +def discover(c): + if os.name == 'posix': + tables = discover_posix(c) + return tables + elif os.name == 'nt': + tables = discover_nt(c) + return tables + elif ox.name == 'mac': + tables = discover_mac(c) + return tables + else: tables = {} + + return(tables) + +def discover_posix(c): + """ Poker client table window finder for posix/Linux = XWindows.""" + tables = {} + for listing in os.popen('xwininfo -root -tree').readlines(): +# xwininfo -root -tree -id 0xnnnnn gets the info on a single window + if re.search('Lobby', listing): continue + if re.search('Instant Hand History', listing): continue + if not re.search('Logged In as ', listing): continue + for s in c.supported_sites.keys(): + if re.search(c.supported_sites[s].table_finder, listing): + mo = re.match('\s+([\dxabcdef]+) (.+):.+ (\d+)x(\d+)\+\d+\+\d+ \+(\d+)\+(\d+)', listing) + if mo.group(2) == '(has no name)': continue + if re.match('[\(\)\d\s]+', mo.group(2)): continue # this is a popup + tw = Table_Window() + tw.site = c.supported_sites[s].site_name + tw.number = mo.group(1) + tw.title = mo.group(2) + tw.width = int( mo.group(3) ) + tw.height = int( mo.group(4) ) + tw.x = int (mo.group(5) ) + tw.y = int (mo.group(6) ) + tw.title = re.sub('\"', '', tw.title) +# this rather ugly hack makes my fake table used for debugging work + if tw.title == "PokerStars.py": continue + +# use this eval thingie to call the title bar decoder specified in the config file + eval("%s(tw)" % c.supported_sites[s].decoder) + tables[tw.name] = tw + return tables +# +# The discover_xx functions query the system and report on the poker clients +# currently displayed on the screen. The discover_posix should give you +# some idea how to support other systems. +# +# discover_xx() returns a dict of TableWindow objects--one TableWindow +# object for each poker client table on the screen. +# +# Each TableWindow object must have the following attributes correctly populated: +# tw.site = the site name, e.g. PokerStars, FullTilt. This must match the site +# name specified in the config file. +# tw.number = This is the system id number for the client table window in the +# format that the system presents it. +# tw.title = The full title from the window title bar. +# tw.width, tw.height = The width and height of the window in pixels. This is +# the internal width and height, not including the title bar and +# window borders. +# tw.x, tw.y = The x, y (horizontal, vertical) location of the window relative +# to the top left of the display screen. This also does not include the +# title bar and window borders. To put it another way, this is the +# screen location of (0, 0) in the working window. + +def win_enum_handler(hwnd, titles): + titles[hwnd] = win32gui.GetWindowText(hwnd) + +def child_enum_handler(hwnd, children): + print hwnd, win32.GetWindowRect(hwnd) + +def discover_nt(c): + """ Poker client table window finder for Windows.""" +# +# I cannot figure out how to get the inside dimensions of the poker table +# windows. So I just assume all borders are 3 thick and all title bars +# are 29 high. No doubt this will be off when used with certain themes. +# + b_width = 3 + tb_height = 29 + titles = {} + tables = {} + win32gui.EnumWindows(win_enum_handler, titles) + for hwnd in titles.keys(): + if re.search('Logged In as', titles[hwnd]) and not re.search('Lobby', titles[hwnd]): + tw = Table_Window() +# tw.site = c.supported_sites[s].site_name + tw.number = hwnd + (x, y, width, height) = win32gui.GetWindowRect(hwnd) + tw.title = titles[hwnd] + tw.width = int( width ) - 2*b_width + tw.height = int( height ) - b_width - tb_height + tw.x = int( x ) + b_width + tw.y = int( y ) + tb_height + eval("%s(tw)" % "pokerstars_decode_table") + tw.site = "PokerStars" + + + tables[tw.name] = tw + return tables + +def discover_mac(c): + """ Poker client table window finder for Macintosh.""" + tables = {} + return tables + +def pokerstars_decode_table(tw): +# extract the table name OR the tournament number and table name from the title +# other info in title is redundant with data in the database + title_bits = re.split(' - ', tw.title) + name = title_bits[0] + mo = re.search('Tournament (\d+) Table (\d+)', name) + if mo: + tw.tournament = int( mo.group(1) ) + tw.table = int( mo.group(2) ) + tw.name = name + else: + tw.tournament = None + for pattern in [' no all-in', ' fast', ',']: + name = re.sub(pattern, '', name) + name = re.sub('\s+$', '', name) + tw.name = name + + mo = re.search('(Razz|Stud H/L|Stud|Omaha H/L|Omaha|Hold\'em|5-Card Draw|Triple Draw 2-7 Lowball)', tw.title) + + tw.game = mo.group(1).lower() + tw.game = re.sub('\'', '', tw.game) + tw.game = re.sub('h/l', 'hi/lo', tw.game) + + mo = re.search('(No Limit|Pot Limit)', tw.title) + if mo: + tw.structure = mo.group(1).lower() + else: + tw.structure = 'limit' + + tw.max = None + if tw.game in ('razz', 'stud', 'stud hi/lo'): + tw.max = 8 + elif tw.game in ('5-card draw', 'triple draw 2-7 lowball'): + tw.max = 6 + elif tw.game == 'holdem': + pass + elif tw.game in ('omaha', 'omaha hi/lo'): + pass + +if __name__=="__main__": + c = Configuration.Config() + tables = discover(c) + + for t in tables.keys(): + print "t = ", t + print tables[t] + + print "press enter to continue" + sys.stdin.readline()