2010-07-08 20:01:03 +02:00
#!/usr/bin/env python
2009-03-12 07:17:46 +01:00
# -*- coding: utf-8 -*-
#
2010-07-04 03:05:16 +02:00
# Copyright 2008-2010, Carl Gherardi
2009-01-27 08:22:15 +01:00
#
# 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
########################################################################
2010-09-22 17:51:56 +02:00
import L10n
_ = L10n . get_translation ( )
2009-01-27 08:22:15 +01:00
import sys
2009-03-12 07:17:46 +01:00
import logging
2009-01-27 08:22:15 +01:00
from HandHistoryConverter import *
# Betfair HH format
class Betfair ( HandHistoryConverter ) :
2009-08-09 16:19:43 +02:00
sitename = ' Betfair '
filetype = " text "
codepage = " cp1252 "
siteId = 7 # Needs to match id entry in Sites database
2009-03-12 07:17:46 +01:00
# Static regexes
2010-09-06 11:25:17 +02:00
re_GameInfo = re . compile ( " ^(?P<LIMIT>NL|PL|) (?P<CURRENCY> \ $|)?(?P<SB>[.0-9]+)/ \ $?(?P<BB>[.0-9]+) (?P<GAME>(Texas Hold \' em|Omaha Hi|Omaha|Razz)) " , re . MULTILINE )
2010-08-25 06:01:17 +02:00
re_SplitHands = re . compile ( r ' \ n \ n+ ' )
2010-10-01 04:51:38 +02:00
re_HandInfo = re . compile ( " \ * \ * \ * \ * \ * Betfair Poker Hand History for Game (?P<HID>[0-9]+) \ * \ * \ * \ * \ * \n (?P<LIMIT>NL|PL|) (?P<CURRENCY> \ $|)?(?P<SB>[.0-9]+)/ \ $?(?P<BB>[.0-9]+) (?P<GAMETYPE>(Texas Hold \' em|Omaha|Razz)) - (?P<DATETIME>[a-zA-Z]+, [a-zA-Z]+ \ d+, \ d \ d: \ d \ d: \ d \ d GMT \ d \ d \ d \ d) \n Table (?P<TABLE>[ a-zA-Z0-9]+) \ d-max \ (Real Money \ ) \n Seat (?P<BUTTON>[0-9]+) " , re . MULTILINE )
2009-03-12 10:23:20 +01:00
re_Button = re . compile ( ur " ^Seat (?P<BUTTON> \ d+) is the button " , re . MULTILINE )
re_PlayerInfo = re . compile ( " Seat (?P<SEAT>[0-9]+): (?P<PNAME>.*) \ s \ ( \ s( \ $(?P<CASH>[.0-9]+)) \ ) " )
re_Board = re . compile ( ur " \ [ (?P<CARDS>.+) \ ] " )
2009-03-12 07:17:46 +01:00
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 )
2009-03-12 10:38:48 +01:00
self . re_PostSB = re . compile ( " ^ %s posts small blind \ [ \ $?(?P<SB>[.0-9]+) " % player_re , re . MULTILINE )
self . re_PostBB = re . compile ( " ^ %s posts big blind \ [ \ $?(?P<BB>[.0-9]+) " % player_re , re . MULTILINE )
2009-03-12 07:17:46 +01:00
self . re_Antes = re . compile ( " ^ %s antes asdf sadf sadf " % player_re , re . MULTILINE )
self . re_BringIn = re . compile ( " ^ %s antes asdf sadf sadf " % player_re , re . MULTILINE )
2009-03-12 10:38:48 +01:00
self . re_PostBoth = re . compile ( " ^ %s posts small \ & big blinds \ [ \ $?(?P<SBBB>[.0-9]+) " % player_re , re . MULTILINE )
2009-03-12 07:17:46 +01:00
self . re_HeroCards = re . compile ( " ^Dealt to %s \ [ (?P<CARDS>.*) \ ] " % player_re , re . MULTILINE )
2009-03-12 10:38:48 +01:00
self . re_Action = re . compile ( " ^ %s (?P<ATYPE>bets|checks|raises to|raises|calls|folds)( \ s \ [ \ $(?P<BET>[. \ d]+) \ ])? " % player_re , re . MULTILINE )
2009-03-12 07:17:46 +01:00
self . re_ShowdownAction = re . compile ( " ^ %s shows \ [ (?P<CARDS>.*) \ ] " % player_re , re . MULTILINE )
2009-03-12 10:38:48 +01:00
self . re_CollectPot = re . compile ( " ^ %s wins \ $(?P<POT>[. \ d]+) (.*? \ [ (?P<CARDS>.*?) \ ])? " % player_re , re . MULTILINE )
2009-03-12 07:17:46 +01:00
self . re_SitsOut = re . compile ( " ^ %s sits out " % player_re , re . MULTILINE )
2009-03-12 10:23:20 +01:00
self . re_ShownCards = re . compile ( r " %s (?P<SEAT>[0-9]+) (?P<CARDS>adsfasdf) " % player_re , re . MULTILINE )
2009-01-27 08:22:15 +01:00
2009-03-12 07:17:46 +01:00
def readSupportedGames ( self ) :
2010-09-06 11:25:17 +02:00
return [ [ " ring " , " hold " , " nl " ] ,
[ " ring " , " hold " , " pl " ]
2009-03-12 07:17:46 +01:00
]
def determineGameType ( self , handText ) :
info = { ' type ' : ' ring ' }
m = self . re_GameInfo . search ( handText )
if not m :
2010-09-06 11:25:17 +02:00
tmp = handText [ 0 : 100 ]
log . error ( _ ( " determineGameType: Unable to recognise gametype from: ' %s ' " ) % tmp )
log . error ( _ ( " determineGameType: Raising FpdbParseError " ) )
raise FpdbParseError ( _ ( " Unable to recognise gametype from: ' %s ' " ) % tmp )
2009-03-12 07:17:46 +01:00
mg = m . groupdict ( )
# translations from captured groups to our info strings
2009-03-12 09:26:15 +01:00
limits = { ' NL ' : ' nl ' , ' PL ' : ' pl ' , ' Limit ' : ' fl ' }
2009-03-12 07:17:46 +01:00
games = { # base, category
2009-03-12 09:26:15 +01:00
" Texas Hold ' em " : ( ' hold ' , ' holdem ' ) ,
' Omaha Hi ' : ( ' hold ' , ' omahahi ' ) ,
2010-09-06 11:25:17 +02:00
' Omaha ' : ( ' hold ' , ' omahahi ' ) ,
2009-03-12 09:26:15 +01:00
' Razz ' : ( ' stud ' , ' razz ' ) ,
' 7 Card Stud ' : ( ' stud ' , ' studhi ' )
2009-03-12 07:17:46 +01:00
}
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 ' ] ]
return info
2009-01-27 08:22:15 +01:00
def readHandInfo ( self , hand ) :
2009-03-12 09:26:15 +01:00
m = self . re_HandInfo . search ( hand . handText )
if ( m == None ) :
2010-09-06 11:25:17 +02:00
log . error ( _ ( " Didn ' t match re_HandInfo " ) )
2010-09-17 19:32:50 +02:00
raise FpdbParseError ( _ ( " No match in readHandInfo. " ) )
2009-03-12 09:26:15 +01:00
logging . debug ( " HID %s , Table %s " % ( m . group ( ' HID ' ) , m . group ( ' TABLE ' ) ) )
2009-01-27 08:22:15 +01:00
hand . handid = m . group ( ' HID ' )
hand . tablename = m . group ( ' TABLE ' )
2010-07-08 17:30:02 +02:00
hand . startTime = datetime . datetime . strptime ( m . group ( ' DATETIME ' ) , " % A, % B %d , % H: % M: % S GMT % Y " )
2009-03-12 07:17:46 +01:00
#hand.buttonpos = int(m.group('BUTTON'))
2009-01-27 08:22:15 +01:00
def readPlayerStacks ( self , hand ) :
2009-03-12 07:17:46 +01:00
m = self . re_PlayerInfo . finditer ( hand . handText )
2009-01-27 08:22:15 +01:00
for a in m :
hand . addPlayer ( int ( a . group ( ' SEAT ' ) ) , a . group ( ' PNAME ' ) , a . group ( ' CASH ' ) )
2009-03-12 10:23:20 +01:00
#Shouldn't really dip into the Hand object, but i've no idea how to tell the length of iter m
if len ( hand . players ) < 2 :
2010-08-29 21:17:58 +02:00
logging . info ( _ ( " readPlayerStacks: Less than 2 players found in a hand " ) )
2009-03-12 10:23:20 +01:00
2009-01-27 08:22:15 +01:00
def markStreets ( self , hand ) :
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 Turn \ * \ *(?P<TURN> \ [ \ S \ S \ ].+(?= \ * \ * Dealing River \ * \ *)|.+))? "
2009-03-12 10:23:20 +01:00
r " ( \ * \ * Dealing River \ * \ *(?P<RIVER> \ [ \ S \ S \ ].+))? " , hand . handText , re . DOTALL )
2009-01-27 08:22:15 +01:00
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)
2009-03-12 07:17:46 +01:00
m = self . re_Board . search ( hand . streets [ street ] )
2009-01-27 08:22:15 +01:00
hand . setCommunityCards ( street , m . group ( ' CARDS ' ) . split ( ' , ' ) )
def readBlinds ( self , hand ) :
try :
2009-03-12 10:23:20 +01:00
m = self . re_PostSB . search ( hand . handText )
2009-01-27 08:22:15 +01:00
hand . addBlind ( m . group ( ' PNAME ' ) , ' small blind ' , m . group ( ' SB ' ) )
except : # no small blind
hand . addBlind ( None , None , None )
2009-03-12 10:23:20 +01:00
for a in self . re_PostBB . finditer ( hand . handText ) :
2009-01-27 08:22:15 +01:00
hand . addBlind ( a . group ( ' PNAME ' ) , ' big blind ' , a . group ( ' BB ' ) )
2009-03-12 10:23:20 +01:00
for a in self . re_PostBoth . finditer ( hand . handText ) :
2009-01-27 08:22:15 +01:00
hand . addBlind ( a . group ( ' PNAME ' ) , ' small & big blinds ' , a . group ( ' SBBB ' ) )
2009-03-12 07:17:46 +01:00
def readAntes ( self , hand ) :
logging . debug ( " reading antes " )
2009-10-18 01:56:34 +02:00
m = self . re_Antes . finditer ( hand . handText )
2009-03-12 07:17:46 +01:00
for player in m :
logging . debug ( " hand.addAnte( %s , %s ) " % ( player . group ( ' PNAME ' ) , player . group ( ' ANTE ' ) ) )
hand . a ddAnte ( player . group ( ' PNAME ' ) , player . group ( ' ANTE ' ) )
def readBringIn ( self , hand ) :
m = self . re_BringIn . search ( hand . handText , re . DOTALL )
if m :
2010-08-29 21:22:46 +02:00
logging . debug ( _ ( " Player bringing in: %s for %s " % ( m . group ( ' PNAME ' ) , m . group ( ' BRINGIN ' ) ) ) )
2009-03-12 07:17:46 +01:00
hand . addBringIn ( m . group ( ' PNAME ' ) , m . group ( ' BRINGIN ' ) )
else :
2010-08-14 02:10:13 +02:00
logging . warning ( _ ( " No bringin found " ) )
2009-03-12 07:17:46 +01:00
def readButton ( self , hand ) :
hand . buttonpos = int ( self . re_Button . search ( hand . handText ) . group ( ' BUTTON ' ) )
2009-01-27 08:22:15 +01:00
def readHeroCards ( self , hand ) :
2009-10-18 01:56:34 +02:00
# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
# we need to grab hero's cards
for street in ( ' PREFLOP ' , ' DEAL ' ) :
if street in hand . streets . keys ( ) :
m = self . re_HeroCards . finditer ( hand . streets [ street ] )
for found in m :
hand . hero = found . group ( ' PNAME ' )
newcards = [ c . strip ( ) for c in found . group ( ' CARDS ' ) . split ( ' , ' ) ]
hand . addHoleCards ( street , hand . hero , closed = newcards , shown = False , mucked = False , dealt = True )
2009-01-27 08:22:15 +01:00
2009-03-12 07:17:46 +01:00
def readStudPlayerCards ( self , hand , street ) :
# balh blah blah
pass
2009-01-27 08:22:15 +01:00
def readAction ( self , hand , street ) :
2009-03-12 07:17:46 +01:00
m = self . re_Action . finditer ( hand . streets [ street ] )
2009-01-27 08:22:15 +01:00
for action in m :
2009-03-12 10:38:48 +01:00
if action . group ( ' ATYPE ' ) == ' raises to ' :
2009-03-12 07:17:46 +01:00
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') )
2009-03-12 10:38:48 +01:00
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 ' ) )
2009-01-27 08:22:15 +01:00
else :
2010-08-14 02:10:13 +02:00
sys . stderr . write ( _ ( " DEBUG: unimplemented readAction: ' %s ' ' %s ' " ) % ( action . group ( ' PNAME ' ) , action . group ( ' ATYPE ' ) , ) )
2009-01-27 08:22:15 +01:00
def readShowdownActions ( self , hand ) :
2009-03-12 07:17:46 +01:00
for shows in self . re_ShowdownAction . finditer ( hand . handText ) :
2009-01-27 08:22:15 +01:00
cards = shows . group ( ' CARDS ' )
2009-03-12 07:17:46 +01:00
cards = cards . split ( ' , ' )
2009-01-27 08:22:15 +01:00
hand . addShownCards ( cards , shows . group ( ' PNAME ' ) )
def readCollectPot ( self , hand ) :
2009-03-12 07:17:46 +01:00
for m in self . re_CollectPot . finditer ( hand . handText ) :
2009-01-27 08:22:15 +01:00
hand . addCollectPot ( player = m . group ( ' PNAME ' ) , pot = m . group ( ' POT ' ) )
def readShownCards ( self , hand ) :
2009-03-12 07:17:46 +01:00
for m in self . re_ShownCards . finditer ( hand . handText ) :
2009-01-27 08:22:15 +01:00
if m . group ( ' CARDS ' ) is not None :
cards = m . group ( ' CARDS ' )
2009-03-12 07:17:46 +01:00
cards = cards . split ( ' , ' )
2009-01-27 08:22:15 +01:00
hand . addShownCards ( cards = None , player = m . group ( ' PNAME ' ) , holeandboard = cards )
if __name__ == " __main__ " :
2009-03-12 07:17:46 +01:00
parser = OptionParser ( )
2010-08-14 02:10:13 +02:00
parser . add_option ( " -i " , " --input " , dest = " ipath " , help = _ ( " parse input hand history " ) , default = " regression-test-files/betfair/befair.02.04.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 )
2009-03-12 07:17:46 +01:00
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 = Betfair ( in_path = options . ipath , out_path = options . opath , follow = options . follow )