gameinfo changes to fit db spec in docs/

some temporary bits, broken commit do not use.
This commit is contained in:
Matt Turnbull 2009-03-09 23:03:17 +00:00
parent ebcfebbc2f
commit 0dc1bafdeb
5 changed files with 203 additions and 102 deletions

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- 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,13 +28,11 @@ 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(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_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(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_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_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_Button = re.compile(ur"^Seat (?P<BUTTON>\d+) is the button", 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_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_Button = re.compile(r"^Seat (?P<BUTTON>\d+) is the button", re.MULTILINE) re_Board = re.compile(ur"\[ (?P<CARDS>.+) \]")
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>.+) \]")
def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True): def __init__(self, in_path = '-', out_path = '-', follow = False, autostart=True):
@ -72,29 +71,58 @@ follow : whether to tail -f the input"""
] ]
def determineGameType(self, handText): def determineGameType(self, handText):
info = {} """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.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
# Tourney:
# Everleaf Gaming Game #75065769
# ***** Hand history for game #75065769 *****
# Blinds 10/20 NL Hold'em - 2009/02/25 - 17:30:32
# Table 2
info = {'type':'ring'}
m = self.re_GameInfo.search(handText) m = self.re_GameInfo.search(handText)
if not m: if not m:
return None return None
info.update(m.groupdict()) mg = m.groupdict()
# translations from captured groups to our info strings
limits = { 'NL':'nl', 'PL':'pl', '':'fl' } limits = { 'NL':'nl', 'PL':'pl', '':'fl' }
games = { 'Hold\'em':'hold', 'Omaha':'omahahi', 'Razz':'razz','7 Card Stud':'studhi' } games = { # base, category
"Hold'em" : ('hold','holdem'),
'Omaha' : ('hold','omahahi'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi')
}
currencies = { u'':'EUR', '$':'USD', '':'T$' } currencies = { u'':'EUR', '$':'USD', '':'T$' }
for key in info: if 'LIMIT' in mg:
if key == 'limit': info['limitType'] = limits[mg['LIMIT']]
info[key] = limits[info[key]] if 'GAME' in mg:
if key == 'game': (info['base'], info['category']) = games[mg['GAME']]
info[key] = games[info[key]] if 'SB' in mg:
if key == 'sb': info['sb'] = mg['SB']
pass if 'BB' in mg:
if key == 'bb': info['bb'] = mg['BB']
pass if 'CURRENCY' in mg:
if key == 'currency': info['currency'] = currencies[mg['CURRENCY']]
info[key] = currencies[info[key]] # NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info return info

View File

@ -1,4 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi # Copyright 2008, Carl Gherardi
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -24,7 +26,7 @@ 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)
@ -44,7 +46,8 @@ follow : whether to tail -f the input"""
self.start() self.start()
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
@ -76,30 +79,36 @@ follow : whether to tail -f the input"""
# 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)

View File

@ -109,6 +109,17 @@ class HandHistoryConverter(threading.Thread):
return tmp return tmp
def run(self): def run(self):
for handtext in self.readHands():
self.processHand(handText)
def readHands(self):
"""yield a hand at a time from the input specified by in_path.
If in follow mode, wait for more data to turn up.
Otherwise, raise the no more things exception..."""
while True:
where = file.tell()
lines = file.readlines(1000)
if self.follow: if self.follow:
for handtext in self.tailHands(): for handtext in self.tailHands():
self.processHand(handtext) self.processHand(handtext)
@ -119,14 +130,43 @@ class HandHistoryConverter(threading.Thread):
self.processHand(handtext) self.processHand(handtext)
if self.out_fh != sys.stdout: if self.out_fh != sys.stdout:
self.ouf_fh.close() self.ouf_fh.close()
def paragraphs(file, separator=None):
if not callable(separator):
def separator(line): return line == '\n'
paragraph = []
for line in file:
if separator(line):
if paragraph:
yield ''.join(paragraph)
paragraph = []
else:
paragraph.append(line)
if paragraph: yield ''.join(paragraph)
def tail_f(file):
interval = 1.0
while True:
where = file.tell()
lines = file.readlines(1000)
if not lines:
time.sleep(interval)
file.seek(where)
else:
yield line
def tailHands(self): def tailHands(self):
"""pseudo-code""" """pseudo-code"""
interval = 1.0
while True: while True:
ifile.tell() pos = ifile.tell()
text = ifile.read() text = ifile.readlines(1000)
if nomoretext: if not text:
wait or sleep time.sleep(interval)
ifile.seek(pos)
else: else:
ahand = thenexthandinthetext ahand = thenexthandinthetext
yield(ahand) yield(ahand)
@ -148,11 +188,13 @@ class HandHistoryConverter(threading.Thread):
return return
hand = None hand = None
if gametype['game'] in ("hold", "omaha"): if gametype['base'] == 'hold':
hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext) hand = Hand.HoldemOmahaHand(self, self.sitename, gametype, handtext)
elif gametype['game'] in ("razz","stud","stud8"): elif gametype['base'] == 'stud':
hand = Hand.StudHand(self, self.sitename, gametype, handtext) hand = Hand.StudHand(self, self.sitename, gametype, handtext)
elif gametype['base'] == 'draw':
hand = Hand.DrawHand(self, self.sitename, gametype, handtext)
if hand: if hand:
hand.writeHand(self.out_fh) hand.writeHand(self.out_fh)
else: else:
@ -248,6 +290,19 @@ class HandHistoryConverter(threading.Thread):
# [ ring, hold, nl , sb, bb ] # [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes # Valid types specified in docs/tabledesign.html in Gametypes
def determineGameType(self): abstract def determineGameType(self): 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
@ -330,8 +385,8 @@ class HandHistoryConverter(threading.Thread):
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): def openFile(self):
"""Read in_path into self.obs or self.doc""" """open in_path according to self.codepage"""
if(self.filetype == "text"): if(self.filetype == "text"):
if self.in_path == '-': if self.in_path == '-':

View File

@ -1,4 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2008, Carl Gherardi # Copyright 2008, Carl Gherardi
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
@ -67,7 +69,7 @@ from HandHistoryConverter import *
class PokerStars(HandHistoryConverter): class PokerStars(HandHistoryConverter):
# Static regexes # Static regexes
re_GameInfo = re.compile('PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud) (?P<LTYPE>No Limit|Limit|Pot Limit),? \(?\$?(?P<SB>[.0-9]+)/\$?(?P<BB>[.0-9]+)\) - (?P<DATETIME>.*$)', re.MULTILINE) re_GameInfo = re.compile('PokerStars Game #(?P<HID>[0-9]+):\s+(HORSE)? \(?(?P<GAME>Hold\'em|Razz|7 Card Stud) (?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_SplitHands = re.compile('\n\n+')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE) 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_Button = re.compile('Seat #(?P<BUTTON>\d+) is the button', re.MULTILINE)
@ -88,7 +90,8 @@ follow : whether to tail -f the input"""
self.start() self.start()
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
@ -111,33 +114,37 @@ follow : whether to tail -f the input"""
return [] return []
def determineGameType(self, handText): def determineGameType(self, handText):
game = None info = {'type':'ring'}
structure = None
sb = None
bb = None
info = {}
m = self.re_GameInfo.search(handText) m = self.re_GameInfo.search(handText)
if m: if not m:
info.update(m.groupdict())
else:
return None return None
mg = m.groupdict()
ltypes = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' } # translations from captured groups to our info strings
gtypes = { 'Hold\'em':'hold', 'Omaha':'omahahi', 'Razz':'razz','7 Card Stud':'studhi' } limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
for key in info: games = { # base, category
if key == 'LTYPE': "Hold'em" : ('hold','holdem'),
structure = ltypes[info[key]] 'Omaha Hi' : ('hold','omahahi'),
if key == 'GAME': 'Razz' : ('stud','razz'),
game = gtypes[info[key]] '7 Card Stud' : ('stud','studhi')
if key == 'SB': }
sb = info[key] currencies = { u'':'EUR', '$':'USD', '':'T$' }
if key == 'BB': if 'LIMIT' in mg:
bb = info[key] info['limitType'] = limits[mg['LIMIT']]
if 'GAME' in mg:
gametype = ["ring", game, structure, sb, bb] (info['base'], info['category']) = games[mg['GAME']]
return gametype 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):
info = {} info = {}

View File

@ -1,36 +1,38 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import EverleafToFpdb import EverleafToFpdb
import py import py
class TestEverleaf:
def testGameInfo1(self):
e = EverleafToFpdb.Everleaf(autostart=False) def checkGameInfo(hhc, header, info):
g = """Everleaf Gaming Game #3732225 assert hhc.determineGameType(header) == info
def testGameInfo():
hhc = EverleafToFpdb.Everleaf(autostart=False)
pairs = (
(u"""Everleaf Gaming Game #3732225
***** Hand history for game #3732225 ***** ***** Hand history for game #3732225 *****
Blinds 0.50/ 1 NL Hold'em - 2009/01/11 - 16:09:40 Blinds 0.50/ 1 NL Hold'em - 2009/01/11 - 16:09:40
Table Casino Lyon Vert 58 Table Casino Lyon Vert 58
Seat 3 is the button Seat 3 is the button
Total number of players: 6""" Total number of players: 6""",
assert e.determineGameType(g) == {'sb':'0.50', 'bb':'1','game':"hold", 'currency':'EUR', 'limit':'nl'} {'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'0.50', 'bb':'1', 'currency':'EUR'}),
("""Everleaf Gaming Game #55198191
def testGameInfo2(self):
e = EverleafToFpdb.Everleaf(autostart=False)
g = """Everleaf Gaming Game #55198191
***** Hand history for game #55198191 ***** ***** Hand history for game #55198191 *****
Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 10:02:11 Blinds $0.50/$1 NL Hold'em - 2008/09/01 - 10:02:11
Table Speed Kuala Table Speed Kuala
Seat 8 is the button Seat 8 is the button
Total number of players: 10""" Total number of players: 10""",
assert e.determineGameType(g) == {'sb':'0.50', 'bb':'1','game':"hold", 'currency':'USD', 'limit':'nl'} {'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'0.50', 'bb':'1', 'currency':'USD'}),
("""Everleaf Gaming Game #75065769
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 ***** ***** 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
Seat 1 is the button Seat 1 is the button
Total number of players: 10""" Total number of players: 10""",
assert e.determineGameType(g) == {'sb':'10', 'bb':'20','game':"hold", 'currency':'T$', 'limit':'nl'} {'type':'ring', 'base':"hold", 'category':'holdem', 'limitType':'nl', 'sb':'10', 'bb':'20', 'currency':'T$'})
)
for (header, info) in pairs:
yield checkGameInfo, hhc, header, info