Merge branch 'master' of git://git.assembla.com/fpdboz.git
Conflicts: pyfpdb/Configuration.py pyfpdb/Database.py
This commit is contained in:
commit
27e0df2a24
|
@ -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,7 @@ 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') != "":
|
||||||
|
@ -639,7 +633,7 @@ class Config:
|
||||||
|
|
||||||
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()
|
||||||
|
|
|
@ -149,8 +149,8 @@ class Database:
|
||||||
cv = "card%dValue" % i
|
cv = "card%dValue" % i
|
||||||
if cv not in d or d[cv] == None:
|
if cv not in d or d[cv] == None:
|
||||||
break
|
break
|
||||||
elif d[cv] == 0:
|
elif d[key] == 0:
|
||||||
cards = "%sxx" % cards
|
cards += "xx"
|
||||||
else:
|
else:
|
||||||
cs = "card%dSuit" % i
|
cs = "card%dSuit" % i
|
||||||
cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs])
|
cards = "%s%s%s" % (cards, ranks[d[cv]], d[cs])
|
||||||
|
|
|
@ -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
|
||||||
|
@ -11,7 +12,7 @@
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
@ -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"
|
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True, debugging=False):
|
||||||
HandHistoryConverter.__init__(self, config, file, sitename="Everleaf") # Call super class init.
|
"""\
|
||||||
self.sitename = "Everleaf"
|
in_path (default '-' = sys.stdin)
|
||||||
self.setFileType("text", "cp1252")
|
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, hand):
|
||||||
try:
|
players = set([player[1] for player in hand.players])
|
||||||
self.ofile = os.path.join(self.hhdir, file.split(os.path.sep)[-2]+"-"+os.path.basename(file))
|
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
|
||||||
except:
|
# we need to recompile the player regexs.
|
||||||
self.ofile = os.path.join(self.hhdir, "x"+strftime("%d-%m-%y")+os.path.basename(file))
|
self.compiledPlayers = players
|
||||||
|
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
|
||||||
def compilePlayerRegexs(self):
|
logging.debug("player_re: "+ player_re)
|
||||||
player_re = "(?P<PNAME>" + "|".join(map(re.escape, self.players)) + ")"
|
self.re_PostSB = re.compile(ur"^%s: posts small blind \[(?:\$| €|) (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
#print "DEBUG player_re: " + player_re
|
self.re_PostBB = re.compile(ur"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_PostSB = re.compile(r"^%s: posts small blind \[\$? (?P<SB>[.0-9]+)" % player_re, re.MULTILINE)
|
self.re_PostBoth = re.compile(ur"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.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(ur"^%s: posts ante \[(?:\$| €|) (?P<ANTE>[.0-9]+)" % player_re, re.MULTILINE)
|
||||||
self.re_PostBoth = re.compile(r"^%s: posts both blinds \[\$? (?P<SBBB>[.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(r"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
self.re_HeroCards = re.compile(ur"^Dealt to %s \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
||||||
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_Action = re.compile(ur"^%s(?P<ATYPE>: bets| checks| raises| calls| folds)(\s\[(?:\$| €|) (?P<BET>[.\d]+) (USD|EUR|)\])?" % player_re, re.MULTILINE)
|
||||||
self.re_ShowdownAction = re.compile(r"^%s shows \[ (?P<CARDS>.*) \]" % 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_CollectPot = re.compile(r"^%s wins \$ (?P<POT>[.\d]+) (USD|EUR)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
|
self.re_ShowdownAction = re.compile(ur"^%s shows \[ (?P<CARDS>.*) \]" % player_re, re.MULTILINE)
|
||||||
self.re_SitsOut = re.compile(r"^%s sits out" % 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"
|
mg = m.groupdict()
|
||||||
elif m.group('LTYPE') == "PL":
|
|
||||||
structure = "pl"
|
# translations from captured groups to our info strings
|
||||||
else:
|
limits = { 'NL':'nl', 'PL':'pl', '':'fl' }
|
||||||
structure = "fl" # we don't support it, but there should be how to detect it at least.
|
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
|
||||||
|
|
||||||
if m.group('GAME') == "Hold\'em":
|
|
||||||
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')]
|
|
||||||
|
|
||||||
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
1
pyfpdb/Exceptions.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
class FpdbParseError(Exception): pass
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
if m.group('LTYPE') == "No ":
|
|
||||||
structure = "nl"
|
|
||||||
elif m.group('LTYPE') == "Pot ":
|
|
||||||
structure = "pl"
|
|
||||||
elif m.group('LTYPE') == None:
|
|
||||||
structure = "fl"
|
|
||||||
|
|
||||||
if m.group('GAME') == "Hold\'em":
|
|
||||||
game = "hold"
|
|
||||||
elif m.group('GAME') == "Omaha":
|
|
||||||
game = "omahahi"
|
|
||||||
elif m.group('GAME') == "Razz":
|
|
||||||
game = "razz"
|
|
||||||
|
|
||||||
logging.debug("HandInfo: %s", m.groupdict())
|
|
||||||
|
|
||||||
gametype = ["ring", game, structure, m.group('SB'), m.group('BB')]
|
|
||||||
|
|
||||||
return gametype
|
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'),
|
||||||
|
'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):
|
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
55
pyfpdb/GuiAutoImport.py
Normal file → Executable 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
|
||||||
|
|
||||||
i = GuiAutoImport(settings)
|
parser = OptionParser()
|
||||||
main_window = gtk.Window()
|
parser.add_option("-q", "--quiet", action="store_false", dest="gui", default=True, help="don't start gui")
|
||||||
main_window.connect("destroy", destroy)
|
|
||||||
main_window.add(i.mainVBox)
|
(options, sys.argv) = parser.parse_args()
|
||||||
main_window.show()
|
|
||||||
gtk.main()
|
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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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""")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
576
pyfpdb/Hand.py
576
pyfpdb/Hand.py
|
@ -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
|
|
||||||
#
|
|
||||||
|
|
||||||
# dict from player names to lists of hole cards
|
|
||||||
self.holecards = {}
|
|
||||||
|
|
||||||
|
|
||||||
|
# Collections indexed by player names
|
||||||
|
self.holecards = {} # dict from player names to dicts by street ... of tuples ... of 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,8 +316,8 @@ 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
|
||||||
|
|
||||||
def lookupLimitBetSize(self):
|
def lookupLimitBetSize(self):
|
||||||
|
@ -421,65 +338,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)
|
||||||
|
|
||||||
|
@ -516,37 +533,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
|
class DrawHand(Hand):
|
||||||
# logic for which players get to showdown
|
def __init__(self, hhc, sitename, gametype, handText):
|
||||||
# I'm just not sure we need to do this so heavily.. and if we do, it's probably better to use pokerlib
|
if gametype['base'] != 'draw':
|
||||||
#if self.holecards[player[1]]: # empty list default is false
|
pass # or indeed don't pass and complain instead
|
||||||
#hole = self.holecards[player[1]]
|
|
||||||
##board = []
|
def discardHoleCards(self, cards, player):
|
||||||
##for s in self.board.values():
|
try:
|
||||||
##board += s
|
self.checkPlayerExists(player)
|
||||||
##playerhand = self.bestHand('hi', board+hole)
|
for card in cards:
|
||||||
##print "Seat %d: %s showed %s and won/lost with %s" % (player[0], player[1], hole, playerhand)
|
self.holecards[player].remove(card)
|
||||||
#print "Seat %d: %s showed %s" % (player[0], player[1], hole)
|
except FpdbParseError, e:
|
||||||
#else:
|
pass
|
||||||
#print "Seat %d: %s mucked or folded" % (player[0], player[1])
|
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 writeStudHand(self, fh=sys.__stdout__):
|
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))
|
||||||
|
|
||||||
|
@ -561,30 +656,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)
|
||||||
|
|
||||||
|
@ -632,77 +785,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):
|
||||||
|
|
||||||
|
|
|
@ -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(self.obs)
|
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"]]
|
||||||
|
@ -195,15 +233,28 @@ class HandHistoryConverter:
|
||||||
# [ 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, handText): 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
|
||||||
|
|
333
pyfpdb/PokerStarsToFpdb.py
Executable file
333
pyfpdb/PokerStarsToFpdb.py
Executable 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)
|
|
@ -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()
|
|
||||||
|
|
|
@ -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,10 +198,15 @@ 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])
|
||||||
|
|
||||||
self.addToDirList = filter(lambda x: self.addImportDirectory(x, True, self.addToDirList[x][0], self.addToDirList[x][1]), self.addToDirList)
|
self.addToDirList = filter(lambda x: self.addImportDirectory(x, True, self.addToDirList[x][0], self.addToDirList[x][1]), self.addToDirList)
|
||||||
|
|
||||||
|
@ -224,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
|
||||||
|
|
52
pyfpdb/test_Everleaf.py
Normal file
52
pyfpdb/test_Everleaf.py
Normal 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
23
pyfpdb/test_FullTilt.py
Normal 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
20
pyfpdb/test_PokerStars.py
Normal 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
40
pyfpdb/test_fpdb_simple.py
Executable 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
|
Loading…
Reference in New Issue
Block a user