This commit is contained in:
Matt Turnbull 2009-03-06 18:10:04 +00:00
parent 426b5c2db5
commit ca6f6e513b
7 changed files with 110 additions and 39 deletions

View File

@ -1,5 +1,5 @@
#!/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
@ -27,16 +27,16 @@ 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(u"^(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_HandInfo = re.compile(u".*#(?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_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_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_HandInfo = re.compile(r".*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?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.MULTILINE) #re_HandInfo = re.compile(r".*#(?P<HID>[0-9]+)\n.*\nBlinds \$?(?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.MULTILINE)
re_Button = re.compile(r"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE) re_Button = re.compile(r"^Seat (?P<BUTTON>\d+) is the button", 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_PlayerInfo = re.compile(u"^Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \(\s+((?:\$| €|) (?P<CASH>[.0-9]+) (USD|EUR|)|new player|All-in) \)", re.MULTILINE)
re_Board = re.compile(r"\[ (?P<CARDS>.+) \]") re_Board = re.compile(r"\[ (?P<CARDS>.+) \]")
def __init__(self, in_path = '-', out_path = '-', follow = False): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
@ -45,23 +45,24 @@ follow : whether to tail -f the input"""
logging.info("Initialising Everleaf converter class") logging.info("Initialising Everleaf converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"
if autostart:
self.start() self.start()
# TODO: It's not clear that init should start the thread.
def compilePlayerRegexs(self, players): 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' if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs. # we need to recompile the player regexs.
self.compiledPlayers = players self.compiledPlayers = players
player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")" player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
logging.debug("player_re: "+ player_re) 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_PostSB = re.compile(u"^%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_PostBB = re.compile(u"^%s: posts big blind \[(?:\$| €|) (?P<BB>[.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_PostBoth = re.compile(u"^%s: posts both blinds \[(?:\$| €|) (?P<SBBB>[.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(u"^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(u"^%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_ShowdownAction = re.compile(u"^%s shows \[ (?P<CARDS>.*) \]" % 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_CollectPot = re.compile(u"^%s wins (?:\$| €|) (?P<POT>[.\d]+) (USD|EUR|chips)(.*?\[ (?P<CARDS>.*?) \])?" % player_re, re.MULTILINE)
self.re_SitsOut = re.compile(r"^%s sits out" % player_re, re.MULTILINE) self.re_SitsOut = re.compile(u"^%s sits out" % player_re, re.MULTILINE)
def readSupportedGames(self): def readSupportedGames(self):
return [["ring", "hold", "nl"], return [["ring", "hold", "nl"],
@ -71,6 +72,33 @@ follow : whether to tail -f the input"""
] ]
def determineGameType(self, handText): def determineGameType(self, handText):
info = {}
m = self.re_GameInfo.search(handText)
if not m:
return None
info.update(m.groupdict())
limits = { 'NL':'nl', 'PL':'pl', '':'fl' }
games = { 'Hold\'em':'hold', 'Omaha':'omahahi', 'Razz':'razz','7 Card Stud':'studhi' }
currencies = { u'':'EUR', '$':'USD', '':'T$' }
for key in info:
if key == 'limit':
info[key] = limits[info[key]]
if key == 'game':
info[key] = games[info[key]]
if key == 'sb':
pass
if key == 'bb':
pass
if key == 'currency':
info[key] = currencies[info[key]]
return info
def determineGameType2(self, handText):
# Cheating with this regex, only support nlhe at the moment # Cheating with this regex, only support nlhe at the moment
# 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
@ -84,6 +112,7 @@ follow : whether to tail -f the input"""
structure = "" # nl, pl, cn, cp, fl structure = "" # nl, pl, cn, cp, fl
game = "" game = ""
currency = "USD" # USD, EUR
m = self.re_GameInfo.search(handText) m = self.re_GameInfo.search(handText)
if m == None: if m == None:
@ -103,7 +132,7 @@ follow : whether to tail -f the input"""
elif m.group('GAME') == "7 Card Stud": elif m.group('GAME') == "7 Card Stud":
game = "studhi" # Everleaf currently only does Hi stud game = "studhi" # Everleaf currently only does Hi stud
gametype = ["ring", game, structure, m.group('SB'), m.group('BB')] gametype = ["ring", game, structure, m.group('SB'), m.group('BB'), currency]
return gametype return gametype
@ -111,7 +140,7 @@ follow : whether to tail -f the input"""
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')

View File

@ -31,7 +31,7 @@ class FullTilt(HandHistoryConverter):
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, in_path = '-', out_path = '-', follow = False): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
@ -40,8 +40,10 @@ follow : whether to tail -f the input"""
logging.info("Initialising FullTilt converter class") logging.info("Initialising FullTilt converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"
if autostart:
self.start() self.start()
def compilePlayerRegexs(self, players): def compilePlayerRegexs(self, players):
if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
# we need to recompile the player regexs. # we need to recompile the player regexs.

View File

@ -314,7 +314,7 @@ Map the tuple self.gametype onto the pokerstars string describing it
} }
logging.debug("gametype: %s" %(self.gametype)) logging.debug("gametype: %s" %(self.gametype))
retstring = "%s %s" %(gs[self.gametype[1]], ls[self.gametype[2]]) retstring = "%s %s" %(gs[self.gametype['game']], ls[self.gametype['limit']])
return retstring return retstring
@ -363,22 +363,22 @@ Map the tuple self.gametype onto the pokerstars string describing it
class HoldemOmahaHand(Hand): class HoldemOmahaHand(Hand):
def __init__(self, hhc, sitename, gametype, handText): def __init__(self, hhc, sitename, gametype, handText):
if gametype[1] not in ["hold","omaha"]: if gametype['game'] not in ["hold","omaha"]:
pass # or indeed don't pass and complain instead pass # or indeed don't pass and complain instead
logging.debug("HoldemOmahaHand") logging.debug("HoldemOmahaHand")
self.streetList = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order self.streetList = ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER'] # a list of the observed street names in order
self.communityStreets = ['FLOP', 'TURN', 'RIVER'] self.communityStreets = ['FLOP', 'TURN', 'RIVER']
self.actionStreets = ['PREFLOP','FLOP','TURN','RIVER'] self.actionStreets = ['PREFLOP','FLOP','TURN','RIVER']
Hand.__init__(self, sitename, gametype, handText) Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype[3] self.sb = gametype['sb']
self.bb = gametype[4] self.bb = gametype['bb']
#Populate a HoldemOmahaHand #Populate a HoldemOmahaHand
#Generally, we call 'read' methods here, which get the info according to the particular filter (hhc) #Generally, we call 'read' methods here, which get the info according to the particular filter (hhc)
# which then invokes a 'addXXX' callback # which then invokes a 'addXXX' callback
hhc.readHandInfo(self) hhc.readHandInfo(self)
hhc.readPlayerStacks(self) hhc.readPlayerStacks(self)
hhc.compilePlayerRegexs(players = set([player[1] for player in self.players])) hhc.compilePlayerRegexs(self)
hhc.markStreets(self) hhc.markStreets(self)
hhc.readBlinds(self) hhc.readBlinds(self)
hhc.readButton(self) hhc.readButton(self)
@ -441,7 +441,7 @@ Card ranks will be uppercased
#May be more than 1 bb posting #May be more than 1 bb posting
if self.gametype[2] == "fl": if self.gametype['limit'] == "fl":
(smallbet, bigbet) = self.lookupLimitBetSize() (smallbet, bigbet) = self.lookupLimitBetSize()
else: else:
smallbet = self.sb smallbet = self.sb
@ -533,7 +533,7 @@ Card ranks will be uppercased
class DrawHand(Hand): class DrawHand(Hand):
def __init__(self, hhc, sitename, gametype, handText): def __init__(self, hhc, sitename, gametype, handText):
if gametype[1] not in ["badugi","5-card-draw"]: if gametype['game'] not in ["badugi","5-card-draw"]:
pass # or indeed don't pass and complain instead pass # or indeed don't pass and complain instead
def discardHoleCards(self, cards, player): def discardHoleCards(self, cards, player):
@ -548,19 +548,19 @@ class DrawHand(Hand):
class StudHand(Hand): class StudHand(Hand):
def __init__(self, hhc, sitename, gametype, handText): def __init__(self, hhc, sitename, gametype, handText):
if gametype[1] not in ["razz","stud","stud8"]: if gametype['game'] not in ["razz","stud","stud8"]:
pass # or indeed don't pass and complain instead 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.streetList = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
self.holeStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] self.holeStreets = ['ANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
Hand.__init__(self, sitename, gametype, handText) Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype[3] self.sb = gametype['sb']
self.bb = gametype[4] self.bb = gametype['bb']
#Populate the StudHand #Populate the StudHand
#Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc) #Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc)
# which then invokes a 'addXXX' callback # which then invokes a 'addXXX' callback
hhc.readHandInfo(self) hhc.readHandInfo(self)
hhc.readPlayerStacks(self) hhc.readPlayerStacks(self)
hhc.compilePlayerRegexs(players = set([player[1] for player in self.players])) hhc.compilePlayerRegexs(self)
hhc.markStreets(self) hhc.markStreets(self)
hhc.readAntes(self) hhc.readAntes(self)
hhc.readBringIn(self) hhc.readBringIn(self)

View File

@ -88,7 +88,7 @@ class HandHistoryConverter(threading.Thread):
# write to stdout # write to stdout
self.out_fh = sys.stdout self.out_fh = sys.stdout
else: else:
self.out_fh = open(self.out_path, 'a') self.out_fh = open(self.out_path, 'a') #TODO: append may be overly conservative.
self.sitename = sitename self.sitename = sitename
self.follow = follow self.follow = follow
self.compiledPlayers = set() self.compiledPlayers = set()
@ -117,6 +117,8 @@ class HandHistoryConverter(threading.Thread):
logging.info("Parsing %d hands" % len(handsList)) logging.info("Parsing %d hands" % len(handsList))
for handtext in handsList: for handtext in handsList:
self.processHand(handtext) self.processHand(handtext)
if self.out_fh != sys.stdout:
self.ouf_fh.close()
def tailHands(self): def tailHands(self):
"""pseudo-code""" """pseudo-code"""
@ -141,19 +143,20 @@ class HandHistoryConverter(threading.Thread):
def processHand(self, handtext): def processHand(self, handtext):
gametype = self.determineGameType(handtext) gametype = self.determineGameType(handtext)
logging.debug("gametype %s" % gametype)
if gametype is None: if gametype is None:
return return
hand = None hand = None
if gametype[1] in ("hold", "omaha"): if gametype['game'] in ("hold", "omaha"):
hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext) hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)
elif gametype[1] in ("razz","stud","stud8"): elif gametype['game'] in ("razz","stud","stud8"):
hand = Hand.StudHand(self, self.sitename, gametype, handtext) hand = Hand.StudHand(self, self.sitename, gametype, handtext)
if hand: if hand:
hand.writeHand(self.out_fh) hand.writeHand(self.out_fh)
else: else:
logging.info("Unrecognised game type: %s" % gametype[1]) logging.info("Unsupported game type: %s" % gametype)
# TODO: pity we don't know the HID at this stage. Log the entire hand? # 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 :) # From the log we can deduce that it is the hand after the one before :)

View File

@ -75,7 +75,7 @@ class PokerStars(HandHistoryConverter):
re_Board = re.compile(r"\[(?P<CARDS>.+)\]") 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]+)') # 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): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
"""\ """\
in_path (default '-' = sys.stdin) in_path (default '-' = sys.stdin)
out_path (default '-' = sys.stdout) out_path (default '-' = sys.stdout)
@ -84,6 +84,7 @@ follow : whether to tail -f the input"""
logging.info("Initialising PokerStars converter class") logging.info("Initialising PokerStars converter class")
self.filetype = "text" self.filetype = "text"
self.codepage = "cp1252" self.codepage = "cp1252"
if autostart:
self.start() self.start()

View File

@ -231,7 +231,7 @@ 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
hhbase = self.config.get_import_parameters().get("hhArchiveBase") hhbase = self.config.get_import_parameters().get("hhArchiveBase")
hhbase = os.path.expanduser(hhbase) hhbase = os.path.expanduser(hhbase)

36
pyfpdb/test_Everleaf.py Normal file
View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
import EverleafToFpdb
import py
class TestEverleaf:
def testGameInfo1(self):
e = EverleafToFpdb.Everleaf(autostart=False)
g = """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"""
assert e.determineGameType(g) == {'sb':'0.50', 'bb':'1','game':"hold", 'currency':'EUR', 'limit':'nl'}
def testGameInfo2(self):
e = EverleafToFpdb.Everleaf(autostart=False)
g = """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"""
assert e.determineGameType(g) == {'sb':'0.50', 'bb':'1','game':"hold", 'currency':'USD', 'limit':'nl'}
def testGameInfo3(self):
# Note: It looks difficult to distinguish T$ from play money.
e = EverleafToFpdb.Everleaf(autostart=False)
g = """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"""
assert e.determineGameType(g) == {'sb':'10', 'bb':'20','game':"hold", 'currency':'T$', 'limit':'nl'}