Merge branch 'master' of git://git.assembla.com/fpdboz

Conflicts:

	pyfpdb/Database.py
	pyfpdb/Mucked.py
This commit is contained in:
Ray 2009-03-11 22:36:10 -04:00
commit 1fb6e5bfeb
23 changed files with 1663 additions and 904 deletions

View File

@ -32,12 +32,6 @@ import shutil
import xml.dom.minidom import xml.dom.minidom
from xml.dom.minidom import Node 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)
class Layout: class Layout:
def __init__(self, node): def __init__(self, node):
@ -47,7 +41,8 @@ class Layout:
self.height = int( node.getAttribute('height') ) self.height = int( node.getAttribute('height') )
self.location = [] self.location = []
for i in range(self.max + 1): self.location.append(None) self.location = map(lambda x: None, range(self.max+1)) # there must be a better way to do this?
for location_node in node.getElementsByTagName('location'): for location_node in node.getElementsByTagName('location'):
if location_node.getAttribute('seat') != "": if location_node.getAttribute('seat') != "":
@ -62,7 +57,7 @@ class Layout:
if hasattr(self, "common"): if hasattr(self, "common"):
temp = temp + " Common = (%d, %d)\n" % (self.common[0], self.common[1]) temp = temp + " Common = (%d, %d)\n" % (self.common[0], self.common[1])
temp = temp + " Locations = " temp = temp + " Locations = "
for i in range(1, len(self.location)): for i in xrange(1, len(self.location)):
temp = temp + "(%d,%d)" % self.location[i] temp = temp + "(%d,%d)" % self.location[i]
return temp + "\n" return temp + "\n"
@ -91,16 +86,17 @@ class Site:
self.layout[lo.max] = lo self.layout[lo.max] = lo
def __str__(self): def __str__(self):
temp = "Site = " + self.site_name + "\n" temp = "Site = %s\n" % self.site_name
for key in dir(self): for key in dir(self):
if key.startswith('__'): continue if key.startswith('__'): continue
if key == 'layout': continue if key == 'layout': continue
value = getattr(self, key) value = getattr(self, key)
if callable(value): continue if callable(value): continue
temp = "%s %s = %s\n" % (temp, key, str(value))
temp = temp + ' ' + key + " = " + str(value) + "\n" temp = temp + ' ' + key + " = " + str(value) + "\n"
for layout in self.layout: for layout in self.layout:
temp = temp + "%s" % self.layout[layout] temp = "%s%s" % (temp, self.layout[layout])
return temp return temp
@ -141,14 +137,10 @@ class Game:
self.stats[stat.stat_name] = stat self.stats[stat.stat_name] = stat
def __str__(self): def __str__(self):
temp = "Game = " + self.game_name + "\n" temp = "Game = %s\n db = %s\n rows = %d\n cols = %d\n aux = %s\n" % (self.game_name, self.db, self.rows, self.cols, self.aux)
temp = temp + " db = %s\n" % self.db
temp = temp + " rows = %d\n" % self.rows
temp = temp + " cols = %d\n" % self.cols
temp = temp + " aux = %s\n" % self.aux
for stat in self.stats.keys(): for stat in self.stats.keys():
temp = temp + "%s" % self.stats[stat] temp = "%s%s" % (temp, self.stats[stat])
return temp return temp
@ -417,7 +409,7 @@ class Config:
site_node = self.get_site_node(site_name) site_node = self.get_site_node(site_name)
layout_node = self.get_layout_node(site_node, max) layout_node = self.get_layout_node(site_node, max)
if layout_node == None: return if layout_node == None: return
for i in range(1, max + 1): for i in xrange(1, max + 1):
location_node = self.get_location_node(layout_node, i) location_node = self.get_location_node(layout_node, i)
location_node.setAttribute("x", str( locations[i-1][0] )) location_node.setAttribute("x", str( locations[i-1][0] ))
location_node.setAttribute("y", str( locations[i-1][1] )) location_node.setAttribute("y", str( locations[i-1][1] ))
@ -507,35 +499,17 @@ class Config:
def get_default_colors(self, site = "PokerStars"): def get_default_colors(self, site = "PokerStars"):
colors = {} colors = {}
if self.supported_sites[site].hudopacity == "": colors['hudopacity'] = float(self.supported_sites[site].hudopacity) if self.supported_sites[site].hudopacity != "" else 0.90
colors['hudopacity'] = 0.90 colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor if self.supported_sites[site].hudbgcolor != "" else "#FFFFFF"
else: colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor if self.supported_sites[site].hudfgcolor != "" else "#000000"
colors['hudopacity'] = float(self.supported_sites[site].hudopacity)
if self.supported_sites[site].hudbgcolor == "":
colors['hudbgcolor'] = "#FFFFFF"
else:
colors['hudbgcolor'] = self.supported_sites[site].hudbgcolor
if self.supported_sites[site].hudfgcolor == "":
colors['hudfgcolor'] = "#000000"
else:
colors['hudfgcolor'] = self.supported_sites[site].hudfgcolor
return colors return colors
def get_default_font(self, site = 'PokerStars'): def get_default_font(self, site = 'PokerStars'):
(font, font_size) = ("Sans", "8") font = self.supported_sites[site].font if self.supported_sites[site].font != "" else "Sans"
if self.supported_sites[site].font == "": font_size = self.supported_sites[site].font_size if self.supported_sites[site].font != "" else "8"
font = "Sans"
else:
font = self.supported_sites[site].font
if self.supported_sites[site].font_size == "":
font_size = "8"
else:
font_size = self.supported_sites[site].font_size
return (font, font_size) return (font, font_size)
def get_locations(self, site = "PokerStars", max = "8"): def get_locations(self, site = "PokerStars", max = "8"):
try: try:
locations = self.supported_sites[site].layout[max].location locations = self.supported_sites[site].layout[max].location
except: except:
@ -612,7 +586,7 @@ class Config:
def get_aux_parameters(self, name): def get_aux_parameters(self, name):
"""Gets a dict of mucked window parameters from the named mw.""" """Gets a dict of mucked window parameters from the named mw."""
param = {} param = {}
if self.aux_windows.has_key(name): if name in self.aux_windows:
for key in dir(self.aux_windows[name]): for key in dir(self.aux_windows[name]):
if key.startswith('__'): continue if key.startswith('__'): continue
value = getattr(self.aux_windows[name], key) value = getattr(self.aux_windows[name], key)
@ -625,7 +599,7 @@ class Config:
def get_game_parameters(self, name): def get_game_parameters(self, name):
"""Get the configuration parameters for the named game.""" """Get the configuration parameters for the named game."""
param = {} param = {}
if self.supported_games.has_key(name): if name in self.supported_games:
param['game_name'] = self.supported_games[name].game_name param['game_name'] = self.supported_games[name].game_name
param['db'] = self.supported_games[name].db param['db'] = self.supported_games[name].db
param['rows'] = self.supported_games[name].rows param['rows'] = self.supported_games[name].rows
@ -636,13 +610,13 @@ class Config:
def get_supported_games(self): def get_supported_games(self):
"""Get the list of supported games.""" """Get the list of supported games."""
sg = [] sg = []
for game in c.supported_games.keys(): for game in c.supported_games:
sg.append(c.supported_games[game].game_name) sg.append(c.supported_games[game].game_name)
return sg return sg
def execution_path(self, filename): def execution_path(self, filename):
"""Join the fpdb path to filename.""" """Join the fpdb path to filename."""
return os.path.join(os.path.dirname(inspect.getfile(sys._getframe(1))), filename) return os.path.join(os.path.dirname(inspect.getfile(sys._getframe(0))), filename)
if __name__== "__main__": if __name__== "__main__":
c = Config() c = Config()

View File

@ -145,7 +145,7 @@ class Database:
def convert_cards(self, d): def convert_cards(self, d):
ranks = ('', '', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A') ranks = ('', '', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A')
cards = "" cards = ""
for i in range(1, 8): for i in xrange(1, 8):
key = 'card' + str(i) + 'Value' key = 'card' + str(i) + 'Value'
if not d.has_key(key): continue if not d.has_key(key): continue
if d[key] == None: if d[key] == None:

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: iso-8859-15 -*- # -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi # Copyright 2008, Carl Gherardi
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -19,9 +20,7 @@
import sys import sys
import logging import logging
import Configuration
from HandHistoryConverter import * from HandHistoryConverter import *
from time import strftime
# Class for converting Everleaf HH format. # Class for converting Everleaf HH format.
@ -29,45 +28,71 @@ class Everleaf(HandHistoryConverter):
# Static regexes # Static regexes
re_SplitHands = re.compile(r"\n\n+") re_SplitHands = re.compile(r"\n\n+")
re_GameInfo = re.compile(r"^(Blinds )?\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) ((?P<LTYPE>NL|PL) )?(?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE) re_GameInfo = re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) ?(?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
re_HandInfo = re.compile(r".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>[- a-zA-Z]+)") #re.compile(ur"^(Blinds )?(?P<CURRENCY>\$| €|)(?P<SB>[.0-9]+)/(?:\$| €)?(?P<BB>[.0-9]+) (?P<LIMIT>NL|PL|) (?P<GAME>(Hold\'em|Omaha|7 Card Stud))", re.MULTILINE)
re_Button = re.compile(r"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE) re_HandInfo = re.compile(ur".*#(?P<HID>[0-9]+)\n.*\n(Blinds )?(?:\$| €|)(?P<SB>[.0-9]+)/(?:\$| €|)(?P<BB>[.0-9]+) (?P<GAMETYPE>.*) - (?P<DATETIME>\d\d\d\d/\d\d/\d\d - \d\d:\d\d:\d\d)\nTable (?P<TABLE>.+$)", re.MULTILINE)
re_PlayerInfo = re.compile(r"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+(\$ (?P<CASH>[.0-9]+) USD|new player|All-in) \)", re.MULTILINE) re_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE)
re_Board = re.compile(r"\[ (?P<CARDS>.+) \]") re_PlayerInfo = re.compile(ur"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+((?:\$| €|) (?P<CASH>[.0-9]+) (USD|EUR|)|new player|All-in) \)", re.MULTILINE)
re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
def __init__(self, config, file):
print "Initialising Everleaf converter class"
HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init.
self.sitename = "Everleaf"
self.setFileType("text", "cp1252")
try: def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False):
self.ofile = os.path.join(self.hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file)) """\
except: in_path (default '-' = sys.stdin)
self.ofile = os.path.join(self.hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file)) out_path (default '-' = sys.stdout)
follow : whether to tail -f the input
autostart: whether to run the thread (or you can call start() yourself)
debugging: if False, pass on partially supported game types. If true, have a go and error..."""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="Everleaf", follow=follow)
logging.info("Initialising Everleaf converter class")
self.filetype = "text"
self.codepage = "cp1252"
self.debugging = debugging
if autostart:
self.start()
def compilePlayerRegexs(self): def compilePlayerRegexs(self, hand):
player_re = "(?P<PNAME>" + "|".join(map(re.escape, self.players)) + ")" players = set([player[1] for player in hand.players])
#print "DEBUG player_re: " + player_re if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
self.re_PostSB = re.compile(r"^%s: posts small blind \[\$? (?P<SB>[.0-9]+)" % player_re, re.MULTILINE) # we need to recompile the player regexs.
self.re_PostBB = re.compile(r"^%s: posts big blind \[\$? (?P<BB>[.0-9]+)" % player_re, re.MULTILINE) self.compiledPlayers = players
self.re_PostBoth = re.compile(r"^%s: posts both blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE) player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
self.re_HeroCards = re.compile(r"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE) logging.debug("player_re: "+ player_re)
self.re_Action = re.compile(r"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[\$ (?P<BET>[.\d]+) (USD|EUR)\])?" % player_re, re.MULTILINE) self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE) self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"^%s wins \$ (?P<POT>[.\d]+) (USD|EUR)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE) self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE) self.re_Antes = re.compile(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
self.re_BringIn = re.compile(ur"^%s posts bring-in (?:\$| €|)(?P<BRINGIN>[.0-9]+)\." % player_re, re.MULTILINE)
self.re_HeroCards = re.compile(ur"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
#self.re_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds| complete to)(\s\[?(?:\$| €|) ?(?P<BET>\d+\.?\d*)\.?\s?(USD|EUR|)\]?)?" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(ur"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(ur"^%s wins (?:\$| €|) (?P<POT>[.\d]+) (USD|EUR|chips)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
self.re_SitsOut = re.compile(ur"^%s sits out" % player_re, re.MULTILINE)
def readSupportedGames(self): def readSupportedGames(self):
return [["ring", "hold", "nl"], return [["ring", "hold", "nl"],
["ring", "hold", "pl"], ["ring", "hold", "pl"],
["ring", "hold", "fl"], ["ring", "hold", "fl"],
["ring", "studhi", "fl"],
["ring", "omahahi", "pl"] ["ring", "omahahi", "pl"]
] ]
def determineGameType(self, handText): def determineGameType(self, handText):
# Cheating with this regex, only support nlhe at the moment """return dict with keys/values:
'type' in ('ring', 'tour')
'limitType' in ('nl', 'cn', 'pl', 'cp', 'fl')
'base' in ('hold', 'stud', 'draw')
'category' in ('holdem', 'omahahi', omahahilo', 'razz', 'studhi', 'studhilo', 'fivedraw', '27_1draw', '27_3draw', 'badugi')
'hilo' in ('h','l','s')
'smallBlind' int?
'bigBlind' int?
'smallBet'
'bigBet'
'currency' in ('USD', 'EUR', 'T$', <countrycode>)
or None if we fail to get the info """
#(TODO: which parts are optional/required?)
# Blinds $0.50/$1 PL Omaha - 2008/12/07 - 21:59:48 # Blinds $0.50/$1 PL Omaha - 2008/12/07 - 21:59:48
# Blinds $0.05/$0.10 NL Hold'em - 2009/02/21 - 11:21:57 # Blinds $0.05/$0.10 NL Hold'em - 2009/02/21 - 11:21:57
# $0.25/$0.50 7 Card Stud - 2008/12/05 - 21:43:59 # $0.25/$0.50 7 Card Stud - 2008/12/05 - 21:43:59
@ -77,37 +102,47 @@ class Everleaf(HandHistoryConverter):
# ***** Hand history for game #75065769 ***** # ***** Hand history for game #75065769 *****
# Blinds 10/20 NL Hold'em - 2009/02/25 - 17:30:32 # Blinds 10/20 NL Hold'em - 2009/02/25 - 17:30:32
# Table 2 # Table 2
info = {'type':'ring'}
structure = "" # nl, pl, cn, cp, fl
game = ""
m = self.re_GameInfo.search(handText) m = self.re_GameInfo.search(handText)
if m == None: if not m:
logging.debug("Gametype didn't match")
return None return None
if m.group('LTYPE') == "NL":
structure = "nl"
elif m.group('LTYPE') == "PL":
structure = "pl"
else:
structure = "fl" # we don't support it, but there should be how to detect it at least.
if m.group('GAME') == "Hold\'em": mg = m.groupdict()
game = "hold"
elif m.group('GAME') == "Omaha":
game = "omahahi"
elif m.group('GAME') == "7 Card Stud":
game = "studhi" # Everleaf currently only does Hi stud
gametype = ["ring", game, structure, m.group('SB'), m.group('BB')] # translations from captured groups to our info strings
limits = { 'NL':'nl', 'PL':'pl', '':'fl' }
games = { # base, category
"Hold'em" : ('hold','holdem'),
'Omaha' : ('hold','omahahi'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
if 'LIMIT' in mg:
info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
(info['base'], info['category']) = games[mg['GAME']]
if 'SB' in mg:
info['sb'] = mg['SB']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
if not self.debugging and info['base']=='stud':
logging.warning("Not processing Everleaf Stud hand")
return None
return info
return gametype
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText) m = self.re_HandInfo.search(hand.handText)
if(m == None): if(m == None):
logging.info("Didn't match re_HandInfo") logging.info("Didn't match re_HandInfo")
logging.info(hand.handtext) logging.info(hand.handText)
return None return None
logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE'))) logging.debug("HID %s, Table %s" % (m.group('HID'), m.group('TABLE')))
hand.handid = m.group('HID') hand.handid = m.group('HID')
@ -138,21 +173,44 @@ class Everleaf(HandHistoryConverter):
# PREFLOP = ** Dealing down cards ** # PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river. # This re fails if, say, river is missing; then we don't get the ** that starts the river.
#m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\* \[ (?P<FLOP1>\S\S), (?P<FLOP2>\S\S), (?P<FLOP3>\S\S) \])?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\* \[ (?P<TURN1>\S\S) \])?(?P<TURN>.*?\*\*)?( Dealing River \*\* \[ (?P<RIVER1>\S\S) \])?(?P<RIVER>.*)', hand.handText,re.DOTALL) #m = re.search('(\*\* Dealing down cards \*\*\n)(?P<PREFLOP>.*?\n\*\*)?( Dealing Flop \*\* \[ (?P<FLOP1>\S\S), (?P<FLOP2>\S\S), (?P<FLOP3>\S\S) \])?(?P<FLOP>.*?\*\*)?( Dealing Turn \*\* \[ (?P<TURN1>\S\S) \])?(?P<TURN>.*?\*\*)?( Dealing River \*\* \[ (?P<RIVER1>\S\S) \])?(?P<RIVER>.*)', hand.handText,re.DOTALL)
if hand.gametype['base'] == 'hold':
m = re.search(r"\*\* Dealing down cards \*\*(?P<PREFLOP>.+(?=\*\* Dealing Flop \*\*)|.+)" m = re.search(r"\*\* Dealing down cards \*\*(?P<PREFLOP>.+(?=\*\* Dealing Flop \*\*)|.+)"
r"(\*\* Dealing Flop \*\*(?P<FLOP> \[ \S\S, \S\S, \S\S \].+(?=\*\* Dealing Turn \*\*)|.+))?" r"(\*\* Dealing Flop \*\*(?P<FLOP> \[ \S\S, \S\S, \S\S \].+(?=\*\* Dealing Turn \*\*)|.+))?"
r"(\*\* Dealing Turn \*\*(?P<TURN> \[ \S\S \].+(?=\*\* Dealing River \*\*)|.+))?" r"(\*\* Dealing Turn \*\*(?P<TURN> \[ \S\S \].+(?=\*\* Dealing River \*\*)|.+))?"
r"(\*\* Dealing River \*\*(?P<RIVER> \[ \S\S \].+))?", hand.handText,re.DOTALL) r"(\*\* Dealing River \*\*(?P<RIVER> \[ \S\S \].+))?", hand.handText,re.DOTALL)
elif hand.gametype['base'] == 'stud':
m = re.search(r"(?P<ANTES>.+(?=\*\* Dealing down cards \*\*)|.+)"
r"(\*\* Dealing down cards \*\*(?P<THIRD>.+(?=\*\*\*\* dealing 4th street \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing 4th street \*\*\*\*(?P<FOURTH>.+(?=\*\*\*\* dealing 5th street \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing 5th street \*\*\*\*(?P<FIFTH>.+(?=\*\*\*\* dealing 6th street \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing 6th street \*\*\*\*(?P<SIXTH>.+(?=\*\*\*\* dealing river \*\*\*\*)|.+))?"
r"(\*\*\*\* dealing river \*\*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL)
hand.addStreets(m) hand.addStreets(m)
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
#print "DEBUG " + street + ":" # If this has been called, street is a street which gets dealt community cards by type hand
#print hand.streets.group(street) + "\n" # but it might be worth checking somehow.
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) # if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
m = self.re_Board.search(hand.streets.group(street)) logging.debug("readCommunityCards (%s)" % street)
hand.setCommunityCards(street, m.group('CARDS').split(', ')) m = self.re_Board.search(hand.streets[street])
cards = m.group('CARDS')
cards = [card.strip() for card in cards.split(',')]
hand.setCommunityCards(street=street, cards=cards)
def readAntes(self, hand):
logging.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m:
logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL)
if m:
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
else:
logging.warning("No bringin found.")
def readBlinds(self, hand): def readBlinds(self, hand):
m = self.re_PostSB.search(hand.handText) m = self.re_PostSB.search(hand.handText)
@ -176,16 +234,24 @@ class Everleaf(HandHistoryConverter):
# "2c, qh" -> ["2c","qc"] # "2c, qh" -> ["2c","qc"]
# Also works with Omaha hands. # Also works with Omaha hands.
cards = m.group('CARDS') cards = m.group('CARDS')
cards = cards.split(', ') cards = [card.strip() for card in cards.split(',')]
hand.addHoleCards(cards, m.group('PNAME')) hand.addHoleCards(cards, m.group('PNAME'))
else: else:
#Not involved in hand #Not involved in hand
hand.involved = False hand.involved = False
def readStudPlayerCards(self, hand, street):
# lol. see Plymouth.txt
logging.warning("Everleaf readStudPlayerCards is only a stub.")
#~ if street in ('THIRD', 'FOURTH', 'FIFTH', 'SIXTH'):
#~ hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = [], open = [])
def readAction(self, hand, street): def readAction(self, hand, street):
logging.debug("readAction (%s)" % street) logging.debug("readAction (%s)" % street)
m = self.re_Action.finditer(hand.streets.group(street)) m = self.re_Action.finditer(hand.streets[street])
for action in m: for action in m:
logging.debug("%s %s" % (action.group('ATYPE'), action.groupdict()))
if action.group('ATYPE') == ' raises': if action.group('ATYPE') == ' raises':
hand.addCallandRaise( street, action.group('PNAME'), action.group('BET') ) hand.addCallandRaise( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls': elif action.group('ATYPE') == ' calls':
@ -196,6 +262,8 @@ class Everleaf(HandHistoryConverter):
hand.addFold( street, action.group('PNAME')) hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == ' checks': elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME')) hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == ' complete to':
hand.addComplete( street, action.group('PNAME'), action.group('BET'))
else: else:
logging.debug("Unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)) logging.debug("Unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),))
@ -209,6 +277,7 @@ class Everleaf(HandHistoryConverter):
logging.debug("readShowdownActions %s %s" %(cards, shows.group('PNAME'))) logging.debug("readShowdownActions %s %s" %(cards, shows.group('PNAME')))
hand.addShownCards(cards, shows.group('PNAME')) hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand): def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText): for m in self.re_CollectPot.finditer(hand.handText):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT')) hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
@ -226,11 +295,21 @@ class Everleaf(HandHistoryConverter):
if __name__ == "__main__": if __name__ == "__main__":
c = Configuration.Config() parser = OptionParser()
if len(sys.argv) == 1: parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="-")
testfile = "regression-test-files/everleaf/plo/Naos.txt" parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
else: parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
testfile = sys.argv[1] parser.add_option("-q", "--quiet",
e = Everleaf(c, testfile) action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
e.processFile() parser.add_option("-v", "--verbose",
print str(e) action="store_const", const=logging.INFO, dest="verbosity")
parser.add_option("--vv",
action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = Everleaf(in_path = options.ipath, out_path = options.opath, follow = options.follow, autostart=True, debugging=True)

1
pyfpdb/Exceptions.py Normal file
View File

@ -0,0 +1 @@
class FpdbParseError(Exception): pass

View File

@ -1054,6 +1054,137 @@ class FpdbSQLQueries:
elif(self.dbname == 'SQLite'): elif(self.dbname == 'SQLite'):
self.query['playerStatsByPosition'] = """ """ self.query['playerStatsByPosition'] = """ """
if(self.dbname == 'MySQL InnoDB'):
self.query['playerStatsByPositionAndHoleCards'] = """
SELECT
concat(upper(stats.limitType), ' '
,concat(upper(substring(stats.category,1,1)),substring(stats.category,2) ), ' '
,stats.name, ' $'
,cast(trim(leading ' ' from
case when stats.bigBlind < 100 then format(stats.bigBlind/100.0,2)
else format(stats.bigBlind/100.0,0)
end ) as char)
) AS Game
,case when stats.PlPosition = -2 then 'BB'
when stats.PlPosition = -1 then 'SB'
when stats.PlPosition = 0 then 'Btn'
when stats.PlPosition = 1 then 'CO'
when stats.PlPosition = 2 then 'MP'
when stats.PlPosition = 5 then 'EP'
else '??'
end AS PlPosition
/*,stats.n*/,hprof2.n
/*,stats.vpip*/,0
/*,stats.pfr*/,0
/*,stats.saw_f*/,0
/*,stats.sawsd*/,0
/*,stats.wtsdwsf*/,0
/*,stats.wmsd*/,0
/*,stats.FlAFq*/,0
/*,stats.TuAFq*/,0
/*,stats.RvAFq*/,0
/*,stats.PoFAFq*/,0
/* if you have handsactions data the next 3 fields should give same answer as
following 3 commented out fields */
/*,stats.Net
,stats.BBper100
,stats.Profitperhand*/
,format(hprof2.sum_profit/100.0,2) AS Net
/*,format((hprof2.sum_profit/(stats.bigBlind+0.0)) / (stats.n/100.0),2)*/,0
AS BBlPer100
,hprof2.profitperhand AS Profitperhand
,format(hprof2.variance,2) AS Variance
FROM
(select /* stats from hudcache */
gt.base
,gt.category
,upper(gt.limitType) as limitType
,s.name
,gt.bigBlind
,hc.gametypeId
,case when hc.position = 'B' then -2
when hc.position = 'S' then -1
when hc.position = 'D' then 0
when hc.position = 'C' then 1
when hc.position = 'M' then 2
when hc.position = 'E' then 5
else 9
end as PlPosition
,sum(HDs) AS n
,format(100.0*sum(street0VPI)/sum(HDs),1) AS vpip
,format(100.0*sum(street0Aggr)/sum(HDs),1) AS pfr
,format(100.0*sum(street1Seen)/sum(HDs),1) AS saw_f
,format(100.0*sum(sawShowdown)/sum(HDs),1) AS sawsd
,case when sum(street1Seen) = 0 then 'oo'
else format(100.0*sum(sawShowdown)/sum(street1Seen),1)
end AS wtsdwsf
,case when sum(sawShowdown) = 0 then 'oo'
end AS wtsdwsf
,case when sum(sawShowdown) = 0 then 'oo'
else format(100.0*sum(wonAtSD)/sum(sawShowdown),1)
end AS wmsd
,case when sum(street1Seen) = 0 then 'oo'
else format(100.0*sum(street1Aggr)/sum(street1Seen),1)
end AS FlAFq
,case when sum(street2Seen) = 0 then 'oo'
else format(100.0*sum(street2Aggr)/sum(street2Seen),1)
end AS TuAFq
,case when sum(street3Seen) = 0 then 'oo'
else format(100.0*sum(street3Aggr)/sum(street3Seen),1)
end AS RvAFq
,case when sum(street1Seen)+sum(street2Seen)+sum(street3Seen) = 0 then 'oo'
else format(100.0*(sum(street1Aggr)+sum(street2Aggr)+sum(street3Aggr))
/(sum(street1Seen)+sum(street2Seen)+sum(street3Seen)),1)
end AS PoFAFq
,format(sum(totalProfit)/100.0,2) AS Net
,format((sum(totalProfit)/(gt.bigBlind+0.0)) / (sum(HDs)/100.0),2)
AS BBper100
,format( (sum(totalProfit)/100.0) / sum(HDs), 4) AS Profitperhand
from Gametypes gt
inner join Sites s on s.Id = gt.siteId
inner join HudCache hc on hc.gameTypeId = gt.Id
where hc.playerId in <player_test>
# use <gametype_test> here ?
group by gt.base
,gt.category
,upper(gt.limitType)
,s.name
,gt.bigBlind
,hc.gametypeId
,PlPosition
) stats
inner join
( select # profit from handsplayers/handsactions
hprof.gameTypeId,
case when hprof.position = 'B' then -2
when hprof.position = 'S' then -1
when hprof.position in ('3','4') then 2
when hprof.position in ('6','7') then 5
else hprof.position
end as PlPosition,
sum(hprof.profit) as sum_profit,
avg(hprof.profit/100.0) as profitperhand,
variance(hprof.profit/100.0) as variance,
count(*) as n
from
(select hp.handId, h.gameTypeId, hp.position, hp.winnings, SUM(ha.amount)
costs, hp.winnings - SUM(ha.amount) profit
from HandsPlayers hp
inner join Hands h ON h.id = hp.handId
left join HandsActions ha ON ha.handPlayerId = hp.id
where hp.playerId in <player_test>
# use <gametype_test> here ?
and hp.tourneysPlayersId IS NULL
and ((hp.card1Value = <first_card> and hp.card2Value = <second_card>) or (hp.card1Value = <second_card> and hp.card2Value = <first_card>))
group by hp.handId, h.gameTypeId, hp.position, hp.winnings
) hprof
group by hprof.gameTypeId, PlPosition
) hprof2
on ( hprof2.gameTypeId = stats.gameTypeId
and hprof2.PlPosition = stats.PlPosition)
order by stats.category, stats.limittype, stats.bigBlind, cast(stats.PlPosition as signed)
"""
if __name__== "__main__": if __name__== "__main__":
from optparse import OptionParser from optparse import OptionParser

View File

@ -1,4 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi # Copyright 2008, Carl Gherardi
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -18,7 +20,6 @@
import sys import sys
import logging import logging
import Configuration
from HandHistoryConverter import * from HandHistoryConverter import *
# FullTilt HH Format converter # FullTilt HH Format converter
@ -26,33 +27,44 @@ from HandHistoryConverter import *
class FullTilt(HandHistoryConverter): class FullTilt(HandHistoryConverter):
# Static regexes # Static regexes
re_GameInfo = re.compile('- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LTYPE>(No|Pot)? )?Limit (?P<GAME>(Hold\'em|Omaha|Razz))') re_GameInfo = re.compile('- (?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<LIMIT>(No Limit|Pot Limit|Limit))? (?P<GAME>(Hold\'em|Omaha Hi|Razz))')
re_SplitHands = re.compile(r"\n\n+") re_SplitHands = re.compile(r"\n\n+")
re_HandInfo = re.compile('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)') re_HandInfo = re.compile('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[- a-zA-Z]+) (\((?P<TABLEATTRIBUTES>.+)\) )?- \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) (Ante \$(?P<ANTE>[.0-9]+) )?- (?P<GAMETYPE>[a-zA-Z\' ]+) - (?P<DATETIME>.*)')
re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE) re_Button = re.compile('^The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[.0-9]+)\)\n') re_PlayerInfo = re.compile('Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$(?P<CASH>[.0-9]+)\)\n')
re_Board = re.compile(r"\[(?P<CARDS>.+)\]") re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
def __init__(self, config, file): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
print "Initialising FullTilt converter class" """\
HandHistoryConverter.__init__(self, config, file, sitename="FullTilt") # Call super class init. in_path (default '-' = sys.stdin)
self.sitename = "FullTilt" out_path (default '-' = sys.stdout)
self.setFileType("text", "cp1252") follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="FullTilt", follow=follow)
logging.info("Initialising FullTilt converter class")
self.filetype = "text"
self.codepage = "cp1252"
if autostart:
self.start()
def compilePlayerRegexs(self):
player_re = "(?P<PNAME>" + "|".join(map(re.escape, self.players)) + ")" def compilePlayerRegexs(self, hand):
print "DEBUG player_re: " + player_re players = set([player[1] for player in hand.players])
self.re_PostSB = re.compile(r"^%s posts the small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE) if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
self.re_PostBB = re.compile(r"^%s posts (the big blind of )?\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE) # we need to recompile the player regexs.
self.re_Antes = re.compile(r"^%s antes \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE) self.compiledPlayers = players
self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE) player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE) logging.debug("player_re: " + player_re)
self.re_HeroCards = re.compile(r"^Dealt to %s \[(?P<CARDS>[AKQJT0-9hcsd ]+)\]( \[(?P<NEWCARD>[AKQJT0-9hcsd ]+)\])?" % player_re, re.MULTILINE) self.re_PostSB = re.compile(r"^%s posts the small blind of \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| calls| folds)(\s\$(?P<BET>[.\d]+))?" % player_re, re.MULTILINE) self.re_PostBB = re.compile(r"^%s posts (the big blind of )?\$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE) self.re_Antes = re.compile(r"^%s antes \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE) self.re_BringIn = re.compile(r"^%s brings in for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE) self.re_PostBoth = re.compile(r"^%s posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE) self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s(?P<ATYPE> bets| checks| raises to| completes it to| calls| folds)(\s\$(?P<BET>[.\d]+))?" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*)" % player_re, re.MULTILINE)
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile(r"^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
def readSupportedGames(self): def readSupportedGames(self):
@ -68,30 +80,36 @@ class FullTilt(HandHistoryConverter):
# Full Tilt Poker Game #10773265574: Table Butte (6 max) - $0.01/$0.02 - Pot Limit Hold'em - 21:33:46 ET - 2009/02/21 # Full Tilt Poker Game #10773265574: Table Butte (6 max) - $0.01/$0.02 - Pot Limit Hold'em - 21:33:46 ET - 2009/02/21
# Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09 # Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09
# Full Tilt Poker Game #10809877615: Table Danville - $0.50/$1 Ante $0.10 - Limit Razz - 21:47:27 ET - 2009/02/23 # Full Tilt Poker Game #10809877615: Table Danville - $0.50/$1 Ante $0.10 - Limit Razz - 21:47:27 ET - 2009/02/23
structure = "" # nl, pl, cn, cp, fl info = {'type':'ring'}
game = ""
m = self.re_GameInfo.search(handText) m = self.re_GameInfo.search(handText)
if m.group('LTYPE') == "No ": if not m:
structure = "nl" return None
elif m.group('LTYPE') == "Pot ":
structure = "pl"
elif m.group('LTYPE') == None:
structure = "fl"
if m.group('GAME') == "Hold\'em": mg = m.groupdict()
game = "hold"
elif m.group('GAME') == "Omaha":
game = "omahahi"
elif m.group('GAME') == "Razz":
game = "razz"
logging.debug("HandInfo: %s", m.groupdict()) # translations from captured groups to our info strings
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
games = { # base, category
"Hold'em" : ('hold','holdem'),
'Omaha Hi' : ('hold','omahahi'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
if 'LIMIT' in mg:
info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
(info['base'], info['category']) = games[mg['GAME']]
if 'SB' in mg:
info['sb'] = mg['SB']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
gametype = ["ring", game, structure, m.group('SB'), m.group('BB')] return info
return gametype
def readHandInfo(self, hand): def readHandInfo(self, hand):
m = self.re_HandInfo.search(hand.handText,re.DOTALL) m = self.re_HandInfo.search(hand.handText,re.DOTALL)
@ -122,14 +140,13 @@ class FullTilt(HandHistoryConverter):
def markStreets(self, hand): def markStreets(self, hand):
# PREFLOP = ** Dealing down cards ** # PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
if self.gametype[1] == "hold" or self.gametype[1] == "omaha": if hand.gametype['base'] == 'hold':
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)" m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?" r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?" r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL) r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
elif self.gametype[1] == "razz": elif hand.gametype['base'] == "stud": # or should this be gametype['category'] == 'razz'
m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)"
r"(\*\*\* 3RD STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?" r"(\*\*\* 3RD STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?"
r"(\*\*\* 4TH STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5TH STREET \*\*\*)|.+))?" r"(\*\*\* 4TH STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5TH STREET \*\*\*)|.+))?"
@ -141,7 +158,7 @@ class FullTilt(HandHistoryConverter):
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
#print "DEBUG readCommunityCards:", street, hand.streets.group(street) #print "DEBUG readCommunityCards:", street, hand.streets.group(street)
m = self.re_Board.search(hand.streets.group(street)) m = self.re_Board.search(hand.streets[street])
hand.setCommunityCards(street, m.group('CARDS').split(' ')) hand.setCommunityCards(street, m.group('CARDS').split(' '))
@ -165,8 +182,11 @@ class FullTilt(HandHistoryConverter):
def readBringIn(self, hand): def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL) m = self.re_BringIn.search(hand.handText,re.DOTALL)
logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN'))) if m:
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN')) logging.debug("Player bringing in: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
else:
logging.warning("No bringin found")
def readButton(self, hand): def readButton(self, hand):
hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON')) hand.buttonpos = int(self.re_Button.search(hand.handText).group('BUTTON'))
@ -180,29 +200,73 @@ class FullTilt(HandHistoryConverter):
hand.hero = m.group('PNAME') hand.hero = m.group('PNAME')
# "2c, qh" -> set(["2c","qc"]) # "2c, qh" -> set(["2c","qc"])
# Also works with Omaha hands. # Also works with Omaha hands.
cards = m.group('CARDS') cards = m.group('NEWCARDS')
cards = cards.split(' ') cards = [c.strip() for c in cards.split(' ')]
hand.addHoleCards(cards, m.group('PNAME')) hand.addHoleCards(cards, m.group('PNAME'))
def readPlayerCards(self, hand, street): def readStudPlayerCards(self, hand, street):
#Used for stud hands - borrows the HeroCards regex for now. # This could be the most tricky one to get right.
m = self.re_HeroCards.finditer(hand.streets.group(street)) # It looks for cards dealt in 'street',
print "DEBUG: razz/stud readPlayerCards" # which may or may not be in the section of the hand designated 'street' by markStreets earlier.
print hand.streets.group(street) # Here's an example at FTP of what 'THIRD' and 'FOURTH' look like to hero PokerAscetic
#
#"*** 3RD STREET ***
#Dealt to BFK23 [Th]
#Dealt to cutiepr1nnymaid [8c]
#Dealt to PokerAscetic [7c 8s] [3h]
#..."
#
#"*** 4TH STREET ***
#Dealt to cutiepr1nnymaid [8c] [2s]
#Dealt to PokerAscetic [7c 8s 3h] [5s]
#..."
#Note that hero's first two holecards are only reported at 3rd street as 'old' cards.
logging.debug("readStudPlayerCards")
m = self.re_HeroCards.finditer(hand.streets[street])
for player in m: for player in m:
logging.debug(player.groupdict()) logging.debug(player.groupdict())
cards = player.group('CARDS') (pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
if player.group('NEWCARD') != None: if oldcards:
print cards oldcards = [c.strip() for c in oldcards.split(' ')]
cards = cards + " " + player.group('NEWCARD') if newcards:
cards = cards.split(' ') newcards = [c.strip() for c in newcards.split(' ')]
hand.addPlayerCards(cards, player.group('PNAME')) # options here:
# (1) we trust the hand will know what to do -- probably check that the old cards match what it already knows, and add the newcards to this street.
# (2) we're the experts at this particular history format and we know how we're going to be called (once for each street in Hand.streetList)
# so call addPlayerCards with the appropriate information.
# I favour (2) here but I'm afraid it is rather stud7-specific.
# in the following, the final list of cards will be in 'newcards' whilst if the first list exists (most of the time it does) it will be in 'oldcards'
if street=='ANTES':
return
elif street=='THIRD':
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# hero: [xx][o]
# others: [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# 4th:
# hero: [xxo] [o]
# others: [o] [o]
# 5th:
# hero: [xxoo] [o]
# others: [oo] [o]
# 6th:
# hero: [xxooo] [o]
# others: [ooo] [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
elif street=='SEVENTH' and newcards:
# hero: [xxoooo] [x]
# others: not reported.
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
def readAction(self, hand, street): def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets.group(street)) m = self.re_Action.finditer(hand.streets[street])
for action in m: for action in m:
if action.group('ATYPE') == ' raises to': if action.group('ATYPE') == ' raises to':
hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') ) hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' completes it to':
hand.addComplete( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls': elif action.group('ATYPE') == ' calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') ) hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' bets': elif action.group('ATYPE') == ' bets':
@ -212,7 +276,7 @@ class FullTilt(HandHistoryConverter):
elif action.group('ATYPE') == ' checks': elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME')) hand.addCheck( street, action.group('PNAME'))
else: else:
print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),) print "DEBUG: unimplemented readAction: '%s' '%s'" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand): def readShowdownActions(self, hand):
@ -234,12 +298,20 @@ class FullTilt(HandHistoryConverter):
if __name__ == "__main__": if __name__ == "__main__":
c = Configuration.Config() parser = OptionParser()
if len(sys.argv) == 1: parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt")
testfile = "regression-test-files/fulltilt/razz/FT20090223 Danville - $0.50-$1 Ante $0.10 - Limit Razz.txt" parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
else: parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
testfile = sys.argv[1] parser.add_option("-q", "--quiet",
print "Converting: ", testfile action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
e = FullTilt(c, testfile) parser.add_option("-v", "--verbose",
e.processFile() action="store_const", const=logging.INFO, dest="verbosity")
print str(e) parser.add_option("--vv",
action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = FullTilt(in_path = options.ipath, out_path = options.opath, follow = options.follow)

55
pyfpdb/GuiAutoImport.py Normal file → Executable file
View File

@ -26,7 +26,8 @@ import os
import sys import sys
import time import time
import fpdb_import import fpdb_import
from optparse import OptionParser
import Configuration
class GuiAutoImport (threading.Thread): class GuiAutoImport (threading.Thread):
def __init__(self, settings, config): def __init__(self, settings, config):
@ -134,7 +135,8 @@ class GuiAutoImport (threading.Thread):
self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE, self.pipe_to_hud = subprocess.Popen(command, bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True) universal_newlines=True)
else: else:
command = self.config.execution_path('HUD_main.py') command = os.path.join(sys.path[0], 'HUD_main.py')
#command = self.config.execution_path('HUD_main.py') # Hi Ray. Sorry about this, kludging.
bs = 1 bs = 1
self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE, self.pipe_to_hud = subprocess.Popen((command, self.database), bufsize = bs, stdin = subprocess.PIPE,
universal_newlines=True) universal_newlines=True)
@ -214,18 +216,39 @@ if __name__== "__main__":
def destroy(*args): # call back for terminating the main eventloop def destroy(*args): # call back for terminating the main eventloop
gtk.main_quit() gtk.main_quit()
settings = {} # settings = {}
settings['db-host'] = "192.168.1.100" # settings['db-host'] = "192.168.1.100"
settings['db-user'] = "mythtv" # settings['db-user'] = "mythtv"
settings['db-password'] = "mythtv" # settings['db-password'] = "mythtv"
settings['db-databaseName'] = "fpdb" # settings['db-databaseName'] = "fpdb"
settings['hud-defaultInterval'] = 10 # settings['hud-defaultInterval'] = 10
settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic' # settings['hud-defaultPath'] = 'C:/Program Files/PokerStars/HandHistory/nutOmatic'
settings['callFpdbHud'] = True # settings['callFpdbHud'] = True
parser = OptionParser()
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True, help="don't start gui")
(options, sys.argv) = parser.parse_args()
config = Configuration.Config()
# db = fpdb_db.fpdb_db()
settings = {}
if os.name == 'nt': settings['os'] = 'windows'
else: settings['os'] = 'linuxmac'
settings.update(config.get_db_parameters('fpdb'))
settings.update(config.get_tv_parameters())
settings.update(config.get_import_parameters())
settings.update(config.get_default_paths())
if(options.gui == True):
i = GuiAutoImport(settings, config)
main_window = gtk.Window()
main_window.connect('destroy', destroy)
main_window.add(i.mainVBox)
main_window.show()
gtk.main()
else:
pass
i = GuiAutoImport(settings)
main_window = gtk.Window()
main_window.connect("destroy", destroy)
main_window.add(i.mainVBox)
main_window.show()
gtk.main()

View File

@ -44,7 +44,7 @@ class GuiBulkImport():
starttime = time() starttime = time()
if not self.importer.settings['threads'] > 1: if not self.importer.settings['threads'] > 1:
(stored, dups, partial, errs, ttime) = self.importer.runImport() (stored, dups, partial, errs, ttime) = self.importer.runImport()
print 'GuiBulkImport.import_dir done: Stored: %d \tDuplicates: %d \tPartial: %d \tErrors: %d in %s seconds - %d/sec'\ print 'GuiBulkImport.import_dir done: Stored: %d Duplicates: %d Partial: %d Errors: %d in %s seconds - %d/sec'\
% (stored, dups, partial, errs, ttime, stored / ttime) % (stored, dups, partial, errs, ttime, stored / ttime)
else: else:
self.importer.RunImportThreaded() self.importer.RunImportThreaded()

View File

@ -46,9 +46,15 @@ class GuiGraphViewer (threading.Thread):
return self.mainHBox return self.mainHBox
#end def get_vbox #end def get_vbox
def clearGraphData(self):
self.fig.clf()
if self.canvas is not None:
self.canvas.destroy()
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
def generateGraph(self, widget, data): def generateGraph(self, widget, data):
try: self.canvas.destroy() self.clearGraphData()
except AttributeError: pass
sitenos = [] sitenos = []
playerids = [] playerids = []
@ -72,7 +78,6 @@ class GuiGraphViewer (threading.Thread):
print "No player ids found" print "No player ids found"
return return
self.fig = Figure(figsize=(5,4), dpi=100)
#Set graph properties #Set graph properties
self.ax = self.fig.add_subplot(111) self.ax = self.fig.add_subplot(111)
@ -104,7 +109,6 @@ class GuiGraphViewer (threading.Thread):
#Draw plot #Draw plot
self.ax.plot(line,) self.ax.plot(line,)
self.canvas = FigureCanvas(self.fig) # a gtk.DrawingArea
self.graphBox.add(self.canvas) self.graphBox.add(self.canvas)
self.canvas.show() self.canvas.show()
self.exportButton.set_sensitive(True) self.exportButton.set_sensitive(True)
@ -280,6 +284,8 @@ class GuiGraphViewer (threading.Thread):
win.destroy() win.destroy()
def exportGraph (self, widget, data): def exportGraph (self, widget, data):
if self.fig is None:
return # Might want to disable export button until something has been generated.
dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:", dia_chooser = gtk.FileChooserDialog(title="Please choose the directory you wish to export to:",
action=gtk.FILE_CHOOSER_ACTION_OPEN, action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
@ -294,6 +300,7 @@ class GuiGraphViewer (threading.Thread):
dia_chooser.destroy() dia_chooser.destroy()
#TODO: Check to see if file exists #TODO: Check to see if file exists
#NOTE: Dangerous - will happily overwrite any file we have write access too #NOTE: Dangerous - will happily overwrite any file we have write access too
#TODO: This asks for a directory but will take a filename and overwrite it.
self.fig.savefig(self.exportDir, format="png") self.fig.savefig(self.exportDir, format="png")
def __init__(self, db, settings, querylist, config, debug=True): def __init__(self, db, settings, querylist, config, debug=True):
@ -360,6 +367,7 @@ class GuiGraphViewer (threading.Thread):
graphButton.connect("clicked", self.generateGraph, "cliced data") graphButton.connect("clicked", self.generateGraph, "cliced data")
graphButton.show() graphButton.show()
self.fig = None
self.exportButton=gtk.Button("Export to File") self.exportButton=gtk.Button("Export to File")
self.exportButton.connect("clicked", self.exportGraph, "show clicked") self.exportButton.connect("clicked", self.exportGraph, "show clicked")
self.exportButton.set_sensitive(False) self.exportButton.set_sensitive(False)
@ -374,6 +382,9 @@ class GuiGraphViewer (threading.Thread):
self.leftPanelBox.show() self.leftPanelBox.show()
self.graphBox.show() self.graphBox.show()
self.fig = Figure(figsize=(5,4), dpi=100)
self.canvas = None
################################# #################################
# #
# self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""") # self.db.cursor.execute("""select UNIX_TIMESTAMP(handStart) as time, id from Hands ORDER BY time""")

View File

@ -35,6 +35,9 @@ class GuiPositionalStats (threading.Thread):
self.activesite = data self.activesite = data
print "DEBUG: activesite set to %s" %(self.activesite) print "DEBUG: activesite set to %s" %(self.activesite)
def cardCallback(self, widget, data=None):
print "DEBUG: %s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])
def refreshStats(self, widget, data): def refreshStats(self, widget, data):
try: self.stats_table.destroy() try: self.stats_table.destroy()
except AttributeError: pass except AttributeError: pass
@ -110,6 +113,27 @@ class GuiPositionalStats (threading.Thread):
vbox.pack_start(hbox, False, True, 0) vbox.pack_start(hbox, False, True, 0)
hbox.show() hbox.show()
def fillCardsFrame(self, vbox):
hbox1 = gtk.HBox(True,0)
hbox1.show()
vbox.pack_start(hbox1, True, True, 0)
cards = [ "A", "K","Q","J","T","9","8","7","6","5","4","3","2" ]
for j in range(0, len(cards)):
hbox1 = gtk.HBox(True,0)
hbox1.show()
vbox.pack_start(hbox1, True, True, 0)
for i in range(0, len(cards)):
if i < (j + 1):
suit = "o"
else:
suit = "s"
button = gtk.ToggleButton("%s%s%s" %(cards[i], cards[j], suit))
button.connect("toggled", self.cardCallback, "%s%s%s" %(cards[i], cards[j], suit))
hbox1.pack_start(button, True, True, 0)
button.show()
def createPlayerLine(self, hbox, site, player): def createPlayerLine(self, hbox, site, player):
if(self.buttongroup == None): if(self.buttongroup == None):
button = gtk.RadioButton(None, site + " id:") button = gtk.RadioButton(None, site + " id:")
@ -164,6 +188,15 @@ class GuiPositionalStats (threading.Thread):
self.fillPlayerFrame(vbox) self.fillPlayerFrame(vbox)
playerFrame.add(vbox) playerFrame.add(vbox)
cardsFrame = gtk.Frame("Cards:")
cardsFrame.set_label_align(0.0, 0.0)
cardsFrame.show()
vbox = gtk.VBox(False, 0)
vbox.show()
self.fillCardsFrame(vbox)
cardsFrame.add(vbox)
statsFrame = gtk.Frame("Stats:") statsFrame = gtk.Frame("Stats:")
statsFrame.set_label_align(0.0, 0.0) statsFrame.set_label_align(0.0, 0.0)
statsFrame.show() statsFrame.show()

View File

@ -2,18 +2,7 @@
<FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd"> <FreePokerToolsConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FreePokerToolsConfig.xsd">
<supported_sites> <supported_sites>
<site enabled="True" <site enabled="True" site_name="PokerStars" table_finder="PokerStars.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/PokerStars/" HH_path="~/.wine/drive_c/Program Files/PokerStars/HandHistory/abc/" decoder="pokerstars_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
site_name="PokerStars"
table_finder="PokerStars.exe"
screen_name="ENTER HERO NAME"
site_path=""
HH_path=""
decoder="pokerstars_decode_table"
converter="passthrough"
bgcolor="#000000"
fgcolor="#FFFFFF"
hudopacity="1.0"
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<layout max="8" width="792" height="546" fav_seat="0"> <layout max="8" width="792" height="546" fav_seat="0">
<location seat="1" x="684" y="61"> </location> <location seat="1" x="684" y="61"> </location>
<location seat="2" x="689" y="239"> </location> <location seat="2" x="689" y="239"> </location>
@ -60,18 +49,7 @@
<location seat="2" x="10" y="288"> </location> <location seat="2" x="10" y="288"> </location>
</layout> </layout>
</site> </site>
<site enabled="True" <site enabled="True" site_name="Full Tilt" table_finder="FullTiltPoker.exe" screen_name="DO NOT NEED THIS YET" site_path="~/.wine/drive_c/Program Files/Full Tilt Poker/" HH_path="~/.wine/drive_c/Program Files/Full Tilt Poker/HandHistory/abc/" decoder="fulltilt_decode_table" converter="passthrough" supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
site_name="Full Tilt Poker"
table_finder="FullTiltPoker.exe"
screen_name="ENTER HERO NAME"
site_path=""
HH_path=""
decoder="fulltilt_decode_table"
converter="passthrough"
bgcolor="#000000"
fgcolor="#FFFFFF"
hudopacity="1.0"
supported_games="holdem,razz,omahahi,omahahilo,studhi,studhilo">
<layout fav_seat="0" height="547" max="8" width="794"> <layout fav_seat="0" height="547" max="8" width="794">
<location seat="1" x="640" y="64"> </location> <location seat="1" x="640" y="64"> </location>
<location seat="2" x="650" y="230"> </location> <location seat="2" x="650" y="230"> </location>
@ -106,41 +84,39 @@
<location seat="9" x="70" y="53"> </location> <location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </site>
<site enabled="False" <site enabled="False" site_name="Everleaf" table_finder="Everleaf.exe" screen_name="DO NOT NEED THIS YET" site_path="" HH_path="" decoder="everleaf_decode_table" converter="EverleafToFpdb" supported_games="holdem">
site_name="Everleaf" <layout fav_seat="0" height="547" max="8" width="794">
table_finder="Poker.exe" <location seat="1" x="640" y="64"> </location>
screen_name="ENTER HERO NAME" <location seat="2" x="650" y="230"> </location>
site_path="" <location seat="3" x="650" y="385"> </location>
HH_path="" <location seat="4" x="588" y="425"> </location>
decoder="Unknown" <location seat="5" x="92" y="425"> </location>
converter="EverleafToFpdb" <location seat="6" x="0" y="373"> </location>
supported_games="holdem,razz,omahahi,omahahilo,studhi" <location seat="7" x="0" y="223"> </location>
fgcolor="#48D1CC" <location seat="8" x="25" y="50"> </location>
bgcolor="#000000"
opacity="0.75">
<layout fav_seat="0" height="546" max="6" width="792">
<location seat="1" x="581" y="109"> </location>
<location seat="2" x="605" y="287"> </location>
<location seat="3" x="544" y="423"> </location>
<location seat="4" x="80" y="393"> </location>
<location seat="5" x="5" y="284"> </location>
<location seat="6" x="38" y="108"> </location>
</layout> </layout>
<layout fav_seat="0" height="546" max="10" width="792"> <layout fav_seat="0" height="547" max="6" width="794">
<location seat="1" x="473" y="78"> </location> <location seat="1" x="640" y="58"> </location>
<location seat="2" x="650" y="93"> </location> <location seat="2" x="654" y="288"> </location>
<location seat="3" x="670" y="219"> </location> <location seat="3" x="615" y="424"> </location>
<location seat="4" x="633" y="358"> </location> <location seat="4" x="70" y="421"> </location>
<location seat="5" x="437" y="393"> </location> <location seat="5" x="0" y="280"> </location>
<location seat="6" x="249" y="396"> </location> <location seat="6" x="70" y="58"> </location>
<location seat="7" x="42" y="357"> </location>
<location seat="8" x="8" y="218"> </location>
<location seat="9" x="35" y="89"> </location>
<location seat="10" x="217" y="78"> </location>
</layout> </layout>
<layout fav_seat="0" height="546" max="2" width="792"> <layout fav_seat="0" height="547" max="2" width="794">
<location seat="1" x="651" y="288"> </location> <location seat="1" x="651" y="288"> </location>
<location seat="2" x="10" y="288"> </location> <location seat="2" x="10" y="288"> </location>
</layout>
<layout fav_seat="0" height="547" max="9" width="794">
<location seat="1" x="634" y="38"> </location>
<location seat="2" x="667" y="184"> </location>
<location seat="3" x="667" y="321"> </location>
<location seat="4" x="667" y="445"> </location>
<location seat="5" x="337" y="459"> </location>
<location seat="6" x="0" y="400"> </location>
<location seat="7" x="0" y="322"> </location>
<location seat="8" x="0" y="181"> </location>
<location seat="9" x="70" y="53"> </location>
</layout> </layout>
</site> </site>
</supported_sites> </supported_sites>
@ -153,7 +129,7 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="razz" rows="2" aux="stud_mucked"> <game cols="3" db="fpdb" game_name="razz" rows="2">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -177,7 +153,7 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="studhi" rows="2" aux="stud_mucked"> <game cols="3" db="fpdb" game_name="studhi" rows="2">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -185,7 +161,7 @@
<stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="1" stat_name="wtsd" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="1" stat_name="wmsd" tip="tip1"> </stat>
</game> </game>
<game cols="3" db="fpdb" game_name="studhilo" rows="2" aux="stud_mucked"> <game cols="3" db="fpdb" game_name="studhilo" rows="2">
<stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat> <stat click="tog_decorate" col="0" popup="default" row="0" stat_name="vpip" tip="tip1"> </stat>
<stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat> <stat click="tog_decorate" col="1" popup="default" row="0" stat_name="pfr" tip="tip1"> </stat>
<stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat> <stat click="tog_decorate" col="2" popup="default" row="0" stat_name="ffreq_1" tip="tip1"> </stat>
@ -220,15 +196,15 @@
<pu_stat pu_stat_name="ffreq_4"> </pu_stat> <pu_stat pu_stat_name="ffreq_4"> </pu_stat>
</pu> </pu>
</popup_windows> </popup_windows>
<import callFpdbHud = "True" interval = "10" hhArchiveBase="~/.fpdb/HandHistories/"></import> <import callFpdbHud = "True" interval = "10" ></import>
<tv combinedStealFold = "True" combined2B3B = "True" combinedPostflop = "True"></tv> <tv combinedStealFold = "True" combined2B3B = "True" combinedPostflop = "True"></tv>
<supported_databases> <supported_databases>
<database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"> </database> <database db_name="fpdb" db_server="mysql" db_ip="localhost" db_user="fpdb" db_pass="YOUR MYSQL PASSWORD" db_type="fpdb"> </database>
</supported_databases> </supported_databases>
<aux_windows> <mucked_windows>
<aw card_ht="42" card_wd="30" class="Stud_mucked" cols="11" deck="Cards01.png" module="Mucked" name="stud_mucked" rows="8"> </aw> <mw mw_name="stud1" format="stud" rows="8" cols="11" deck="Cards01.png" card_wd="30" card_ht="42"> </mw>
</aux_windows> </mucked_windows>
</FreePokerToolsConfig> </FreePokerToolsConfig>

View File

@ -123,8 +123,7 @@ class HUD_main(object):
gtk.gdk.threads_enter() gtk.gdk.threads_enter()
try: try:
self.hud_dict[table_name].update(new_hand_id, config) self.hud_dict[table_name].update(new_hand_id, config)
for m in self.hud_dict[table_name].aux_windows: map(lambda aw: aw.update_gui(new_hand_id), self.hud_dict[table_name].aux_windows)
m.update_gui(new_hand_id)
return False return False
finally: finally:
gtk.gdk.threads_leave() gtk.gdk.threads_leave()
@ -140,7 +139,7 @@ class HUD_main(object):
self.db_connection = Database.Database(self.config, self.db_name, 'temp') self.db_connection = Database.Database(self.config, self.db_name, 'temp')
tourny_finder = re.compile('(\d+) (\d+)') tourny_finder = re.compile('(\d+) (\d+)')
while True: # wait for a new hand number on stdin while 1: # wait for a new hand number on stdin
new_hand_id = sys.stdin.readline() new_hand_id = sys.stdin.readline()
new_hand_id = string.rstrip(new_hand_id) new_hand_id = string.rstrip(new_hand_id)
if new_hand_id == "": # blank line means quit if new_hand_id == "": # blank line means quit
@ -187,7 +186,7 @@ class HUD_main(object):
if tablewindow == None: if tablewindow == None:
# If no client window is found on the screen, complain and continue # If no client window is found on the screen, complain and continue
if is_tournament: if is_tournament:
table_name = tour_number + " " + tab_number table_name = "%s %s" % (tour_number, tab_number)
sys.stderr.write("table name "+table_name+" not found, skipping.\n") sys.stderr.write("table name "+table_name+" not found, skipping.\n")
else: else:
self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, is_tournament, stat_dict, cards) self.create_HUD(new_hand_id, tablewindow, temp_key, max, poker_game, is_tournament, stat_dict, cards)

View File

@ -15,39 +15,25 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
import Configuration
import FpdbRegex
import Hand
import re import re
import sys import sys
import traceback import traceback
import logging import logging
import os import os
import os.path import os.path
import xml.dom.minidom
import codecs
from decimal import Decimal from decimal import Decimal
import operator import operator
import time import time
from copy import deepcopy from copy import deepcopy
from Exceptions import *
class Hand: class Hand:
# def __init__(self, sitename, gametype, sb, bb, string):
UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'} UPS = {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'}
def __init__(self, sitename, gametype, handText): def __init__(self, sitename, gametype, handText):
self.sitename = sitename self.sitename = sitename
self.gametype = gametype self.gametype = gametype
self.handText = handText self.handText = handText
if gametype[1] == "hold" or self.gametype[1] == "omahahi":
self.streetList = ['PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
elif self.gametype[1] == "razz" or self.gametype[1] == "stud" or self.gametype[1] == "stud8":
self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.handid = 0 self.handid = 0
self.sb = gametype[3]
self.bb = gametype[4]
self.tablename = "Slartibartfast" self.tablename = "Slartibartfast"
self.hero = "Hiro" self.hero = "Hiro"
self.maxseats = 10 self.maxseats = 10
@ -58,53 +44,37 @@ class Hand:
self.posted = [] self.posted = []
self.involved = True self.involved = True
self.pot = Pot()
#
# Collections indexed by street names # Collections indexed by street names
# self.bets = {}
self.lastBet = {}
# A MatchObject using a groupnames to identify streets. self.streets = {}
# filled by markStreets() self.actions = {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
self.streets = None self.board = {} # dict from street names to community cards
for street in self.streetList:
# dict from street names to lists of tuples, such as self.streets[street] = "" # portions of the handText, filled by markStreets()
# [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']] self.bets[street] = {}
# actually they're clearly lists but they probably should be tuples. self.lastBet[street] = 0
self.actions = {} self.actions[street] = []
self.board[street] = []
# dict from street names to community cards
self.board = {}
#
# Collections indexed by player names # Collections indexed by player names
# self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards
# dict from player names to lists of hole cards
self.holecards = {}
self.stacks = {} self.stacks = {}
self.collected = [] #list of ?
# dict from player names to amounts collected self.collectees = {} # dict from player names to amounts collected (?)
self.collected = []
self.collectees = {}
# Sets of players # Sets of players
self.shown = set() self.shown = set()
self.folded = set() self.folded = set()
self.action = [] # self.action = []
# Things to do with money
self.pot = Pot()
self.totalpot = None self.totalpot = None
self.totalcollected = None self.totalcollected = None
self.rake = None self.rake = None
self.bets = {}
self.lastBet = {}
for street in self.streetList:
self.bets[street] = {}
self.lastBet[street] = 0
def addPlayer(self, seat, name, chips): def addPlayer(self, seat, name, chips):
"""\ """\
@ -113,68 +83,27 @@ seat (int) indicating the seat
name (string) player name name (string) player name
chips (string) the chips the player has at the start of the hand (can be None) chips (string) the chips the player has at the start of the hand (can be None)
If a player has None chips he won't be added.""" If a player has None chips he won't be added."""
logging.debug("addPlayer: %s %s (%s)" % (seat, name, chips))
if chips is not None: if chips is not None:
self.players.append([seat, name, chips]) self.players.append([seat, name, chips])
self.stacks[name] = Decimal(chips) self.stacks[name] = Decimal(chips)
self.holecards[name] = set() self.holecards[name] = []
self.pot.addPlayer(name) self.pot.addPlayer(name)
for street in self.streetList: for street in self.streetList:
self.bets[street][name] = [] self.bets[street][name] = []
self.holecards[name] = {} # dict from street names.
def addStreets(self, match): def addStreets(self, match):
# go through m and initialise actions to empty list for each street. # go through m and initialise actions to empty list for each street.
if match: if match:
self.streets = match self.streets.update(match.groupdict())
for street in match.groupdict(): logging.debug("markStreets:\n"+ str(self.streets))
if match.group(street) is not None:
self.actions[street] = []
else: else:
logging.error("markstreets didn't match") logging.error("markstreets didn't match")
def addHoleCards(self, cards, player): #def addHoleCards -- to Holdem subclass
"""\
Assigns observed holecards to a player.
cards set of card bigrams e.g. set(['2h','Jc'])
player (string) name of player
"""
#print "DEBUG: addHoleCards", cards,player
try:
self.checkPlayerExists(player)
cards = set([self.card(c) for c in cards])
self.holecards[player].update(cards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def addPlayerCards(self, cards, player):
"""\
Assigns observed cards to a player.
cards set of card bigrams e.g. set(['2h','Jc'])
player (string) name of player
Should probably be merged with addHoleCards
"""
print "DEBUG: addPlayerCards", cards,player
try:
self.checkPlayerExists(player)
cards = set([self.card(c) for c in cards])
self.holecards[player].update(cards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def addShownCards(self, cards, player, holeandboard=None):
"""\
For when a player shows cards for any reason (for showdown or out of choice).
Card ranks will be uppercased
"""
#print "DEBUG: addShownCards", cards,player,holeandboard
if cards is not None:
self.shown.add(player)
self.addHoleCards(cards,player)
elif holeandboard is not None:
holeandboard = set([self.card(c) for c in holeandboard])
board = set([c for s in self.board.values() for c in s])
self.addHoleCards(holeandboard.difference(board),player)
def checkPlayerExists(self,player): def checkPlayerExists(self,player):
@ -182,15 +111,7 @@ Card ranks will be uppercased
print "checkPlayerExists", player, "fail" print "checkPlayerExists", player, "fail"
raise FpdbParseError raise FpdbParseError
def discardHoleCards(self, cards, player):
try:
self.checkPlayerExists(player)
for card in cards:
self.holecards[player].remove(card)
except FpdbParseError, e:
pass
except ValueError:
print "[ERROR] discardHoleCard tried to discard a card %s didn't have" % (player,)
def setCommunityCards(self, street, cards): def setCommunityCards(self, street, cards):
logging.debug("setCommunityCards %s %s" %(street, cards)) logging.debug("setCommunityCards %s %s" %(street, cards))
@ -203,24 +124,23 @@ Card ranks will be uppercased
return c return c
def addAnte(self, player, ante): def addAnte(self, player, ante):
logging.debug("%s %s antes %s" % ('ANTES', player, ante))
if player is not None: if player is not None:
self.bets['ANTES'][player].append(Decimal(ante)) self.bets['ANTES'][player].append(Decimal(ante))
self.stacks[player] -= Decimal(ante) self.stacks[player] -= Decimal(ante)
act = (player, 'posts', "ante", ante, self.stacks[player]==0) act = (player, 'posts', "ante", ante, self.stacks[player]==0)
self.actions['ANTES'].append(act) self.actions['ANTES'].append(act)
#~ self.lastBet['ANTES'] = Decimal(ante)
self.pot.addMoney(player, Decimal(ante)) self.pot.addMoney(player, Decimal(ante))
def addBlind(self, player, blindtype, amount): def addBlind(self, player, blindtype, amount):
# if player is None, it's a missing small blind. # if player is None, it's a missing small blind.
# TODO:
# The situation we need to cover are: # The situation we need to cover are:
# Player in small blind posts # Player in small blind posts
# - this is a bet of 1 sb, as yet uncalled. # - this is a bet of 1 sb, as yet uncalled.
# Player in the big blind posts # Player in the big blind posts
# - this is a call of 1 bb and is the new uncalled # - this is a call of 1 sb and a raise to 1 bb
# #
# If a player posts a big & small blind
# - FIXME: We dont record this for later printing yet
logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount)) logging.debug("addBlind: %s posts %s, %s" % (player, blindtype, amount))
if player is not None: if player is not None:
@ -228,7 +148,7 @@ Card ranks will be uppercased
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
#print "DEBUG %s posts, stack %s" % (player, self.stacks[player]) #print "DEBUG %s posts, stack %s" % (player, self.stacks[player])
act = (player, 'posts', blindtype, amount, self.stacks[player]==0) act = (player, 'posts', blindtype, amount, self.stacks[player]==0)
self.actions['PREFLOP'].append(act) self.actions['BLINDSANTES'].append(act)
self.pot.addMoney(player, Decimal(amount)) self.pot.addMoney(player, Decimal(amount))
if blindtype == 'big blind': if blindtype == 'big blind':
self.lastBet['PREFLOP'] = Decimal(amount) self.lastBet['PREFLOP'] = Decimal(amount)
@ -238,16 +158,10 @@ Card ranks will be uppercased
self.posted = self.posted + [[player,blindtype]] self.posted = self.posted + [[player,blindtype]]
#print "DEBUG: self.posted: %s" %(self.posted) #print "DEBUG: self.posted: %s" %(self.posted)
def addBringIn(self, player, ante):
if player is not None:
self.bets['THIRD'][player].append(Decimal(ante))
self.stacks[player] -= Decimal(ante)
act = (player, 'bringin', "bringin", ante, self.stacks[player]==0)
self.actions['THIRD'].append(act)
self.pot.addMoney(player, Decimal(ante))
def addCall(self, street, player=None, amount=None): def addCall(self, street, player=None, amount=None):
logging.debug("%s %s calls %s" %(street, player, amount))
# Potentially calculate the amount of the call if not supplied # Potentially calculate the amount of the call if not supplied
# corner cases include if player would be all in # corner cases include if player would be all in
if amount is not None: if amount is not None:
@ -281,10 +195,11 @@ Add a raise by amountBy on [street] by [player]
C = Bp - Bc C = Bp - Bc
Rt = Bp + Rb Rt = Bp + Rb
self.bets[street][player].append(C + Rb) self._addRaise(street, player, C, Rb, Rt)
self.stacks[player] -= (C + Rb) #~ self.bets[street][player].append(C + Rb)
self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)] #~ self.stacks[player] -= (C + Rb)
self.lastBet[street] = Rt #~ self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)]
#~ self.lastBet[street] = Rt
def addCallandRaise(self, street, player, amount): def addCallandRaise(self, street, player, amount):
"""\ """\
@ -313,6 +228,7 @@ Add a raise on [street] by [player] to [amountTo]
self._addRaise(street, player, C, Rb, Rt) self._addRaise(street, player, C, Rb, Rt)
def _addRaise(self, street, player, C, Rb, Rt): def _addRaise(self, street, player, C, Rb, Rt):
logging.debug("%s %s raise %s" %(street, player, Rt))
self.bets[street][player].append(C + Rb) self.bets[street][player].append(C + Rb)
self.stacks[player] -= (C + Rb) self.stacks[player] -= (C + Rb)
act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0) act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
@ -323,6 +239,7 @@ Add a raise on [street] by [player] to [amountTo]
def addBet(self, street, player, amount): def addBet(self, street, player, amount):
logging.debug("%s %s bets %s" %(street, player, amount))
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.bets[street][player].append(Decimal(amount)) self.bets[street][player].append(Decimal(amount))
self.stacks[player] -= Decimal(amount) self.stacks[player] -= Decimal(amount)
@ -334,7 +251,7 @@ Add a raise on [street] by [player] to [amountTo]
def addFold(self, street, player): def addFold(self, street, player):
#print "DEBUG: %s %s folded" % (street, player) logging.debug("%s %s folds" % (street, player))
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.folded.add(player) self.folded.add(player)
self.pot.addFold(player) self.pot.addFold(player)
@ -348,7 +265,7 @@ Add a raise on [street] by [player] to [amountTo]
def addCollectPot(self,player, pot): def addCollectPot(self,player, pot):
#print "DEBUG: %s collected %s" % (player, pot) logging.debug("%s collected %s" % (player, pot))
self.checkPlayerExists(player) self.checkPlayerExists(player)
self.collected = self.collected + [[player, pot]] self.collected = self.collected + [[player, pot]]
if player not in self.collectees: if player not in self.collectees:
@ -380,11 +297,11 @@ Add a raise on [street] by [player] to [amountTo]
Map the tuple self.gametype onto the pokerstars string describing it Map the tuple self.gametype onto the pokerstars string describing it
""" """
# currently it appears to be something like ["ring", "hold", "nl", sb, bb]: # currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
gs = {"hold" : "Hold'em", gs = {"holdem" : "Hold'em",
"omahahi" : "Omaha", "omahahi" : "Omaha",
"omahahilo" : "FIXME", "omahahilo" : "FIXME",
"razz" : "Razz", "razz" : "Razz",
"studhi" : "FIXME", "studhi" : "7 Card Stud",
"studhilo" : "FIXME", "studhilo" : "FIXME",
"fivedraw" : "5 Card Draw", "fivedraw" : "5 Card Draw",
"27_1draw" : "FIXME", "27_1draw" : "FIXME",
@ -399,7 +316,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
} }
logging.debug("gametype: %s" %(self.gametype)) logging.debug("gametype: %s" %(self.gametype))
retstring = "%s %s" %(gs[self.gametype[1]], ls[self.gametype[2]]) retstring = "%s %s" %(gs[self.gametype['category']], ls[self.gametype['limitType']])
return retstring return retstring
@ -418,65 +335,165 @@ Map the tuple self.gametype onto the pokerstars string describing it
"4" : ("1", "2") "4" : ("1", "2")
} }
} }
return betlist[self.sitename][self.bb] try:
ret = betlist[self.sitename][self.bb]
except:
logging.warning("Don't know the small blind/big blind size for %s, big bet size %s." % (self.sitename, self.bb))
ret = (Decimal(self.sb)/2,Decimal(self.bb)/2)
return ret
def writeHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
if self.gametype[1] == "hold" or self.gametype[1] == "omahahi": print >>fh, "Override me"
self.writeHoldemHand(fh)
else: def printHand(self):
self.writeStudHand(fh) self.writeHand(sys.stdout)
def printActionLine(self, act, fh):
if act[1] == 'folds':
print >>fh, _("%s: folds " %(act[0]))
elif act[1] == 'checks':
print >>fh, _("%s: checks " %(act[0]))
elif act[1] == 'calls':
print >>fh, _("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'bets':
print >>fh, _("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'raises':
print >>fh, _("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else ''))
elif act[1] == 'completea':
print >>fh, _("%s: completes to $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
elif act[1] == 'posts':
if(act[2] == "small blind"):
print >>fh, _("%s: posts small blind $%s" %(act[0], act[3]))
elif(act[2] == "big blind"):
print >>fh, _("%s: posts big blind $%s" %(act[0], act[3]))
elif(act[2] == "both"):
print >>fh, _("%s: posts small & big blinds $%s" %(act[0], act[3]))
elif act[1] == 'bringin':
print >>fh, _("%s: brings in for $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
class HoldemOmahaHand(Hand):
def __init__(self, hhc, sitename, gametype, handText):
if gametype['base'] != 'hold':
pass # or indeed don't pass and complain instead
logging.debug("HoldemOmahaHand")
self.streetList = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
self.communityStreets = ['FLOP', 'TURN', 'RIVER']
self.actionStreets = ['PREFLOP','FLOP','TURN','RIVER']
Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype['sb']
self.bb = gametype['bb']
#Populate a HoldemOmahaHand
#Generally, we call 'read' methods here, which get the info according to the particular filter (hhc)
# which then invokes a 'addXXX' callback
hhc.readHandInfo(self)
hhc.readPlayerStacks(self)
hhc.compilePlayerRegexs(self)
hhc.markStreets(self)
hhc.readBlinds(self)
hhc.readButton(self)
hhc.readHeroCards(self)
hhc.readShowdownActions(self)
# Read actions in street order
for street in self.communityStreets:
if self.streets[street]:
hhc.readCommunityCards(self, street)
for street in self.actionStreets:
if self.streets[street]:
hhc.readAction(self, street)
hhc.readCollectPot(self)
hhc.readShownCards(self)
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
def addHoleCards(self, cards, player):
"""\
Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','Jc']
player (string) name of player
"""
logging.debug("addHoleCards %s %s" % (cards, player))
try:
self.checkPlayerExists(player)
cardset = set(self.card(c) for c in cards)
if 'PREFLOP' in self.holecards[player]:
self.holecards[player]['PREFLOP'].update(cardset)
else:
self.holecards[player]['PREFLOP'] = cardset
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def addShownCards(self, cards, player, holeandboard=None):
"""\
For when a player shows cards for any reason (for showdown or out of choice).
Card ranks will be uppercased
"""
logging.debug("addShownCards %s hole=%s all=%s" % (player, cards, holeandboard))
if cards is not None:
self.shown.add(player)
self.addHoleCards(cards,player)
elif holeandboard is not None:
holeandboard = set([self.card(c) for c in holeandboard])
board = set([c for s in self.board.values() for c in s])
self.addHoleCards(holeandboard.difference(board),player)
def writeHoldemHand(self, fh=sys.__stdout__): def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
#print "\n### Pseudo stars format ###"
#print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime))
print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime))) print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime)))
print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)) print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
players_who_act_preflop = set([x[0] for x in self.actions['PREFLOP']]) players_who_act_preflop = set(([x[0] for x in self.actions['PREFLOP']]+[x[0] for x in self.actions['BLINDSANTES']]))
logging.debug(self.actions['PREFLOP'])
for player in [x for x in self.players if x[1] in players_who_act_preflop]: for player in [x for x in self.players if x[1] in players_who_act_preflop]:
#Only print stacks of players who do something preflop #Only print stacks of players who do something preflop
print >>fh, _("Seat %s: %s ($%s)" %(player[0], player[1], player[2])) print >>fh, _("Seat %s: %s ($%s in chips) " %(player[0], player[1], player[2]))
#May be more than 1 bb posting #May be more than 1 bb posting
if self.gametype[2] == "fl": if self.gametype['limitType'] == "fl":
(smallbet, bigbet) = self.lookupLimitBetSize() (smallbet, bigbet) = self.lookupLimitBetSize()
else: else:
smallbet = self.sb smallbet = self.sb
bigbet = self.bb bigbet = self.bb
for a in self.posted: # for a in self.posted:
if(a[1] == "small blind"): # if(a[1] == "small blind"):
print >>fh, _("%s: posts small blind $%s" %(a[0], smallbet)) # print >>fh, _("%s: posts small blind $%s" %(a[0], smallbet))
if(a[1] == "big blind"): # if(a[1] == "big blind"):
print >>fh, _("%s: posts big blind $%s" %(a[0], bigbet)) # print >>fh, _("%s: posts big blind $%s" %(a[0], bigbet))
if(a[1] == "both"): # if(a[1] == "both"):
print >>fh, _("%s: posts small & big blinds $%.2f" %(a[0], (Decimal(smallbet) + Decimal(bigbet)))) # print >>fh, _("%s: posts small & big blinds $%.2f" %(a[0], (Decimal(smallbet) + Decimal(bigbet))))
# I think these can just be actions in 'blindsantes' round
if self.actions['BLINDSANTES']:
for act in self.actions['BLINDSANTES']:
self.printActionLine(act, fh)
print >>fh, _("*** HOLE CARDS ***") print >>fh, _("*** HOLE CARDS ***")
if self.involved: if self.involved:
print >>fh, _("Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero]))) print >>fh, _("Dealt to %s [%s]" %(self.hero , " ".join(self.holecards[self.hero]['PREFLOP'])))
if 'PREFLOP' in self.actions: if self.actions['PREFLOP']:
for act in self.actions['PREFLOP']: for act in self.actions['PREFLOP']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'FLOP' in self.actions: if self.board['FLOP']:
print >>fh, _("*** FLOP *** [%s]" %( " ".join(self.board['FLOP']))) print >>fh, _("*** FLOP *** [%s]" %( " ".join(self.board['FLOP'])))
if self.actions['FLOP']:
for act in self.actions['FLOP']: for act in self.actions['FLOP']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'TURN' in self.actions: if self.board['TURN']:
print >>fh, _("*** TURN *** [%s] [%s]" %( " ".join(self.board['FLOP']), " ".join(self.board['TURN']))) print >>fh, _("*** TURN *** [%s] [%s]" %( " ".join(self.board['FLOP']), " ".join(self.board['TURN'])))
if self.actions['TURN']:
for act in self.actions['TURN']: for act in self.actions['TURN']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'RIVER' in self.actions: if self.board['RIVER']:
print >>fh, _("*** RIVER *** [%s] [%s]" %(" ".join(self.board['FLOP']+self.board['TURN']), " ".join(self.board['RIVER']) )) print >>fh, _("*** RIVER *** [%s] [%s]" %(" ".join(self.board['FLOP']+self.board['TURN']), " ".join(self.board['RIVER']) ))
if self.actions['RIVER']:
for act in self.actions['RIVER']: for act in self.actions['RIVER']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
@ -513,37 +530,115 @@ Map the tuple self.gametype onto the pokerstars string describing it
seatnum = player[0] seatnum = player[0]
name = player[1] name = player[1]
if name in self.collectees and name in self.shown: if name in self.collectees and name in self.shown:
print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]), self.collectees[name])) print >>fh, _("Seat %d: %s showed [%s] and won ($%s)" % (seatnum, name, " ".join(self.holecards[name]['PREFLOP']), self.collectees[name]))
elif name in self.collectees: elif name in self.collectees:
print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name])) print >>fh, _("Seat %d: %s collected ($%s)" % (seatnum, name, self.collectees[name]))
elif name in self.shown: elif name in self.shown:
print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]))) print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]['PREFLOP'])))
elif name in self.folded: elif name in self.folded:
print >>fh, _("Seat %d: %s folded" % (seatnum, name)) print >>fh, _("Seat %d: %s folded" % (seatnum, name))
else: else:
print >>fh, _("Seat %d: %s mucked" % (seatnum, name)) print >>fh, _("Seat %d: %s mucked" % (seatnum, name))
print >>fh, "\n\n" print >>fh, "\n\n"
# TODO:
# logic for side pots
# logic for which players get to showdown
# I'm just not sure we need to do this so heavily.. and if we do, it's probably better to use pokerlib
#if self.holecards[player[1]]: # empty list default is false
#hole = self.holecards[player[1]]
##board = []
##for s in self.board.values():
##board += s
##playerhand = self.bestHand('hi', board+hole)
##print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
#print "Seat %d: %s showed %s" % (player[0], player[1], hole)
#else:
#print "Seat %d: %s mucked or folded" % (player[0], player[1])
class DrawHand(Hand):
def __init__(self, hhc, sitename, gametype, handText):
if gametype['base'] != 'draw':
pass # or indeed don't pass and complain instead
def writeStudHand(self, fh=sys.__stdout__): def discardHoleCards(self, cards, player):
try:
self.checkPlayerExists(player)
for card in cards:
self.holecards[player].remove(card)
except FpdbParseError, e:
pass
except ValueError:
print "[ERROR] discardHoleCard tried to discard a card %s didn't have" % (player,)
class StudHand(Hand):
def __init__(self, hhc, sitename, gametype, handText):
if gametype['base'] != 'stud':
pass # or indeed don't pass and complain instead
self.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.holeStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype['sb']
self.bb = gametype['bb']
#Populate the StudHand
#Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc)
# which then invokes a 'addXXX' callback
hhc.readHandInfo(self)
hhc.readPlayerStacks(self)
hhc.compilePlayerRegexs(self)
hhc.markStreets(self)
hhc.readAntes(self)
hhc.readBringIn(self)
# hhc.readShowdownActions(self) # not done yet
# Read actions in street order
for street in self.streetList:
if self.streets[street]:
logging.debug(street)
logging.debug(self.streets[street])
hhc.readStudPlayerCards(self, street)
hhc.readAction(self, street)
hhc.readCollectPot(self)
# hhc.readShownCards(self) # not done yet
self.totalPot() # finalise it (total the pot)
hhc.getRake(self)
def addPlayerCards(self, player, street, open=[], closed=[]):
"""\
Assigns observed cards to a player.
player (string) name of player
street (string) the street name (in streetList)
open list of card bigrams e.g. ['2h','Jc'], dealt face up
closed likewise, but known only to player
"""
logging.debug("addPlayerCards %s, o%s x%s" % (player, open, closed))
try:
self.checkPlayerExists(player)
self.holecards[player][street] = (open, closed)
# cards = set([self.card(c) for c in cards])
# self.holecards[player].update(cards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
# TODO: def addComplete(self, player, amount):
def addComplete(self, street, player, amountTo):
# assert street=='THIRD'
# This needs to be called instead of addRaiseTo, and it needs to take account of self.lastBet['THIRD'] to determine the raise-by size
"""\
Add a complete on [street] by [player] to [amountTo]
"""
logging.debug("%s %s completes %s" % (street, player, amountTo))
self.checkPlayerExists(player)
Bp = self.lastBet['THIRD']
Bc = reduce(operator.add, self.bets[street][player], 0)
Rt = Decimal(amountTo)
C = Bp - Bc
Rb = Rt - C
self._addRaise(street, player, C, Rb, Rt)
#~ self.bets[street][player].append(C + Rb)
#~ self.stacks[player] -= (C + Rb)
#~ act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
#~ self.actions[street].append(act)
#~ self.lastBet[street] = Rt # TODO check this is correct
#~ self.pot.addMoney(player, C+Rb)
def addBringIn(self, player, bringin):
if player is not None:
logging.debug("Bringin: %s, %s" % (player , bringin))
self.bets['THIRD'][player].append(Decimal(bringin))
self.stacks[player] -= Decimal(bringin)
act = (player, 'bringin', bringin, self.stacks[player]==0)
self.actions['THIRD'].append(act)
self.lastBet['THIRD'] = Decimal(bringin)
self.pot.addMoney(player, Decimal(bringin))
def writeHand(self, fh=sys.__stdout__):
# PokerStars format. # PokerStars format.
#print "\n### Pseudo stars format ###"
#print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %(self.sitename, self.handid, self.getGameTypeAsString(), self.sb, self.bb, self.starttime))
print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime))) print >>fh, _("%s Game #%s: %s ($%s/$%s) - %s" %("PokerStars", self.handid, self.getGameTypeAsString(), self.sb, self.bb, time.strftime('%Y/%m/%d - %H:%M:%S (ET)', self.starttime)))
print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos)) print >>fh, _("Table '%s' %d-max Seat #%s is the button" %(self.tablename, self.maxseats, self.buttonpos))
@ -558,30 +653,88 @@ Map the tuple self.gametype onto the pokerstars string describing it
print >>fh, _("%s: posts the ante $%s" %(act[0], act[3])) print >>fh, _("%s: posts the ante $%s" %(act[0], act[3]))
if 'THIRD' in self.actions: if 'THIRD' in self.actions:
print >>fh, _("*** 3RD STREET ***") dealt = 0
for player in [x for x in self.players if x[1] in players_who_post_antes]: #~ print >>fh, _("*** 3RD STREET ***")
print >>fh, _("Dealt to ") for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'THIRD' in self.holecards[player]:
(open, closed) = self.holecards[player]['THIRD']
dealt+=1
if dealt==1:
print >>fh, _("*** 3RD STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "")
for act in self.actions['THIRD']: for act in self.actions['THIRD']:
#FIXME: Need some logic here for bringin vs completes #FIXME: Need some logic here for bringin vs completes
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'FOURTH' in self.actions: if 'FOURTH' in self.actions:
print >>fh, _("*** 4TH STREET ***") dealt = 0
#~ print >>fh, _("*** 4TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'FOURTH' in self.holecards[player]:
old = []
(o,c) = self.holecards[player]['THIRD']
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['FOURTH'][0]
dealt+=1
if dealt==1:
print >>fh, _("*** 4TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['FOURTH']: for act in self.actions['FOURTH']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'FIFTH' in self.actions: if 'FIFTH' in self.actions:
print >>fh, _("*** 5TH STREET ***") dealt = 0
#~ print >>fh, _("*** 5TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'FIFTH' in self.holecards[player]:
old = []
for street in ('THIRD','FOURTH'):
(o,c) = self.holecards[player][street]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['FIFTH'][0]
dealt+=1
if dealt==1:
print >>fh, _("*** 5TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['FIFTH']: for act in self.actions['FIFTH']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'SIXTH' in self.actions: if 'SIXTH' in self.actions:
print >>fh, _("*** 6TH STREET ***") dealt = 0
#~ print >>fh, _("*** 6TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'SIXTH' in self.holecards[player]:
old = []
for street in ('THIRD','FOURTH','FIFTH'):
(o,c) = self.holecards[player][street]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['SIXTH'][0]
dealt += 1
if dealt == 1:
print >>fh, _("*** 6TH STREET ***")
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['SIXTH']: for act in self.actions['SIXTH']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
if 'SEVENTH' in self.actions: if 'SEVENTH' in self.actions:
# OK. It's possible that they're all in at an earlier street, but only closed cards are dealt.
# Then we have no 'dealt to' lines, no action lines, but still 7th street should appear.
# The only way I can see to know whether to print this line is by knowing the state of the hand
# i.e. are all but one players folded; is there an allin showdown; and all that.
print >>fh, _("*** 7TH STREET ***") print >>fh, _("*** 7TH STREET ***")
for player in [x[1] for x in self.players if x[1] in players_who_post_antes]:
if 'SEVENTH' in self.holecards[player]:
old = []
for street in ('THIRD','FOURTH','FIFTH','SIXTH'):
(o,c) = self.holecards[player][street]
if o:old.extend(o)
if c:old.extend(c)
new = self.holecards[player]['SEVENTH'][0]
if new:
print >>fh, _("Dealt to %s:%s%s") % (player, " [" + " ".join(old) + "] " if old else " ", "[" + " ".join(new) + "]" if new else "")
for act in self.actions['SEVENTH']: for act in self.actions['SEVENTH']:
self.printActionLine(act, fh) self.printActionLine(act, fh)
@ -629,77 +782,8 @@ Map the tuple self.gametype onto the pokerstars string describing it
print >>fh, _("Seat %d: %s mucked" % (seatnum, name)) print >>fh, _("Seat %d: %s mucked" % (seatnum, name))
print >>fh, "\n\n" print >>fh, "\n\n"
# TODO:
# logic for side pots
# logic for which players get to showdown
# I'm just not sure we need to do this so heavily.. and if we do, it's probably better to use pokerlib
#if self.holecards[player[1]]: # empty list default is false
#hole = self.holecards[player[1]]
##board = []
##for s in self.board.values():
##board += s
##playerhand = self.bestHand('hi', board+hole)
##print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
#print "Seat %d: %s showed %s" % (player[0], player[1], hole)
#else:
#print "Seat %d: %s mucked or folded" % (player[0], player[1])
def printHand(self):
self.writeHand(sys.stdout)
def printActionLine(self, act, fh):
if act[1] == 'folds':
print >>fh, _("%s: folds " %(act[0]))
elif act[1] == 'checks':
print >>fh, _("%s: checks " %(act[0]))
if act[1] == 'calls':
print >>fh, _("%s: calls $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
if act[1] == 'bets':
print >>fh, _("%s: bets $%s%s" %(act[0], act[2], ' and is all-in' if act[3] else ''))
if act[1] == 'raises':
print >>fh, _("%s: raises $%s to $%s%s" %(act[0], act[2], act[3], ' and is all-in' if act[5] else ''))
# going to use pokereval to figure out hands at some point.
# these functions are copied from pokergame.py
def bestHand(self, side, cards):
return HandHistoryConverter.eval.best('hi', cards, [])
# from pokergame.py
# got rid of the _ for internationalisation
def readableHandValueLong(self, side, value, cards):
if value == "NoPair":
if side == "low":
if cards[0][0] == '5':
return ("The wheel")
else:
return join(map(lambda card: card[0], cards), ", ")
else:
return ("High card %(card)s") % { 'card' : (letter2name[cards[0][0]]) }
elif value == "OnePair":
return ("A pair of %(card)s") % { 'card' : (letter2names[cards[0][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[2][0]]) }
elif value == "TwoPair":
return ("Two pairs %(card1)s and %(card2)s") % { 'card1' : (letter2names[cards[0][0]]), 'card2' : _(letter2names[cards[2][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[4][0]]) }
elif value == "Trips":
return ("Three of a kind %(card)s") % { 'card' : (letter2names[cards[0][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[3][0]]) }
elif value == "Straight":
return ("Straight %(card1)s to %(card2)s") % { 'card1' : (letter2name[cards[0][0]]), 'card2' : (letter2name[cards[4][0]]) }
elif value == "Flush":
return ("Flush %(card)s high") % { 'card' : (letter2name[cards[0][0]]) }
elif value == "FlHouse":
return ("%(card1)ss full of %(card2)ss") % { 'card1' : (letter2name[cards[0][0]]), 'card2' : (letter2name[cards[3][0]]) }
elif value == "Quads":
return _("Four of a kind %(card)s") % { 'card' : (letter2names[cards[0][0]]) } + (", %(card)s kicker") % { 'card' : (letter2name[cards[4][0]]) }
elif value == "StFlush":
if letter2name[cards[0][0]] == 'Ace':
return ("Royal flush")
else:
return ("Straight flush %(card)s high") % { 'card' : (letter2name[cards[0][0]]) }
return value
class FpdbParseError(Exception): pass
class Pot(object): class Pot(object):

View File

@ -15,13 +15,13 @@
#In the "official" distribution you can find the license in #In the "official" distribution you can find the license in
#agpl-3.0.txt in the docs folder of the package. #agpl-3.0.txt in the docs folder of the package.
import Configuration
import FpdbRegex
import Hand import Hand
import re import re
import sys import sys
import threading
import traceback import traceback
import logging import logging
from optparse import OptionParser
import os import os
import os.path import os.path
import xml.dom.minidom import xml.dom.minidom
@ -72,117 +72,155 @@ letter2names = {
import gettext import gettext
gettext.install('myapplication') gettext.install('myapplication')
class HandHistoryConverter(threading.Thread):
READ_CHUNK_SIZE = 1000 # bytes to read at a time from file
class HandHistoryConverter: def __init__(self, in_path = '-', out_path = '-', sitename = None, follow=False):
threading.Thread.__init__(self)
def __init__(self, config, file, sitename):
logging.info("HandHistory init called") logging.info("HandHistory init called")
self.c = config
self.sitename = sitename # default filetype and codepage. Subclasses should set these properly.
self.obs = "" # One big string
self.filetype = "text" self.filetype = "text"
self.codepage = "utf8" self.codepage = "utf8"
self.doc = None # For XML based HH files
self.file = file self.in_path = in_path
self.hhbase = self.c.get_import_parameters().get("hhArchiveBase") self.out_path = out_path
self.hhbase = os.path.expanduser(self.hhbase) if self.out_path == '-':
self.hhdir = os.path.join(self.hhbase,sitename) # write to stdout
self.gametype = [] self.out_fh = sys.stdout
self.ofile = os.path.join(self.hhdir, os.path.basename(file)) else:
self.rexx = FpdbRegex.FpdbRegex() # TODO: out_path should be sanity checked before opening. Perhaps in fpdb_import?
self.players = set() # I'm not sure what we're looking for, although we don't want out_path==in_path!='-'
self.out_fh = open(self.out_path, 'a') #TODO: append may be overly conservative.
self.sitename = sitename
self.follow = follow
self.compiledPlayers = set()
self.maxseats = 10 self.maxseats = 10
def __str__(self): def __str__(self):
#TODO : I got rid of most of the hhdir stuff.
tmp = "HandHistoryConverter: '%s'\n" % (self.sitename) tmp = "HandHistoryConverter: '%s'\n" % (self.sitename)
tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase) #tmp = tmp + "\thhbase: '%s'\n" % (self.hhbase)
tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir) #tmp = tmp + "\thhdir: '%s'\n" % (self.hhdir)
tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype) tmp = tmp + "\tfiletype: '%s'\n" % (self.filetype)
tmp = tmp + "\tinfile: '%s'\n" % (self.file) tmp = tmp + "\tinfile: '%s'\n" % (self.in_path)
tmp = tmp + "\toutfile: '%s'\n" % (self.ofile) tmp = tmp + "\toutfile: '%s'\n" % (self.out_path)
#tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0]) #tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
#tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1]) #tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
#tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2]) #tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
#tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4]) #tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
return tmp return tmp
def processFile(self): def run(self):
"""process a hand at a time from the input specified by in_path.
If in follow mode, wait for more data to turn up.
Otherwise, finish at eof..."""
starttime = time.time() starttime = time.time()
if not self.sanityCheck(): if not self.sanityCheck():
print "Cowardly refusing to continue after failed sanity check" print "Cowardly refusing to continue after failed sanity check"
return return
self.readFile(self.file) if self.follow:
if self.obs == "" or self.obs == None: numHands = 0
print "Did not read anything from file." for handText in self.tailHands():
return numHands+=1
self.processHand(handText)
self.obs = self.obs.replace('\r\n', '\n') else:
self.gametype = self.determineGameType() handsList = self.allHandsAsList()
if self.gametype == None: logging.info("Parsing %d hands" % len(handsList))
print "Unknown game type from file, aborting on this file." for handText in handsList:
return self.processHand(handText)
self.hands = self.splitFileIntoHands() numHands= len(handsList)
outfile = open(self.ofile, 'w')
for hand in self.hands:
#print "\nDEBUG: Input:\n"+hand.handText
self.readHandInfo(hand)
self.readPlayerStacks(hand)
#print "DEBUG stacks:", hand.stacks
# at this point we know the player names, they are in hand.players
playersThisHand = set([player[1] for player in hand.players])
if playersThisHand <= self.players: # x <= y means 'x is subset of y'
# we're ok; the regex should already cover them all.
pass
else:
# we need to recompile the player regexs.
self.players = playersThisHand
self.compilePlayerRegexs()
self.markStreets(hand)
# Different calls if stud or holdem like
if self.gametype[1] == "hold" or self.gametype[1] == "omahahi":
self.readBlinds(hand)
self.readButton(hand)
self.readHeroCards(hand) # want to generalise to draw games
elif self.gametype[1] == "razz" or self.gametype[1] == "stud" or self.gametype[1] == "stud8":
self.readAntes(hand)
self.readBringIn(hand)
self.readShowdownActions(hand)
# Read actions in street order
for street in hand.streetList: # go through them in order
print "DEBUG: ", street
if hand.streets.group(street) is not None:
if self.gametype[1] == "hold" or self.gametype[1] == "omahahi":
self.readCommunityCards(hand, street) # read community cards
elif self.gametype[1] == "razz" or self.gametype[1] == "stud" or self.gametype[1] == "stud8":
self.readPlayerCards(hand, street)
self.readAction(hand, street)
self.readCollectPot(hand)
self.readShownCards(hand)
# finalise it (total the pot)
hand.totalPot()
self.getRake(hand)
hand.writeHand(outfile)
#if(hand.involved == True):
#self.writeHand("output file", hand)
#hand.printHand()
#else:
#pass #Don't write out observed hands
outfile.close()
endtime = time.time() endtime = time.time()
print "Processed %d hands in %.3f seconds" % (len(self.hands), endtime - starttime) print "Processed %d hands in %.3f seconds" % (numHands, endtime - starttime)
def tailHands(self):
"""Generator of handTexts from a tailed file:
Tail the in_path file and yield handTexts separated by re_SplitHands"""
if in_path == '-': raise StopIteration
interval = 1.0 # seconds to sleep between reads for new data
fd = open(filename,'r')
data = ''
while 1:
where = fd.tell()
newdata = fd.read(self.READ_CHUNK_SIZE)
if not newdata:
fd_results = os.fstat(fd.fileno())
try:
st_results = os.stat(filename)
except OSError:
st_results = fd_results
if st_results[1] == fd_results[1]:
time.sleep(interval)
fd.seek(where)
else:
print "%s changed inode numbers from %d to %d" % (filename, fd_results[1], st_results[1])
fd = open(filename, 'r')
fd.seek(where)
else:
# yield hands
data = data + newdata
result = self.re_SplitHands.split(data)
result = iter(result)
# --x data (- is bit of splitter, x is paragraph) yield,...,keep
# [,--,x] result of re.split (with group around splitter)
# ,x our output: yield nothing, keep x
#
# --x--x [,--,x,--,x] x,x
# -x--x [-x,--,x] x,x
# x- [x-] ,x-
# x-- [x,--,] x,--
# x--x [x,--,x] x,x
# x--x-- [x,--,x,--,] x,x,--
# The length is always odd.
# 'odd' indices are always splitters.
# 'even' indices are always paragraphs or ''
# We want to discard all the ''
# We want to discard splitters unless the final item is '' (because the splitter could grow with new data)
# We want to yield all paragraphs followed by a splitter, i.e. all even indices except the last.
for para in result:
try:
splitter = result.next()
except StopIteration:
splitter = None
if splitter: # para is followed by a splitter
if para: yield para # para not ''
else:
data = para # keep final partial paragraph
def allHandsAsList(self):
"""Return a list of handtexts in the file at self.in_path"""
#TODO : any need for this to be generator? e.g. stars support can email one huge file of all hands in a year. Better to read bit by bit than all at once.
self.readFile()
self.obs = self.obs.strip()
self.obs = self.obs.replace('\r\n', '\n')
if self.obs == "" or self.obs == None:
logging.info("Read no hands.")
return
return re.split(self.re_SplitHands, self.obs)
def processHand(self, handText):
gametype = self.determineGameType(handText)
logging.debug("gametype %s" % gametype)
if gametype is None:
return
hand = None
if gametype['base'] == 'hold':
logging.debug("hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)")
hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handText)
elif gametype['base'] == 'stud':
hand = Hand.StudHand(self, self.sitename, gametype, handText)
elif gametype['base'] == 'draw':
hand = Hand.DrawHand(self, self.sitename, gametype, handText)
if hand:
hand.writeHand(self.out_fh)
else:
logging.info("Unsupported game type: %s" % gametype)
# TODO: pity we don't know the HID at this stage. Log the entire hand?
# From the log we can deduce that it is the hand after the one before :)
#####
# These functions are parse actions that may be overridden by the inheriting class # These functions are parse actions that may be overridden by the inheriting class
# This function should return a list of lists looking like: # This function should return a list of lists looking like:
# return [["ring", "hold", "nl"], ["tour", "hold", "nl"]] # return [["ring", "hold", "nl"], ["tour", "hold", "nl"]]
@ -194,16 +232,29 @@ class HandHistoryConverter:
# type base limit # type base limit
# [ ring, hold, nl , sb, bb ] # [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes # Valid types specified in docs/tabledesign.html in Gametypes
def determineGameType(self): abstract def determineGameType(self, handText): abstract
"""return dict with keys/values:
'type' in ('ring', 'tour')
'limitType' in ('nl', 'cn', 'pl', 'cp', 'fl')
'base' in ('hold', 'stud', 'draw')
'category' in ('holdem', 'omahahi', omahahilo', 'razz', 'studhi', 'studhilo', 'fivedraw', '27_1draw', '27_3draw', 'badugi')
'hilo' in ('h','l','s')
'smallBlind' int?
'bigBlind' int?
'smallBet'
'bigBet'
'currency' in ('USD', 'EUR', 'T$', <countrycode>)
or None if we fail to get the info """
#TODO: which parts are optional/required?
# Read any of: # Read any of:
# HID HandID # HID HandID
# TABLE Table name # TABLE Table name
# SB small blind # SB small blind
# BB big blind # BB big blind
# GAMETYPE gametype # GAMETYPE gametype
# YEAR MON DAY HR MIN SEC datetime # YEAR MON DAY HR MIN SEC datetime
# BUTTON button seat number # BUTTON button seat number
def readHandInfo(self, hand): abstract def readHandInfo(self, hand): abstract
# Needs to return a list of lists in the format # Needs to return a list of lists in the format
@ -238,27 +289,34 @@ class HandHistoryConverter:
def sanityCheck(self): def sanityCheck(self):
"""Check we aren't going to do some stupid things"""
#TODO: the hhbase stuff needs to be in fpdb_import
sane = False sane = False
base_w = False base_w = False
#Check if hhbase exists and is writable #~ #Check if hhbase exists and is writable
#Note: Will not try to create the base HH directory #~ #Note: Will not try to create the base HH directory
if not (os.access(self.hhbase, os.W_OK) and os.path.isdir(self.hhbase)): #~ if not (os.access(self.hhbase, os.W_OK) and os.path.isdir(self.hhbase)):
print "HH Sanity Check: Directory hhbase '" + self.hhbase + "' doesn't exist or is not writable" #~ print "HH Sanity Check: Directory hhbase '" + self.hhbase + "' doesn't exist or is not writable"
else: #~ else:
#Check if hhdir exists and is writable #~ #Check if hhdir exists and is writable
if not os.path.isdir(self.hhdir): #~ if not os.path.isdir(self.hhdir):
# In first pass, dir may not exist. Attempt to create dir #~ # In first pass, dir may not exist. Attempt to create dir
print "Creating directory: '%s'" % (self.hhdir) #~ print "Creating directory: '%s'" % (self.hhdir)
os.mkdir(self.hhdir) #~ os.mkdir(self.hhdir)
sane = True #~ sane = True
elif os.access(self.hhdir, os.W_OK): #~ elif os.access(self.hhdir, os.W_OK):
sane = True #~ sane = True
else: #~ else:
print "HH Sanity Check: Directory hhdir '" + self.hhdir + "' or its parent directory are not writable" #~ print "HH Sanity Check: Directory hhdir '" + self.hhdir + "' or its parent directory are not writable"
# Make sure input and output files are different or we'll overwrite the source file # Make sure input and output files are different or we'll overwrite the source file
if(self.ofile == self.file): if True: # basically.. I don't know
sane = True
if(self.in_path != '-' and self.out_path == self.in_path):
print "HH Sanity Check: output and input files are the same, check config" print "HH Sanity Check: output and input files are the same, check config"
sane = False
return sane return sane
@ -269,7 +327,7 @@ class HandHistoryConverter:
def splitFileIntoHands(self): def splitFileIntoHands(self):
hands = [] hands = []
self.obs.strip() self.obs = self.obs.strip()
list = self.re_SplitHands.split(self.obs) list = self.re_SplitHands.split(self.obs)
list.pop() #Last entry is empty list.pop() #Last entry is empty
for l in list: for l in list:
@ -277,13 +335,19 @@ class HandHistoryConverter:
hands = hands + [Hand.Hand(self.sitename, self.gametype, l)] hands = hands + [Hand.Hand(self.sitename, self.gametype, l)]
return hands return hands
def readFile(self, filename): def readFile(self):
"""Read file""" """open in_path according to self.codepage"""
print "Reading file: '%s'" %(filename)
if(self.filetype == "text"): if(self.filetype == "text"):
infile=codecs.open(filename, "r", self.codepage) if self.in_path == '-':
self.obs = infile.read() # read from stdin
infile.close() logging.debug("Reading stdin with %s" % self.codepage) # is this necessary? or possible? or what?
in_fh = codecs.getreader('cp1252')(sys.stdin)
else:
logging.debug("Opening %s with %s" % (self.in_path, self.codepage))
in_fh = codecs.open(self.in_path, 'r', self.codepage)
self.obs = in_fh.read()
in_fh.close()
elif(self.filetype == "xml"): elif(self.filetype == "xml"):
try: try:
doc = xml.dom.minidom.parse(filename) doc = xml.dom.minidom.parse(filename)
@ -297,4 +361,4 @@ class HandHistoryConverter:
return True return True
def getProcessedFile(self): def getProcessedFile(self):
return self.ofile return self.out_path

View File

@ -98,8 +98,7 @@ class Hud:
# Set up a main window for this this instance of the HUD # Set up a main window for this this instance of the HUD
self.main_window = gtk.Window() self.main_window = gtk.Window()
self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC) self.main_window.set_gravity(gtk.gdk.GRAVITY_STATIC)
self.main_window.set_title(self.table.name + " FPDBHUD") self.main_window.set_title("%s FPDBHUD" % (self.table.name))
# self.main_window.destroyhandler = self.main_window.connect("destroy", self.kill_hud)
self.main_window.set_decorated(False) self.main_window.set_decorated(False)
self.main_window.set_opacity(self.colors["hudopacity"]) self.main_window.set_opacity(self.colors["hudopacity"])
@ -171,7 +170,7 @@ class Hud:
self.main_window.move(x, y) self.main_window.move(x, y)
adj = self.adj_seats(self.hand, self.config) adj = self.adj_seats(self.hand, self.config)
loc = self.config.get_locations(self.table.site, self.max) loc = self.config.get_locations(self.table.site, self.max)
for i in range(1, self.max + 1): for i in xrange(1, self.max + 1):
(x, y) = loc[adj[i]] (x, y) = loc[adj[i]]
if i in self.stat_windows: if i in self.stat_windows:
self.stat_windows[i].relocate(x, y) self.stat_windows[i].relocate(x, y)
@ -190,18 +189,15 @@ class Hud:
# kill all stat_windows, popups and aux_windows in this HUD # kill all stat_windows, popups and aux_windows in this HUD
# heap dead, burnt bodies, blood 'n guts, veins between my teeth # heap dead, burnt bodies, blood 'n guts, veins between my teeth
for s in self.stat_windows.itervalues(): for s in self.stat_windows.itervalues():
for p in s.popups: s.kill_popups()
s.kill_popup(p)
s.window.destroy() s.window.destroy()
self.stat_windows = {} self.stat_windows = {}
# also kill any aux windows # also kill any aux windows
for m in self.aux_windows: map(lambda m: m.destroy(), self.aux_windows)
m.destroy()
self.aux_windows = [] self.aux_windows = []
def reposition_windows(self, *args): def reposition_windows(self, *args):
for w in self.stat_windows: map(lambda x: x.window.move(x.x, x.y), self.stat_windows)
self.stat_windows[w].window.move(self.stat_windows[w].x, self.stat_windows[w].y)
return True return True
def debug_stat_windows(self, *args): def debug_stat_windows(self, *args):
@ -220,7 +216,7 @@ class Hud:
def adj_seats(self, hand, config): def adj_seats(self, hand, config):
adj = range(0, self.max + 1) # default seat adjustments = no adjustment adj = xrange(0, self.max + 1) # default seat adjustments = no adjustment
# does the user have a fav_seat? # does the user have a fav_seat?
try: try:
sys.stderr.write("site = %s, max = %d, fav seat = %d\n" % (self.table.site, self.max, config.supported_sites[self.table.site].layout[self.max].fav_seat)) sys.stderr.write("site = %s, max = %d, fav seat = %d\n" % (self.table.site, self.max, config.supported_sites[self.table.site].layout[self.max].fav_seat))
@ -230,7 +226,7 @@ class Hud:
# actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name) # actual_seat = self.db_connection.get_actual_seat(hand, config.supported_sites[self.table.site].screen_name)
actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name) actual_seat = self.get_actual_seat(config.supported_sites[self.table.site].screen_name)
sys.stderr.write("found actual seat = %d\n" % actual_seat) sys.stderr.write("found actual seat = %d\n" % actual_seat)
for i in range(0, self.max + 1): for i in xrange(0, self.max + 1):
j = actual_seat + i j = actual_seat + i
if j > self.max: j = j - self.max if j > self.max: j = j - self.max
adj[j] = fav_seat + i adj[j] = fav_seat + i
@ -241,7 +237,7 @@ class Hud:
return adj return adj
def get_actual_seat(self, name): def get_actual_seat(self, name):
for key in self.stat_dict.keys(): for key in self.stat_dict:
if self.stat_dict[key]['screen_name'] == name: if self.stat_dict[key]['screen_name'] == name:
return self.stat_dict[key]['seat'] return self.stat_dict[key]['seat']
sys.stderr.write("Error finding actual seat.\n") sys.stderr.write("Error finding actual seat.\n")
@ -263,7 +259,7 @@ class Hud:
loc = self.config.get_locations(self.table.site, self.max) loc = self.config.get_locations(self.table.site, self.max)
# create the stat windows # create the stat windows
for i in range(1, self.max + 1): for i in xrange(1, self.max + 1):
(x, y) = loc[adj[i]] (x, y) = loc[adj[i]]
if i in self.stat_windows: if i in self.stat_windows:
self.stat_windows[i].relocate(x, y) self.stat_windows[i].relocate(x, y)
@ -280,7 +276,7 @@ class Hud:
font = self.font) font = self.font)
self.stats = [] self.stats = []
for i in range(0, config.supported_games[self.poker_game].rows + 1): for i in xrange(0, config.supported_games[self.poker_game].rows + 1):
row_list = [''] * config.supported_games[self.poker_game].cols row_list = [''] * config.supported_games[self.poker_game].cols
self.stats.append(row_list) self.stats.append(row_list)
for stat in config.supported_games[self.poker_game].stats: for stat in config.supported_games[self.poker_game].stats:
@ -304,8 +300,8 @@ class Hud:
self.create(hand, config, self.stat_dict, self.cards) self.create(hand, config, self.stat_dict, self.cards)
self.stat_windows[self.stat_dict[s]['seat']].player_id = self.stat_dict[s]['player_id'] self.stat_windows[self.stat_dict[s]['seat']].player_id = self.stat_dict[s]['player_id']
for r in range(0, config.supported_games[self.poker_game].rows): for r in xrange(0, config.supported_games[self.poker_game].rows):
for c in range(0, config.supported_games[self.poker_game].cols): for c in xrange(0, config.supported_games[self.poker_game].cols):
this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]] this_stat = config.supported_games[self.poker_game].stats[self.stats[r][c]]
number = Stats.do_stat(self.stat_dict, player = self.stat_dict[s]['player_id'], stat = self.stats[r][c]) number = Stats.do_stat(self.stat_dict, player = self.stat_dict[s]['player_id'], stat = self.stats[r][c])
statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix statstring = this_stat.hudprefix + str(number[1]) + this_stat.hudsuffix
@ -370,6 +366,10 @@ class Stat_Window:
popup.window.destroy() popup.window.destroy()
self.popups.remove(popup) self.popups.remove(popup)
def kill_popups(self):
map(lambda x: x.window.destroy(), self.popups)
self.popups = { }
def relocate(self, x, y): def relocate(self, x, y):
self.x = x + self.table.x self.x = x + self.table.x
self.y = y + self.table.y self.y = y + self.table.y
@ -403,35 +403,38 @@ class Stat_Window:
self.e_box = [] self.e_box = []
self.frame = [] self.frame = []
self.label = [] self.label = []
for r in range(self.game.rows): usegtkframes = self.useframes
if self.useframes: e_box = self.e_box
label = self.label
for r in xrange(self.game.rows):
if usegtkframes:
self.frame.append([]) self.frame.append([])
self.e_box.append([]) e_box.append([])
self.label.append([]) label.append([])
for c in range(self.game.cols): for c in xrange(self.game.cols):
if self.useframes: if usegtkframes:
self.frame[r].append( gtk.Frame() ) self.frame[r].append( gtk.Frame() )
self.e_box[r].append( gtk.EventBox() ) e_box[r].append( gtk.EventBox() )
self.e_box[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) e_box[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.e_box[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor) e_box[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor)
Stats.do_tip(self.e_box[r][c], 'stuff') Stats.do_tip(e_box[r][c], 'stuff')
if self.useframes: if usegtkframes:
self.grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) self.grid.attach(self.frame[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
self.frame[r][c].add(self.e_box[r][c]) self.frame[r][c].add(e_box[r][c])
else: else:
self.grid.attach(self.e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0) self.grid.attach(e_box[r][c], c, c+1, r, r+1, xpadding = 0, ypadding = 0)
self.label[r].append( gtk.Label('xxx') ) label[r].append( gtk.Label('xxx') )
if self.useframes: if usegtkframes:
self.frame[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) self.frame[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.label[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor) label[r][c].modify_bg(gtk.STATE_NORMAL, parent.backgroundcolor)
self.label[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor) label[r][c].modify_fg(gtk.STATE_NORMAL, parent.foregroundcolor)
self.e_box[r][c].add(self.label[r][c]) e_box[r][c].add(self.label[r][c])
self.e_box[r][c].connect("button_press_event", self.button_press_cb) e_box[r][c].connect("button_press_event", self.button_press_cb)
self.label[r][c].modify_font(font) label[r][c].modify_font(font)
self.window.set_opacity(parent.colors['hudopacity']) self.window.set_opacity(parent.colors['hudopacity'])
@ -475,8 +478,8 @@ class Popup_window:
# figure out the row, col address of the click that activated the popup # figure out the row, col address of the click that activated the popup
row = 0 row = 0
col = 0 col = 0
for r in range(0, stat_window.game.rows): for r in xrange(0, stat_window.game.rows):
for c in range(0, stat_window.game.cols): for c in xrange(0, stat_window.game.cols):
if stat_window.e_box[r][c] == parent: if stat_window.e_box[r][c] == parent:
row = r row = r
col = c col = c
@ -557,7 +560,10 @@ class Popup_window:
for w in tl_windows: for w in tl_windows:
if w[1] == unique_name: if w[1] == unique_name:
win32gui.SetWindowPos(w[0], win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE|win32con.SWP_NOSIZE) window.set_transient_for(self.parent.main_window)
style = win32gui.GetWindowLong(self.table.number, win32con.GWL_EXSTYLE)
style |= win32con.WS_CLIPCHILDREN
win32gui.SetWindowLong(self.table.number, win32con.GWL_EXSTYLE, style)
window.set_title(real_name) window.set_title(real_name)

333
pyfpdb/PokerStarsToFpdb.py Executable file
View File

@ -0,0 +1,333 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi
#
# 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
########################################################################
import sys
from HandHistoryConverter import *
# PokerStars HH Format
#PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET
#Table 'Gianfar IV' 6-max Seat #1 is the button
#Seat 1: ZeKGB ($224 in chips)
#Seat 2: quimboavida ($107.75 in chips)
#Seat 3: tropical100 ($190 in chips)
#Seat 4: jackhama33 ($54.95 in chips)
#Seat 5: Olubanu ($196 in chips)
#Seat 6: LSgambler ($205.35 in chips)
#quimboavida: posts small blind $1
#tropical100: posts big blind $2
#*** HOLE CARDS ***
#jackhama33: folds
#Olubanu: folds
#LSgambler: folds
#ZeKGB: folds
#quimboavida: calls $1
#tropical100: raises $5 to $7
#quimboavida: calls $5
#*** FLOP *** [3d Qs Kd]
#quimboavida: bets $10
#tropical100: calls $10
#*** TURN *** [3d Qs Kd] [Ah]
#quimboavida: checks
#tropical100: checks
#*** RIVER *** [3d Qs Kd Ah] [7c]
#quimboavida: bets $30
#tropical100: folds
#quimboavida collected $32.35 from pot
#*** SUMMARY ***
#Total pot $34 | Rake $1.65
#Board [3d Qs Kd Ah 7c]
#Seat 1: ZeKGB (button) folded before Flop (didn't bet)
#Seat 2: quimboavida (small blind) collected ($32.35)
#Seat 3: tropical100 (big blind) folded on the River
#Seat 4: jackhama33 folded before Flop (didn't bet)
#Seat 5: Olubanu folded before Flop (didn't bet)
#Seat 6: LSgambler folded before Flop (didn't bet)
#PokerStars Game #25381215423: HORSE (Razz Limit, $0.10/$0.20) - 2009/02/26 15:20:19 ET
#Table 'Natalie V' 8-max
class PokerStars(HandHistoryConverter):
# Static regexes
re_GameInfo = re.compile('PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud|Omaha Hi/Lo) (?P<LIMIT>No Limit|Limit|Pot Limit),? \(?(?P<CURRENCY>\$|)?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)', re.MULTILINE)
re_SplitHands = re.compile('\n\n+')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
re_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
re_PlayerInfo = re.compile('^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\$?(?P<CASH>[.0-9]+) in chips\)', re.MULTILINE)
re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
# self.re_setHandInfoRegex('.*#(?P<HID>[0-9]+): Table (?P<TABLE>[ a-zA-Z]+) - \$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+) - (?P<GAMETYPE>.*) - (?P<HR>[0-9]+):(?P<MIN>[0-9]+) ET - (?P<YEAR>[0-9]+)/(?P<MON>[0-9]+)/(?P<DAY>[0-9]+)Table (?P<TABLE>[ a-zA-Z]+)\nSeat (?P<BUTTON>[0-9]+)')
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
"""\
in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout)
follow : whether to tail -f the input"""
HandHistoryConverter.__init__(self, in_path, out_path, sitename="PokerStars", follow=follow)
logging.info("Initialising PokerStars converter class")
self.filetype = "text"
self.codepage = "cp1252"
if autostart:
self.start()
def compilePlayerRegexs(self, hand):
players = set([player[1] for player in hand.players])
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs.
self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
logging.debug("player_re: " + player_re)
self.re_PostSB = re.compile(r"^%s: posts small blind \$?(?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_PostBB = re.compile(r"^%s: posts big blind \$?(?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_Antes = re.compile(r"^%s: posts the ante \$?(?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
self.re_BringIn = re.compile(r"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re, re.MULTILINE)
self.re_PostBoth = re.compile(r"^%s: posts small \& big blinds \[\$? (?P<SBBB>[.0-9]+)" % player_re, re.MULTILINE)
self.re_HeroCards = re.compile(r"^Dealt to %s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s:(?P<ATYPE> bets| checks| raises| calls| folds)( \$(?P<BET>[.\d]+))?( to \$(?P<BETTO>[.\d]+))?" % player_re, re.MULTILINE)
self.re_ShowdownAction = re.compile(r"^%s: shows \[(?P<CARDS>.*)\]" % player_re, re.MULTILINE)
self.re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %s (\(button\) |\(small blind\) |\(big blind\) )?(collected|showed \[.*\] and won) \(\$(?P<POT>[.\d]+)\)(, mucked| with.*|)" % player_re, re.MULTILINE)
self.re_sitsOut = re.compile("^%s sits out" % player_re, re.MULTILINE)
self.re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re, re.MULTILINE)
def readSupportedGames(self):
return []
def determineGameType(self, handText):
info = {'type':'ring'}
m = self.re_GameInfo.search(handText)
if not m:
return None
mg = m.groupdict()
# translations from captured groups to our info strings
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
games = { # base, category
"Hold'em" : ('hold','holdem'),
'Omaha Hi' : ('hold','omahahi'),
'Omaha Hi/Lo' : ('hold','omahahilo'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
if 'LIMIT' in mg:
info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
(info['base'], info['category']) = games[mg['GAME']]
if 'SB' in mg:
info['sb'] = mg['SB']
if 'BB' in mg:
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info
def readHandInfo(self, hand):
info = {}
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m: info.update(m.groupdict())
m = self.re_GameInfo.search(hand.handText)
if m: info.update(m.groupdict())
m = self.re_Button.search(hand.handText)
if m: info.update(m.groupdict())
# TODO : I rather like the idea of just having this dict as hand.info
logging.debug("readHandInfo: %s" % info)
for key in info:
if key == 'DATETIME':
datetime = info[key].replace(" - "," ") # some are like "2009/02/26 - 15:22:55 ET"
datetime = datetime.replace(" (ET)","") # kludge for now.
datetime = datetime.replace(" ET","") # kludge for now.
hand.starttime = time.strptime(datetime, "%Y/%m/%d %H:%M:%S")
if key == 'HID':
hand.handid = info[key]
if key == 'TABLE':
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
if m:
hand.buttonpos = int(m.group('BUTTON'))
else:
logging.info('readButton: not found')
def readPlayerStacks(self, hand):
logging.debug("readPlayerStacks")
m = self.re_PlayerInfo.finditer(hand.handText)
players = []
for a in m:
hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
def markStreets(self, hand):
# PREFLOP = ** Dealing down cards **
# This re fails if, say, river is missing; then we don't get the ** that starts the river.
if hand.gametype['base'] in ("hold"):
m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
elif hand.gametype['base'] in ("stud"):
m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3rd STREET \*\*\*)|.+)"
r"(\*\*\* 3rd STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4th STREET \*\*\*)|.+))?"
r"(\*\*\* 4th STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5th STREET \*\*\*)|.+))?"
r"(\*\*\* 5th STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6th STREET \*\*\*)|.+))?"
r"(\*\*\* 6th STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* RIVER \*\*\*)|.+))?"
r"(\*\*\* RIVER \*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL)
hand.addStreets(m)
def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
#print "DEBUG readCommunityCards:", street, hand.streets.group(street)
m = self.re_Board.search(hand.streets[street])
hand.setCommunityCards(street, m.group('CARDS').split(' '))
def readAntes(self, hand):
logging.debug("reading antes")
m = self.re_Antes.finditer(hand.handText)
for player in m:
#~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
hand.addAnte(player.group('PNAME'), player.group('ANTE'))
def readBringIn(self, hand):
m = self.re_BringIn.search(hand.handText,re.DOTALL)
if m:
#~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
def readBlinds(self, hand):
try:
m = self.re_PostSB.search(hand.handText)
hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
except: # no small blind
hand.addBlind(None, None, None)
for a in self.re_PostBB.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
for a in self.re_PostBoth.finditer(hand.handText):
hand.addBlind(a.group('PNAME'), 'small & big blinds', a.group('SBBB'))
def readHeroCards(self, hand):
m = self.re_HeroCards.search(hand.handText)
if(m == None):
#Not involved in hand
hand.involved = False
else:
hand.hero = m.group('PNAME')
# "2c, qh" -> set(["2c","qc"])
# Also works with Omaha hands.
cards = m.group('NEWCARDS')
cards = set(cards.split(' '))
hand.addHoleCards(cards, m.group('PNAME'))
def readStudPlayerCards(self, hand, street):
# See comments of reference implementation in FullTiltToFpdb.py
logging.debug("readStudPlayerCards")
m = self.re_HeroCards.finditer(hand.streets[street])
for player in m:
#~ logging.debug(player.groupdict())
(pname, oldcards, newcards) = (player.group('PNAME'), player.group('OLDCARDS'), player.group('NEWCARDS'))
if oldcards:
oldcards = [c.strip() for c in oldcards.split(' ')]
if newcards:
newcards = [c.strip() for c in newcards.split(' ')]
if street=='ANTES':
return
elif street=='THIRD':
# we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
# hero: [xx][o]
# others: [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = oldcards, open = newcards)
elif street in ('FOURTH', 'FIFTH', 'SIXTH'):
# 4th:
# hero: [xxo] [o]
# others: [o] [o]
# 5th:
# hero: [xxoo] [o]
# others: [oo] [o]
# 6th:
# hero: [xxooo] [o]
# others: [ooo] [o]
hand.addPlayerCards(player = player.group('PNAME'), street = street, open = newcards)
# we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
elif street=='SEVENTH' and newcards:
# hero: [xxoooo] [x]
# others: not reported.
hand.addPlayerCards(player = player.group('PNAME'), street = street, closed = newcards)
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
if action.group('ATYPE') == ' raises':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls':
hand.addCall( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' bets':
hand.addBet( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' folds':
hand.addFold( street, action.group('PNAME'))
elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME'))
else:
print "DEBUG: unimplemented readAction: %s %s" %(action.group('PNAME'),action.group('ATYPE'),)
def readShowdownActions(self, hand):
for shows in self.re_ShowdownAction.finditer(hand.handText):
cards = shows.group('CARDS')
cards = set(cards.split(' '))
hand.addShownCards(cards, shows.group('PNAME'))
def readCollectPot(self,hand):
for m in self.re_CollectPot.finditer(hand.handText):
hand.addCollectPot(player=m.group('PNAME'),pot=m.group('POT'))
def readShownCards(self,hand):
for m in self.re_ShownCards.finditer(hand.handText):
if m.group('CARDS') is not None:
cards = m.group('CARDS')
cards = set(cards.split(' '))
hand.addShownCards(cards=cards, player=m.group('PNAME'))
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-i", "--input", dest="ipath", help="parse input hand history", default="regression-test-files/pokerstars/HH20090226 Natalie V - $0.10-$0.20 - HORSE.txt")
parser.add_option("-o", "--output", dest="opath", help="output translation to", default="-")
parser.add_option("-f", "--follow", dest="follow", help="follow (tail -f) the input", action="store_true", default=False)
parser.add_option("-q", "--quiet",
action="store_const", const=logging.CRITICAL, dest="verbosity", default=logging.INFO)
parser.add_option("-v", "--verbose",
action="store_const", const=logging.INFO, dest="verbosity")
parser.add_option("--vv",
action="store_const", const=logging.DEBUG, dest="verbosity")
(options, args) = parser.parse_args()
LOG_FILENAME = './logging.out'
logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity)
e = PokerStars(in_path = options.ipath, out_path = options.opath, follow = options.follow)

View File

@ -1,176 +0,0 @@
#!/usr/bin/python
#Copyright 2008 Steffen Jobbagy-Felso
#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 in the docs folder of the package.
############################################################################
#
# File for Regression Testing fpdb
#
import os
import sys
import datetime
import Configuration
import fpdb_db
import fpdb_import
import fpdb_simple
import FpdbSQLQueries
import EverleafToFpdb
import Tables
import unittest
class TestSequenceFunctions(unittest.TestCase):
def setUp(self):
"""Configure MySQL settings/database and establish connection"""
self.c = Configuration.Config()
self.mysql_settings={ 'db-host':"localhost",
'db-backend':2,
'db-databaseName':"fpdbtest",
'db-user':"fpdb",
'db-password':"fpdb"}
self.mysql_db = fpdb_db.fpdb_db()
self.mysql_db.connect(self.mysql_settings['db-backend'], self.mysql_settings['db-host'],
self.mysql_settings['db-databaseName'], self.mysql_settings['db-user'],
self.mysql_settings['db-password'])
self.mysqldict = FpdbSQLQueries.FpdbSQLQueries('MySQL InnoDB')
self.mysqlimporter = fpdb_import.Importer(self, self.mysql_settings, self.c)
self.mysqlimporter.setCallHud(False)
self.everleaf = EverleafToFpdb.Everleaf(self.c, "Nofile")
# """Configure Postgres settings/database and establish connection"""
# self.pg_settings={ 'db-host':"localhost", 'db-backend':3, 'db-databaseName':"fpdbtest", 'db-user':"fpdb", 'db-password':"fpdb"}
# self.pg_db = fpdb_db.fpdb_db()
# self.pg_db.connect(self.pg_settings['db-backend'], self.pg_settings['db-host'],
# self.pg_settings['db-databaseName'], self.pg_settings['db-user'],
# self.pg_settings['db-password'])
# self.pgdict = FpdbSQLQueries.FpdbSQLQueries('PostgreSQL')
def testDatabaseConnection(self):
"""Test all supported DBs"""
self.result = self.mysql_db.cursor.execute(self.mysqldict.query['list_tables'])
self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
def testMySQLRecreateTables(self):
"""Test droping then recreating fpdb table schema"""
self.mysql_db.recreate_tables()
self.result = self.mysql_db.cursor.execute("SHOW TABLES")
self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
def testPokerStarsHHDate(self):
latest = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]"
previous = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/08/17 - 01:14:43 (ET)"
older1 = "PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/09/07 06:23:14 ET"
result = fpdb_simple.parseHandStartTime(older1, "ps")
self.failUnless(result==datetime.datetime(2008,9,7,11,23,14),
"Date incorrect, expected: 2008-09-07 11:23:14 got: " + str(result))
result = fpdb_simple.parseHandStartTime(latest, "ps")
self.failUnless(result==datetime.datetime(2008,11,12,15,00,48),
"Date incorrect, expected: 2008-11-12 15:00:48 got: " + str(result))
result = fpdb_simple.parseHandStartTime(previous, "ps")
self.failUnless(result==datetime.datetime(2008,8,17,6,14,43),
"Date incorrect, expected: 2008-08-17 01:14:43 got: " + str(result))
def testFullTiltHHDate(self):
sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29"
cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09"
cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13"
result = fpdb_simple.parseHandStartTime(sitngo1,"ftp")
self.failUnless(result==datetime.datetime(2009,1,29,05,07,45),
"Date incorrect, expected: 2009-01-29 05:07:45 got: " + str(result))
result = fpdb_simple.parseHandStartTime(cash1,"ftp")
self.failUnless(result==datetime.datetime(2008,12,9,14,40,20),
"Date incorrect, expected: 2008-12-09 14:40:20 got: " + str(result))
result = fpdb_simple.parseHandStartTime(cash2,"ftp")
self.failUnless(result==datetime.datetime(2008,12,13,10,9,36),
"Date incorrect, expected: 2008-12-13 10:09:36 got: " + str(result))
def testTableDetection(self):
result = Tables.clean_title("French (deep)")
self.failUnless(result == "French", "French (deep) parsed incorrectly. Expected 'French' got: " + str(result))
# result = ("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
def testEverleafGameInfoRegex(self):
cash_nlhe = """Everleaf Gaming Game #55198191
***** Hand history for game #55198191 *****
Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 10:02:11
Table Speed Kuala
Seat 8 is the button
Total number of players: 10"""
cash_plo = """Everleaf Gaming Game #65295370
***** Hand history for game #65295370 *****
Blinds $0.50/$1 PL Omaha - 2008/12/07 - 21:59:48
Table Guanajuato
Seat 5 is the button
Total number of players: 6"""
cash_flhe = """Everleaf Gaming Game #55809022
***** Hand history for game #55809022 *****
$1/$2 Hold'em - 2008/09/07 - 08:04:36
Table Jeonju
Seat 4 is the button
Total number of players: 5
"""
#NLHE
m = self.everleaf.re_GameInfo.search(cash_nlhe)
sb = m.group('SB')
bb = m.group('BB')
ltype = m.group('LTYPE')
game = m.group('GAME')
self.failUnless(sb == "0.50", "SB incorrect, expected: 0.50 got: '" + sb + "'")
self.failUnless(bb == "1", "BB incorrect, expected: 1 got: '" + bb + "'")
self.failUnless(ltype == "NL", "LTYPE incorrect, expected: NL got: '" + ltype + "'")
self.failUnless(game == "Hold\'em", "GAME incorrect, expected: Hold\'em got: '" + game + "'")
#FLHE
m = self.everleaf.re_GameInfo.search(cash_flhe)
sb = m.group('SB')
bb = m.group('BB')
ltype = m.group('LTYPE')
game = m.group('GAME')
print m.groups()
self.failUnless(sb == "1", "SB incorrect, expected: 1 got: '" + sb + "'")
self.failUnless(bb == "2", "BB incorrect, expected: 2 got: '" + bb + "'")
self.failUnless(ltype == None, "LTYPE incorrect, expected: NL got: '%s'" %(ltype))
self.failUnless(game == "Hold\'em", "GAME incorrect, expected: Hold\'em got: '" + game + "'")
# def testImportHandHistoryFiles(self):
# """Test import of single HH file"""
# self.mysqlimporter.addImportFile("regression-test-files/hand-histories/ps-lhe-ring-3hands.txt")
# self.mysqlimporter.runImport()
# self.mysqlimporter.addImportDirectory("regression-test-files/hand-histories")
# self.mysqlimporter.runImport()
# def testPostgresSQLRecreateTables(self):
# """Test droping then recreating fpdb table schema"""
# self.pg_db.recreate_tables()
# self.result = self.pg_db.cursor.execute(self.pgdict.query['list_tables'])
# self.failUnless(self.result==13, "Number of tables in database incorrect. Expected 13 got " + str(self.result))
if __name__ == '__main__':
unittest.main()

View File

@ -118,7 +118,6 @@ class Importer:
def addBulkImportImportFileOrDir(self, inputPath,filter = "passthrough"): def addBulkImportImportFileOrDir(self, inputPath,filter = "passthrough"):
"""Add a file or directory for bulk import""" """Add a file or directory for bulk import"""
# Bulk import never monitors # Bulk import never monitors
# if directory, add all files in it. Otherwise add single file. # if directory, add all files in it. Otherwise add single file.
# TODO: only add sane files? # TODO: only add sane files?
if os.path.isdir(inputPath): if os.path.isdir(inputPath):
@ -133,6 +132,7 @@ class Importer:
#dirlist is a hash of lists: #dirlist is a hash of lists:
#dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] } #dirlist{ 'PokerStars' => ["/path/to/import/", "filtername"] }
def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"): def addImportDirectory(self,dir,monitor = False, site = "default", filter = "passthrough"):
#gets called by GuiAutoImport.
#This should really be using os.walk #This should really be using os.walk
#http://docs.python.org/library/os.html #http://docs.python.org/library/os.html
if os.path.isdir(dir): if os.path.isdir(dir):
@ -182,9 +182,10 @@ class Importer:
#Run import on updated files, then store latest update time. #Run import on updated files, then store latest update time.
def runUpdated(self): def runUpdated(self):
#Check for new files in directory #Check for new files in monitored directories
#todo: make efficient - always checks for new file, should be able to use mtime of directory #todo: make efficient - always checks for new file, should be able to use mtime of directory
# ^^ May not work on windows # ^^ May not work on windows
for site in self.dirlist: for site in self.dirlist:
self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1]) self.addImportDirectory(self.dirlist[site][0], False, site, self.dirlist[site][1])
@ -197,13 +198,17 @@ class Importer:
self.updated[file] = time() self.updated[file] = time()
except: except:
self.updated[file] = time() self.updated[file] = time()
# This codepath only runs first time the file is found, if modified in the last # If modified in the last minute run an immediate import.
# minute run an immediate import. # This codepath only runs first time the file is found.
if (time() - stat_info.st_mtime) < 60 or os.path.isdir(file): # TODO: figure out a way to dispatch this to the seperate thread so our main window doesn't lock up on initial import if (time() - stat_info.st_mtime) < 60:
# TODO attach a HHC thread to the file
# TODO import the output of the HHC thread -- this needs to wait for the HHC to block?
self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1]) self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
# TODO we also test if directory, why?
#if os.path.isdir(file):
#self.import_file_dict(file, self.filelist[file][0], self.filelist[file][1])
for dir in self.addToDirList: self.addToDirList = filter(lambda x: self.addImportDirectory(x, True, self.addToDirList[x][0], self.addToDirList[x][1]), self.addToDirList)
self.addImportDirectory(dir, True, self.addToDirList[dir][0], self.addToDirList[dir][1])
for file in self.removeFromFileList: for file in self.removeFromFileList:
if file in self.filelist: if file in self.filelist:
@ -225,12 +230,21 @@ class Importer:
# TODO: Shouldn't we be able to use some sort of lambda or something to just call a Python object by whatever name we specify? then we don't have to hardcode them, # TODO: Shouldn't we be able to use some sort of lambda or something to just call a Python object by whatever name we specify? then we don't have to hardcode them,
# someone can just create their own python module for it # someone can just create their own python module for it
if filter == "EverleafToFpdb": if filter in ("EverleafToFpdb","Everleaf"):
print "converting ", file print "converting ", file
conv = EverleafToFpdb.Everleaf(self.config, file) hhbase = self.config.get_import_parameters().get("hhArchiveBase")
hhbase = os.path.expanduser(hhbase)
hhdir = os.path.join(hhbase,site)
try:
out_path = os.path.join(hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file))
except:
out_path = os.path.join(hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
#out_fh = open(ofile, 'w') # TODO: seek to previous place in input and append output
conv = EverleafToFpdb.Everleaf(in_path = file, out_path = out_path)
conv.join()
elif filter == "FulltiltToFpdb": elif filter == "FulltiltToFpdb":
print "converting ", file print "converting ", file
conv = FulltiltToFpdb.FullTilt(self.config, file) conv = FulltiltToFpdb.FullTilt(in_fh = file, out_fh = out_fh)
else: else:
print "Unknown filter ", filter print "Unknown filter ", filter
return return
@ -294,7 +308,7 @@ class Importer:
partial=0 #counter partial=0 #counter
errors=0 #counter errors=0 #counter
for i in range (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method for i in xrange (len(self.lines)): #main loop, iterates through the lines of a file and calls the appropriate parser method
if (len(self.lines[i])<2): if (len(self.lines[i])<2):
endpos=i endpos=i
hand=self.lines[startpos:endpos] hand=self.lines[startpos:endpos]

View File

@ -399,59 +399,59 @@ def checkPositions(positions):
def classifyLines(hand, category, lineTypes, lineStreets): def classifyLines(hand, category, lineTypes, lineStreets):
currentStreet="predeal" currentStreet="predeal"
done=False #set this to true once we reach the last relevant line (the summary, except rake, is all repeats) done=False #set this to true once we reach the last relevant line (the summary, except rake, is all repeats)
for i in range (len(hand)): for i, line in enumerate(hand):
if (done): if done:
if (hand[i].find("[")==-1 or hand[i].find("mucked [")==-1): if "[" not in line or "mucked [" not in line:
lineTypes.append("ignore") lineTypes.append("ignore")
else: #it's storing a mucked card else:
lineTypes.append("cards") lineTypes.append("cards")
elif (hand[i].startswith("Dealt to")): elif line.startswith("Dealt to"):
lineTypes.append("cards") lineTypes.append("cards")
elif (i==0): elif i == 0:
lineTypes.append("header") lineTypes.append("header")
elif (hand[i].startswith("Seat ") and ((hand[i].find("in chips")!=-1) or (hand[i].find("($")!=-1))): elif line.startswith("Seat ") and ( ("in chips" in line) or "($" in line):
lineTypes.append("name") lineTypes.append("name")
elif (isActionLine(hand[i])): elif isActionLine(line):
lineTypes.append("action") lineTypes.append("action")
if (hand[i].find(" posts ")!=-1 or hand[i].find(" posts the ")!=-1):#need to set this here so the "action" of posting blinds is registered properly if " posts " in line or " posts the " in line:
currentStreet="preflop" currentStreet="preflop"
elif (isWinLine(hand[i])): elif isWinLine(line):
lineTypes.append("win") lineTypes.append("win")
elif (hand[i].startswith("Total pot ") and hand[i].find("Rake")!=-1): elif line.startswith("Total pot ") and "Rake" in line:
lineTypes.append("rake") lineTypes.append("rake")
done=True done=True
elif (hand[i]=="*** SHOW DOWN ***" or hand[i]=="*** SUMMARY ***"): elif "*** SHOW DOWN ***" in line or "*** SUMMARY ***" in line:
lineTypes.append("ignore") lineTypes.append("ignore")
#print "in classifyLine, showdown or summary" #print "in classifyLine, showdown or summary"
elif (hand[i].find(" antes ")!=-1 or hand[i].find(" posts the ante ")!=-1): elif " antes " in line or " posts the ante " in line:
lineTypes.append("ante") lineTypes.append("ante")
elif (hand[i].startswith("*** FLOP *** [")): elif line.startswith("*** FLOP *** ["):
lineTypes.append("cards") lineTypes.append("cards")
currentStreet="flop" currentStreet="flop"
elif (hand[i].startswith("*** TURN *** [")): elif line.startswith("*** TURN *** ["):
lineTypes.append("cards") lineTypes.append("cards")
currentStreet="turn" currentStreet="turn"
elif (hand[i].startswith("*** RIVER *** [")): elif line.startswith("*** RIVER *** ["):
lineTypes.append("cards") lineTypes.append("cards")
currentStreet="river" currentStreet="river"
elif (hand[i].startswith("*** 3")): elif line.startswith("*** 3"):
lineTypes.append("ignore") lineTypes.append("ignore")
currentStreet=0 currentStreet=0
elif (hand[i].startswith("*** 4")): elif line.startswith("*** 4"):
lineTypes.append("ignore") lineTypes.append("ignore")
currentStreet=1 currentStreet=1
elif (hand[i].startswith("*** 5")): elif line.startswith("*** 5"):
lineTypes.append("ignore") lineTypes.append("ignore")
currentStreet=2 currentStreet=2
elif (hand[i].startswith("*** 6")): elif line.startswith("*** 6"):
lineTypes.append("ignore") lineTypes.append("ignore")
currentStreet=3 currentStreet=3
elif (hand[i].startswith("*** 7") or hand[i]=="*** RIVER ***"): elif line.startswith("*** 7") or line == "*** RIVER ***":
lineTypes.append("ignore") lineTypes.append("ignore")
currentStreet=4 currentStreet=4
elif (hand[i].find(" shows [")!=-1): elif " shows [" in line:
lineTypes.append("cards") lineTypes.append("cards")
elif (hand[i].startswith("Table '")): elif line.startswith("Table '"):
lineTypes.append("table") lineTypes.append("table")
else: else:
raise FpdbError("unrecognised linetype in:"+hand[i]) raise FpdbError("unrecognised linetype in:"+hand[i])

52
pyfpdb/test_Everleaf.py Normal file
View File

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
import EverleafToFpdb
import py
def checkGameInfo(hhc, header, info):
assert hhc.determineGameType(header) == info
def testGameInfo():
hhc = EverleafToFpdb.Everleaf(autostart=False)
pairs = (
(u"""Everleaf Gaming Game #3732225
***** Hand history for game #3732225 *****
Blinds 0.50/ 1 NL Hold'em - 2009/01/11 - 16:09:40
Table Casino Lyon Vert 58
Seat 3 is the button
Total number of players: 6""",
{'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'0.50', 'bb':'1', 'currency':'EUR'}),
("""Everleaf Gaming Game #55198191
***** Hand history for game #55198191 *****
Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 10:02:11
Table Speed Kuala
Seat 8 is the button
Total number of players: 10""",
{'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'0.50', 'bb':'1', 'currency':'USD'}),
("""Everleaf Gaming Game #75065769
***** Hand history for game #75065769 *****
Blinds 10/20 NL Hold'em - 2009/02/25 - 17:30:32
Table 2
Seat 1 is the button
Total number of players: 10""",
{'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'10', 'bb':'20', 'currency':'T$'}),
("""Everleaf Gaming Game #65087798
***** Hand history for game #65087798 *****
$0.25/$0.50 7 Card Stud - 2008/12/05 - 21:46:00
Table Plymouth""",
{'type':'ring', 'base':'stud', 'category':'studhi', 'limitType':'fl', 'sb':'0.25', 'bb':'0.50', 'currency':'USD'}),
("""Everleaf Gaming Game #65295370
***** Hand history for game #65295370 *****
Blinds $0.50/$1 PL Omaha - 2008/12/07 - 21:59:48
Table Guanajuato""",
{'type':'ring', 'base':'hold', 'category':'omahahi', 'limitType':'pl', 'sb':'0.50', 'bb':'1','currency':'USD'})
)
for (header, info) in pairs:
yield checkGameInfo, hhc, header, info

23
pyfpdb/test_FullTilt.py Normal file
View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
import FulltiltToFpdb
import py
def checkGameInfo(hhc, header, info):
assert hhc.determineGameType(header) == info
def testGameInfo():
hhc = FulltiltToFpdb.FullTilt(autostart=False)
pairs = (
("Full Tilt Poker Game #10777181585: Table Deerfly (deep 6) - $0.01/$0.02 - Pot Limit Omaha Hi - 2:24:44 ET - 2009/02/22",
{'type':'ring', 'base':'hold', 'category':'omahahi', 'limitType':'pl', 'sb':'0.01', 'bb':'0.02', 'currency':'USD'}),
("Full Tilt Poker Game #10773265574: Table Butte (6 max) - $0.01/$0.02 - Pot Limit Hold'em - 21:33:46 ET - 2009/02/21",
{'type':'ring', 'base':'hold', 'category':'holdem', 'limitType':'pl', 'sb':'0.01', 'bb':'0.02', 'currency':'USD'}),
("Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09",
{'type':'ring', 'base':'hold', 'category':'holdem', 'limitType':'nl', 'sb':'0.05', 'bb':'0.10', 'currency':'USD'}),
("Full Tilt Poker Game #10809877615: Table Danville - $0.50/$1 Ante $0.10 - Limit Razz - 21:47:27 ET - 2009/02/23",
{'type':'ring', 'base':'stud', 'category':'razz', 'limitType':'fl', 'sb':'0.50', 'bb':'1', 'currency':'USD'})
)
for (header, info) in pairs:
yield checkGameInfo, hhc, header, info

20
pyfpdb/test_PokerStars.py Normal file
View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
import PokerStarsToFpdb
import py
def checkGameInfo(hhc, header, info):
assert hhc.determineGameType(header) == info
def testGameInfo():
hhc = PokerStarsToFpdb.PokerStars(autostart=False)
pairs = (
(u"PokerStars Game #20461877044: Hold'em No Limit ($1/$2) - 2008/09/16 18:58:01 ET",
{'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'1', 'bb':'2', 'currency':'USD'}),
(u"PokerStars Game #5999635897: HORSE (Omaha Hi/Lo Limit, $2/$4) - 2006/08/21 - 13:59:19 (ET)",
{'type':'ring', 'base':'hold', 'category':'omahahilo', 'limitType':'fl', 'sb':'2', 'bb':'4','currency':'USD'})
)
for (header, info) in pairs:
yield checkGameInfo, hhc, header, info

40
pyfpdb/test_fpdb_simple.py Executable file
View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
import fpdb_simple
import datetime
import py
def checkDateParse(header, result):
assert fpdb_simple.parseHandStartTime(header) == result
def testPokerStarsHHDate():
tuples = (
("PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET]",
datetime.datetime(2008,9,7,11,23,14)),
("PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/08/17 - 01:14:43 (ET)",
datetime.datetime(2008,11,12,15,00,48)),
("PokerStars Game #21969660557: Hold'em No Limit ($0.50/$1.00) - 2008/09/07 06:23:14 ET",
datetime.datetime(2008,8,17,6,14,43))
)
#def testFullTiltHHDate(self):
# sitngo1 = "Full Tilt Poker Game #10311865543: $1 + $0.25 Sit & Go (78057629), Table 1 - 25/50 - No Limit Hold'em - 0:07:45 ET - 2009/01/29"
# cash1 = "Full Tilt Poker Game #9403951181: Table CR - tay - $0.05/$0.10 - No Limit Hold'em - 9:40:20 ET - 2008/12/09"
# cash2 = "Full Tilt Poker Game #9468383505: Table Bike (deep 6) - $0.05/$0.10 - No Limit Hold'em - 5:09:36 ET - 2008/12/13"
# result = fpdb_simple.parseHandStartTime(sitngo1,"ftp")
# self.failUnless(result==datetime.datetime(2009,1,29,05,07,45),
# "Date incorrect, expected: 2009-01-29 05:07:45 got: " + str(result))
# result = fpdb_simple.parseHandStartTime(cash1,"ftp")
# self.failUnless(result==datetime.datetime(2008,12,9,14,40,20),
# "Date incorrect, expected: 2008-12-09 14:40:20 got: " + str(result))
# result = fpdb_simple.parseHandStartTime(cash2,"ftp")
# self.failUnless(result==datetime.datetime(2008,12,13,10,9,36),
# "Date incorrect, expected: 2008-12-13 10:09:36 got: " + str(result))
# def testTableDetection(self):
# result = Tables.clean_title("French (deep)")
# self.failUnless(result == "French", "French (deep) parsed incorrectly. Expected 'French' got: " + str(result))
# result = ("French (deep) - $0.25/$0.50 - No Limit Hold'em - Logged In As xxxx")
for (header, result) in tuples:
yield checkDateParse header, result