2008-11-07 09:47:00 +01:00
#!/usr/bin/python
#Copyright 2008 Carl Gherardi
#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.
2008-11-09 01:46:14 +01:00
import Configuration
2008-11-09 12:57:58 +01:00
import FpdbRegex
import re
2008-11-09 01:46:14 +01:00
import sys
import traceback
2008-11-09 03:58:46 +01:00
import os
import os . path
2008-11-09 01:46:14 +01:00
import xml . dom . minidom
2008-12-05 03:40:04 +01:00
from decimal import Decimal
import operator
2008-11-09 01:46:14 +01:00
from xml . dom . minidom import Node
2008-11-07 09:47:00 +01:00
class HandHistoryConverter :
2008-11-09 01:46:14 +01:00
def __init__ ( self , config , file , sitename ) :
2008-11-09 00:49:05 +01:00
print " HandHistory init called "
self . c = config
2008-11-09 01:46:14 +01:00
self . sitename = sitename
2008-11-09 00:49:05 +01:00
self . obs = " " # One big string
self . filetype = " text "
self . doc = None # For XML based HH files
self . file = file
self . hhbase = self . c . get_import_parameters ( ) . get ( " hhArchiveBase " )
2008-11-09 03:58:46 +01:00
self . hhbase = os . path . expanduser ( self . hhbase )
self . hhdir = os . path . join ( self . hhbase , sitename )
2008-11-09 06:33:42 +01:00
self . gametype = [ ]
2008-11-09 04:29:58 +01:00
# self.ofile = os.path.join(self.hhdir,file)
2008-11-09 12:57:58 +01:00
self . rexx = FpdbRegex . FpdbRegex ( )
2008-11-09 00:49:05 +01:00
def __str__ ( self ) :
tmp = " HandHistoryConverter: ' %s ' \n " % ( self . sitename )
2008-11-09 01:46:14 +01:00
tmp = tmp + " \t hhbase: ' %s ' \n " % ( self . hhbase )
tmp = tmp + " \t hhdir: ' %s ' \n " % ( self . hhdir )
tmp = tmp + " \t filetype: ' %s ' \n " % ( self . filetype )
tmp = tmp + " \t infile: ' %s ' \n " % ( self . file )
2008-11-09 04:29:58 +01:00
# tmp = tmp + "\toutfile: '%s'\n" % (self.ofile)
2008-11-27 13:29:19 +01:00
# tmp = tmp + "\tgametype: '%s'\n" % (self.gametype[0])
# tmp = tmp + "\tgamebase: '%s'\n" % (self.gametype[1])
# tmp = tmp + "\tlimit: '%s'\n" % (self.gametype[2])
# tmp = tmp + "\tsb/bb: '%s/%s'\n" % (self.gametype[3], self.gametype[4])
2008-11-09 00:49:05 +01:00
return tmp
2008-11-10 07:41:04 +01:00
def processFile ( self ) :
if not self . sanityCheck ( ) :
print " Cowardly refusing to continue after failed sanity check "
return
self . readFile ( self . file )
self . gametype = self . determineGameType ( )
self . hands = self . splitFileIntoHands ( )
2008-11-10 14:02:56 +01:00
for hand in self . hands :
self . readHandInfo ( hand )
2008-11-10 15:19:45 +01:00
self . readPlayerStacks ( hand )
2008-11-16 05:44:16 +01:00
self . markStreets ( hand )
2008-11-11 09:54:24 +01:00
self . readBlinds ( hand )
2008-11-12 05:12:18 +01:00
self . readHeroCards ( hand )
2008-11-27 12:54:42 +01:00
# Read action (Note: no guarantee this is in hand order.
for street in hand . streets . groupdict ( ) :
self . readAction ( hand , street )
2008-11-12 05:12:18 +01:00
if ( hand . involved == True ) :
2008-12-05 03:40:04 +01:00
#self.writeHand("output file", hand)
hand . printHand ( )
2008-11-12 05:12:18 +01:00
else :
pass #Don't write out observed hands
2008-11-10 07:41:04 +01:00
2008-11-07 11:19:18 +01:00
# Functions to be implemented in the inheriting class
2008-11-07 09:47:00 +01:00
def readSupportedGames ( self ) : abstract
2008-11-09 06:33:42 +01:00
# should return a list
# type base limit
# [ ring, hold, nl , sb, bb ]
# Valid types specified in docs/tabledesign.html in Gametypes
2008-11-07 09:47:00 +01:00
def determineGameType ( self ) : abstract
2008-11-11 09:54:24 +01:00
#TODO: Comment
2008-11-10 14:02:56 +01:00
def readHandInfo ( self , hand ) : abstract
2008-11-11 09:54:24 +01:00
# Needs to return a list of lists in the format
# [['seat#', 'player1name', 'stacksize'] ['seat#', 'player2name', 'stacksize'] [...]]
2008-11-10 15:19:45 +01:00
def readPlayerStacks ( self , hand ) : abstract
2008-11-11 09:54:24 +01:00
2008-11-27 12:54:42 +01:00
# Needs to return a MatchObject with group names identifying the streets into the Hand object
def markStreets ( self , hand ) : abstract
2008-11-16 05:44:16 +01:00
2008-11-11 09:54:24 +01:00
#Needs to return a list in the format
# ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
# addtional players are assumed to post a bb oop
def readBlinds ( self , hand ) : abstract
2008-11-12 05:12:18 +01:00
def readHeroCards ( self , hand ) : abstract
2008-11-16 05:44:16 +01:00
def readAction ( self , hand , street ) : abstract
2008-11-07 09:47:00 +01:00
2008-11-09 03:58:46 +01:00
def sanityCheck ( self ) :
2008-12-04 00:57:20 +01:00
sane = True
2008-11-09 03:58:46 +01:00
base_w = False
#Check if hhbase exists and is writable
#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 ) ) :
print " HH Sanity Check: Directory hhbase ' " + self . hhbase + " ' doesn ' t exist or is not writable "
else :
#Check if hhdir exists and is writable
if not os . path . isdir ( self . hhdir ) :
# In first pass, dir may not exist. Attempt to create dir
print " Creating directory: ' %s ' " % ( self . hhdir )
os . mkdir ( self . hhdir )
sane = True
elif os . access ( self . hhdir , os . W_OK ) :
sane = True
else :
print " HH Sanity Check: Directory hhdir ' " + self . hhdir + " ' or its parent directory are not writable "
return sane
2008-11-09 00:49:05 +01:00
2008-11-07 11:19:18 +01:00
# Functions not necessary to implement in sub class
2008-11-09 00:49:05 +01:00
def setFileType ( self , filetype = " text " ) :
self . filetype = filetype
2008-11-09 12:57:58 +01:00
def splitFileIntoHands ( self ) :
hands = [ ]
list = self . rexx . split_hand_re . split ( self . obs )
2008-11-10 14:02:56 +01:00
list . pop ( ) #Last entry is empty
2008-11-09 12:57:58 +01:00
for l in list :
2008-11-10 14:02:56 +01:00
# print "'" + l + "'"
2008-11-10 07:41:04 +01:00
hands = hands + [ Hand ( self . sitename , self . gametype , l ) ]
return hands
2008-11-09 00:49:05 +01:00
2008-11-07 09:47:00 +01:00
def readFile ( self , filename ) :
""" Read file """
2008-11-09 01:46:14 +01:00
print " Reading file: ' %s ' " % ( filename )
2008-11-09 00:49:05 +01:00
if ( self . filetype == " text " ) :
infile = open ( filename , " rU " )
2008-11-09 01:46:14 +01:00
self . obs = infile . read ( )
infile . close ( )
2008-11-09 00:49:05 +01:00
elif ( self . filetype == " xml " ) :
try :
doc = xml . dom . minidom . parse ( filename )
self . doc = doc
except :
traceback . print_exc ( file = sys . stderr )
2008-11-07 09:47:00 +01:00
2008-11-09 12:57:58 +01:00
def writeHand ( self , file , hand ) :
2008-11-07 09:47:00 +01:00
""" Write out parsed data """
2008-11-27 13:29:19 +01:00
print " DEBUG: ************************* "
print " DEBUG: Start of print hand "
print " DEBUG: ************************* "
2008-11-10 23:22:33 +01:00
print " %s Game # %s : %s ($ %s /$ %s ) - %s " % ( hand . sitename , hand . handid , " XXXXhand.gametype " , hand . sb , hand . bb , hand . starttime )
2008-11-10 14:29:49 +01:00
print " Table ' %s ' %d -max Seat # %s is the button " % ( hand . tablename , hand . maxseats , hand . buttonpos )
2008-11-10 15:19:45 +01:00
for player in hand . players :
print " Seat %s : %s ($ %s ) " % ( player [ 0 ] , player [ 1 ] , player [ 2 ] )
2008-11-11 09:54:24 +01:00
if ( hand . posted [ 0 ] == " FpdbNBP " ) :
print " No small blind posted "
else :
print " %s : posts small blind $ %s " % ( hand . posted [ 0 ] , hand . sb )
#May be more than 1 bb posting
print " %s : posts big blind $ %s " % ( hand . posted [ 1 ] , hand . bb )
if ( len ( hand . posted ) > 2 ) :
# Need to loop on all remaining big blinds - lazy
print " XXXXXXXXX FIXME XXXXXXXX "
2008-11-10 14:02:56 +01:00
print " *** HOLE CARDS *** "
2008-12-04 00:57:20 +01:00
print " Dealt to %s [ %s %s ] " % ( hand . hero , hand . holecards [ 0 ] , hand . holecards [ 1 ] )
2008-11-09 00:49:05 +01:00
#
## ACTION STUFF
2008-11-27 13:29:19 +01:00
# This is no limit only at the moment
for act in hand . actions [ ' PREFLOP ' ] :
self . printActionLine ( act , 0 )
2008-12-05 03:40:04 +01:00
if ' PREFLOP ' in hand . actions :
for act in hand . actions [ ' PREFLOP ' ] :
print " PF action "
2008-11-27 13:29:19 +01:00
if ' FLOP ' in hand . actions :
2008-12-04 00:57:20 +01:00
print " *** FLOP *** [ %s %s %s ] " % ( hand . streets . group ( " FLOP1 " ) , hand . streets . group ( " FLOP2 " ) , hand . streets . group ( " FLOP3 " ) )
2008-11-27 13:29:19 +01:00
for act in hand . actions [ ' FLOP ' ] :
self . printActionLine ( act , 0 )
if ' TURN ' in hand . actions :
2008-12-04 00:57:20 +01:00
print " *** TURN *** [ %s %s %s ] [ %s ] " % ( hand . streets . group ( " FLOP1 " ) , hand . streets . group ( " FLOP2 " ) , hand . streets . group ( " FLOP3 " ) , hand . streets . group ( " TURN1 " ) )
2008-11-27 13:29:19 +01:00
for act in hand . actions [ ' TURN ' ] :
self . printActionLine ( act , 0 )
if ' RIVER ' in hand . actions :
2008-12-04 00:57:20 +01:00
print " *** RIVER *** [ %s %s %s %s ] [ %s ] " % ( hand . streets . group ( " FLOP1 " ) , hand . streets . group ( " FLOP2 " ) , hand . streets . group ( " FLOP3 " ) , hand . streets . group ( " TURN1 " ) , hand . streets . group ( " RIVER1 " ) )
2008-11-27 13:29:19 +01:00
for act in hand . actions [ ' RIVER ' ] :
self . printActionLine ( act , 0 )
2008-11-10 14:02:56 +01:00
print " *** SUMMARY *** "
2008-11-27 13:29:19 +01:00
print " XXXXXXXXXXXX Need sumary info XXXXXXXXXXX "
2008-11-16 05:44:16 +01:00
# print "Total pot $%s | Rake $%s)" %(hand.totalpot $" + hand.rake)
2008-11-09 00:49:05 +01:00
# print "Board [" + boardcards + "]"
#
2008-11-27 13:29:19 +01:00
# SUMMARY STUFF
def printActionLine ( self , act , pot ) :
if act [ 1 ] == ' folds ' or act [ 1 ] == ' checks ' :
2008-12-05 03:40:04 +01:00
print " %s : %s " % ( act [ 0 ] , act [ 1 ] )
2008-11-27 13:29:19 +01:00
if act [ 1 ] == ' calls ' :
print " %s : %s $ %s " % ( act [ 0 ] , act [ 1 ] , act [ 2 ] )
if act [ 1 ] == ' raises ' :
print " %s : %s $ %s to XXXpottotalXXX " % ( act [ 0 ] , act [ 1 ] , act [ 2 ] )
2008-12-05 03:40:04 +01:00
2008-11-07 11:19:18 +01:00
2008-11-09 07:27:27 +01:00
#takes a poker float (including , for thousand seperator and converts it to an int
def float2int ( self , string ) :
pos = string . find ( " , " )
if ( pos != - 1 ) : #remove , the thousand seperator
string = string [ 0 : pos ] + string [ pos + 1 : ]
pos = string . find ( " . " )
if ( pos != - 1 ) : #remove decimal point
string = string [ 0 : pos ] + string [ pos + 1 : ]
result = int ( string )
if pos == - 1 : #no decimal point - was in full dollars - need to multiply with 100
result * = 100
return result
#end def float2int
2008-11-09 12:57:58 +01:00
class Hand :
# def __init__(self, sitename, gametype, sb, bb, string):
2008-12-04 00:57:20 +01:00
2008-12-05 03:40:04 +01:00
UPS = { ' a ' : ' A ' , ' t ' : ' T ' , ' j ' : ' J ' , ' q ' : ' Q ' , ' k ' : ' K ' }
STREETS = [ ' BLINDS ' , ' PREFLOP ' , ' FLOP ' , ' TURN ' , ' RIVER ' ]
2008-12-04 00:57:20 +01:00
def __init__ ( self , sitename , gametype , string ) :
self . sitename = sitename
self . gametype = gametype
self . string = string
self . streets = None # A MatchObject using a groupnames to identify streets.
self . actions = { }
self . handid = 0
self . sb = gametype [ 3 ]
self . bb = gametype [ 4 ]
self . tablename = " Slartibartfast "
self . maxseats = 10
self . counted_seats = 0
self . buttonpos = 0
self . seating = [ ]
self . players = [ ]
self . posted = [ ]
self . involved = True
self . hero = " Hiro "
self . holecards = " Xx Xx "
self . action = [ ]
2008-12-05 03:40:04 +01:00
2008-12-04 00:57:20 +01:00
self . rake = 0
2008-12-05 03:40:04 +01:00
self . bets = { }
self . lastBet = { }
self . orderedBets = { }
for street in self . STREETS :
self . bets [ street ] = { }
self . lastBet [ street ] = 0
def addPlayer ( self , seat , name , chips ) :
""" seat, an int indicating the seat
name , the player name
chips , the chips the player has at the start of the hand """
#self.players.append(name)
self . players . append ( [ seat , name , chips ] )
#self.startChips[name] = chips
#self.endChips[name] = chips
#self.winners[name] = 0
for street in self . STREETS :
self . bets [ street ] [ name ] = [ 0 ]
2008-12-04 00:57:20 +01:00
def addHoleCards ( self , h1 , h2 , seat = None ) : # generalise to add hole cards for a specific seat or player
self . holecards = [ self . card ( h1 ) , self . card ( h2 ) ]
def card ( self , c ) :
""" upper case the ranks but not suits, ' atjqk ' => ' ATJQK ' """
# don't know how to make this 'static'
2008-12-05 03:40:04 +01:00
for k , v in self . UPS . items ( ) :
2008-12-04 00:57:20 +01:00
c = c . replace ( k , v )
return c
2008-12-05 03:40:04 +01:00
def addBlind ( self , player , amount ) :
#self.bets['BLINDS'][player].append(Decimal(amount))
self . lastBet [ ' PREFLOP ' ] = Decimal ( amount )
self . posted + = [ player ]
def addCall ( self , street , player = None , amount = 0 ) :
self . bets [ street ] [ player ] . append ( Decimal ( amount ) )
#self.lastBet[street] = Decimal(amount)
self . actions [ street ] + = [ [ player , ' calls ' , amount ] ]
def addRaiseTo ( self , street , player , amountTo ) :
# amount is the amount raised to, not the amount raised.by
committedThisStreet = reduce ( operator . add , self . bets [ street ] [ player ] , 0 )
amountToCall = self . lastBet [ street ] - committedThisStreet
self . lastBet [ street ] = Decimal ( amountTo )
amountBy = Decimal ( amountTo ) - amountToCall
self . bets [ street ] [ player ] . append ( amountBy )
self . actions [ street ] + = [ [ player , ' raises ' , amountBy , amountTo ] ]
#def addRaiseTo(self, street, player=None, amountTo=None):
#self.amounts[street] += Decimal(amountTo)
def addBet ( self , street , player = None , amount = 0 ) :
self . bets [ street ] [ name ] . append ( Decimal ( amount ) )
self . orderedBets [ street ] . append ( Decimal ( amount ) )
self . actions [ street ] + = [ [ player , ' bets ' , amount ] ]
2008-12-04 00:57:20 +01:00
def printHand ( self ) :
2008-12-05 03:40:04 +01:00
# PokerStars format.
print " ### DEBUG ### "
print " %s Game # %s : %s ($ %s /$ %s ) - %s " % ( self . sitename , self . handid , " XXXXhand.gametype " , self . sb , self . bb , self . starttime )
print " Table ' %s ' %d -max Seat # %s is the button " % ( self . tablename , self . maxseats , self . buttonpos )
for player in self . players :
print " Seat %s : %s ($ %s ) " % ( player [ 0 ] , player [ 1 ] , player [ 2 ] )
if ( self . posted [ 0 ] is None ) :
print " No small blind posted "
else :
print " %s : posts small blind $ %s " % ( self . posted [ 0 ] , self . sb )
#May be more than 1 bb posting
for a in self . posted [ 1 : ] :
print " %s : posts big blind $ %s " % ( self . posted [ 1 ] , self . bb )
# What about big & small blinds?
print " *** HOLE CARDS *** "
print " Dealt to %s [ %s %s ] " % ( self . hero , self . holecards [ 0 ] , self . holecards [ 1 ] )
if ' PREFLOP ' in self . actions :
for act in self . actions [ ' PREFLOP ' ] :
self . printActionLine ( act )
if ' FLOP ' in self . actions :
print " *** FLOP *** [ %s %s %s ] " % ( self . streets . group ( " FLOP1 " ) , self . streets . group ( " FLOP2 " ) , self . streets . group ( " FLOP3 " ) )
for act in self . actions [ ' FLOP ' ] :
self . printActionLine ( act )
if ' TURN ' in self . actions :
print " *** TURN *** [ %s %s %s ] [ %s ] " % ( self . streets . group ( " FLOP1 " ) , self . streets . group ( " FLOP2 " ) , self . streets . group ( " FLOP3 " ) , self . streets . group ( " TURN1 " ) )
for act in self . actions [ ' TURN ' ] :
self . printActionLine ( act )
if ' RIVER ' in self . actions :
print " *** RIVER *** [ %s %s %s %s ] [ %s ] " % ( self . streets . group ( " FLOP1 " ) , self . streets . group ( " FLOP2 " ) , self . streets . group ( " FLOP3 " ) , self . streets . group ( " TURN1 " ) , self . streets . group ( " RIVER1 " ) )
for act in self . actions [ ' RIVER ' ] :
self . printActionLine ( act )
print " *** SUMMARY *** "
print " XXXXXXXXXXXX Need sumary info XXXXXXXXXXX "
# print "Total pot $%s | Rake $%s)" %(hand.totalpot $" + hand.rake)
# print "Board [" + boardcards + "]"
#
# SUMMARY STUFF
#print self.sitename
#print self.gametype
#print self.string
#print self.handid
#print self.sb
#print self.bb
#print self.tablename
#print self.maxseats
#print self.counted_seats
#print self.buttonpos
#print self.seating
#print self.players
#print self.posted
#print self.action
#print self.involved
#print self.hero
def printActionLine ( self , act ) :
if act [ 1 ] == ' folds ' or act [ 1 ] == ' checks ' :
print " %s : %s " % ( act [ 0 ] , act [ 1 ] )
if act [ 1 ] == ' calls ' :
print " %s : %s $ %s " % ( act [ 0 ] , act [ 1 ] , act [ 2 ] )
if act [ 1 ] == ' raises ' :
print " %s : %s $ %s to $ %s " % ( act [ 0 ] , act [ 1 ] , act [ 2 ] , act [ 3 ] )