Allow parsing of tournaments, draw and play money.

Sorry about the massive commit. There are still numerous bugs
parsing non-holdem hands and writehand() is broken for all
but holdem cash games.
This commit is contained in:
Ray 2009-07-11 13:44:32 -04:00
parent 4e952de825
commit d8820ae1f7
2 changed files with 108 additions and 49 deletions

View File

@ -45,6 +45,9 @@ class Hand:
self.maxseats = 10
self.counted_seats = 0
self.buttonpos = 0
self.tourNo = None
self.buyin = None
self.level = None
self.seating = []
self.players = []
self.posted = []
@ -56,17 +59,19 @@ class Hand:
self.actions = {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
self.board = {} # dict from street names to community cards
self.holecards = {}
self.discards = {}
for street in self.allStreets:
self.streets[street] = "" # portions of the handText, filled by markStreets()
self.actions[street] = []
for street in self.actionStreets:
self.bets[street] = {}
self.lastBet[street] = 0
self.actions[street] = []
self.board[street] = []
for street in self.holeStreets:
self.holecards[street] = {} # dict from player names to holecards
self.discards[street] = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
# Collections indexed by player names
# self.holecards = {} # dict from player names to dicts by street ... of tuples ... of holecards
self.discards = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
self.stacks = {}
self.collected = [] #list of ?
self.collectees = {} # dict from player names to amounts collected (?)
@ -93,6 +98,9 @@ class Hand:
("TABLE NAME", self.tablename),
("HERO", self.hero),
("MAXSEATS", self.maxseats),
("TOURNAMENT NO", self.tourNo),
("BUYIN", self.buyin),
("LEVEL", self.level),
("LASTBET", self.lastBet),
("ACTION STREETS", self.actionStreets),
("STREETS", self.streets),
@ -221,10 +229,10 @@ If a player has None chips he won't be added."""
self.players.append([seat, name, chips])
self.stacks[name] = Decimal(chips)
self.pot.addPlayer(name)
for street in self.allStreets:
for street in self.actionStreets:
self.bets[street][name] = []
#self.holecards[name] = {} # dict from street names.
self.discards[name] = {} # dict from street names.
#self.discards[name] = {} # dict from street names.
def addStreets(self, match):
@ -784,8 +792,10 @@ class DrawHand(Hand):
if gametype['base'] != 'draw':
pass # or indeed don't pass and complain instead
self.streetList = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.allStreets = ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.holeStreets = ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.actionStreets = ['PREDEAL', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
self.communityStreets = []
Hand.__init__(self, sitename, gametype, handText)
self.sb = gametype['sb']
self.bb = gametype['bb']
@ -849,18 +859,19 @@ player (string) name of player
self.checkPlayerExists(player)
# if shown and len(cardset) > 0:
# self.shown.add(player)
self.holecards[player][street] = (newcards,oldcards)
self.holecards[street][player] = (newcards,oldcards)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
def discardDrawHoleCards(self, cards, player, street):
logging.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards, player, street))
self.discards[player][street] = set([cards])
self.discards[street][player] = set([cards])
def addDiscard(self, street, player, num, cards):
self.checkPlayerExists(player)
print "street, player, num, cards =", street, player, num, cards
if cards:
act = (player, 'discards', num, cards)
self.discardDrawHoleCards(cards, player, street)
@ -869,12 +880,12 @@ player (string) name of player
self.actions[street].append(act)
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))
# def addShownCards(self, cards, player, holeandboard=None, shown=False, mucked=False):
# """\
#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)
@ -884,6 +895,33 @@ Card ranks will be uppercased
# self.addHoleCards(holeandboard.difference(board),player,shown=True)
def addHoleCards(self, cards, player, shown, mucked, dealt=False):
"""\
Assigns observed holecards to a player.
cards list of card bigrams e.g. ['2h','Jc']
player (string) name of player
shown whether they were revealed at showdown
mucked whether they were mucked at showdown
dealt whether they were seen in a 'dealt to' line
"""
# I think this only gets called for shown cards.
logging.debug("addHoleCards %s %s" % (cards, player))
try:
self.checkPlayerExists(player)
except FpdbParseError, e:
print "[ERROR] Tried to add holecards for unknown player: %s" % (player,)
return
if dealt:
self.dealt.add(player)
if shown:
self.shown.add(player)
if mucked:
self.mucked.add(player)
if player != self.hero: #skip hero, we know his cards
print "player, cards =", player, cards
self.holecards[self.holeStreets[-1]][player] = (cards, set([]))
def writeHand(self, fh=sys.__stdout__):
# PokerStars format.
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)))
@ -913,8 +951,8 @@ Card ranks will be uppercased
for act in self.actions['DRAWONE']:
print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWONE']
dc = self.discards[act[0]]['DRAWONE']
(nc,oc) = self.holecards['DRAWONE'][act[0]]
dc = self.discards['DRAWONE'][act[0]]
kc = oc - dc
print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc)))
@ -923,8 +961,8 @@ Card ranks will be uppercased
for act in self.actions['DRAWTWO']:
print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWTWO']
dc = self.discards[act[0]]['DRAWTWO']
(nc,oc) = self.holecards['DRAWTWO'][act[0]]
dc = self.discards['DRAWTWO'][act[0]]
kc = oc - dc
print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc)))
@ -933,8 +971,8 @@ Card ranks will be uppercased
for act in self.actions['DRAWTHREE']:
print >>fh, self.actionString(act)
if act[0] == self.hero and act[1] == 'discards':
(nc,oc) = self.holecards[act[0]]['DRAWTHREE']
dc = self.discards[act[0]]['DRAWTHREE']
(nc,oc) = self.holecards['DRAWTHREE'][act[0]]
dc = self.discards['DRAWTHREE'][act[0]]
kc = oc - dc
print >>fh, _("Dealt to %s [%s] [%s]" % (act[0], " ".join(kc), " ".join(nc)))

View File

@ -30,7 +30,7 @@ class PokerStars(HandHistoryConverter):
re_GameInfo = re.compile("""PokerStars\sGame\s\#(?P<HID>[0-9]+):\s+
(Tournament\s\#(?P<TOURNO>\d+),\s(?P<BUYIN>[\$\+\d\.]+)\s)?
(?P<MIXED>HORSE|8\-Game|HOSE)?\s?\(?
(?P<GAME>Hold\'em|Razz|7\sCard Stud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi)\s
(?P<GAME>Hold\'em|Razz|7\sCard Stud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball)\s
(?P<LIMIT>No\sLimit|Limit|Pot\sLimit),?\s
(-\sLevel\s(?P<LEVEL>[IVXLC]+)\s)?\(?
(?P<CURRENCY>\$|)?
@ -40,7 +40,11 @@ class PokerStars(HandHistoryConverter):
re.MULTILINE|re.VERBOSE)
re_SplitHands = re.compile('\n\n+')
re_TailSplitHands = re.compile('(\n\n\n+)')
re_HandInfo = re.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re.MULTILINE)
re_HandInfo = re.compile("""^Table\s\'(?P<TABLE>[-\ a-zA-Z\d]+)\'\s
((?P<MAX>\d+)-max\s)?
(?P<PLAY>\(Play\sMoney\)\s)?
(Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
re.MULTILINE|re.VERBOSE)
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>.+)\]")
@ -73,8 +77,11 @@ follow : whether to tail -f the input"""
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| discards| stands pat)( \$(?P<BET>[.\d]+))?( to \$(?P<BETTO>[.\d]+))?( (?P<NODISCARDED>\d) cards?( \[(?P<DISCARDED>.+?)\])?)?" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"^%s:(?P<ATYPE> bets| checks| raises| calls| folds| discards| stands pat)( \$?(?P<BET>[.\d]+))?( to \$?(?P<BETTO>[.\d]+))?( (?P<NODISCARDED>\d) cards?( \[(?P<DISCARDED>.+?)\])?)?" % player_re, re.MULTILINE)
# self.re_Action = re.compile(r"^%s:(?P<ATYPE> bets| checks| raises| calls| folds| discards| stands pat)( \$?(?P<BET>[.\d]+))?( to \$?(?P<BETTO>[.\d]+))?( (?P<NODISCARDED>\d) cards?( \[(?P<DISCARDED>.+?)\])?)?" % player_re, re.MULTILINE)
self.re_Action = re.compile(r"""^%s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
(\s\$?(?P<BET>[.\d]+))?(\sto\s\$?(?P<BETTO>[.\d]+))? # the number discarded goes in <BET>
(\scards?(\s\[(?P<DISCARDED>.+?)\])?)?"""
% player_re, re.MULTILINE|re.VERBOSE)
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)
@ -86,7 +93,8 @@ follow : whether to tail -f the input"""
["ring", "hold", "fl"],
["ring", "stud", "fl"],
#["ring", "draw", "fl"],
["ring", "draw", "fl"],
["tour", "hold", "nl"],
["tour", "hold", "pl"],
@ -96,6 +104,9 @@ follow : whether to tail -f the input"""
]
def determineGameType(self, handText):
# inspect the handText and return the gametype dict
# gametype dict is:
# {'limitType': xxx, 'base': xxx, 'category': xxx}
info = {}
m = self.re_GameInfo.search(handText)
@ -103,18 +114,18 @@ follow : whether to tail -f the input"""
return None
mg = m.groupdict()
# print "mg =", mg
# translations from captured groups to our info strings
# translations from captured groups to fpdb info strings
limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl' }
mixes = { 'HORSE': 'horse', '8-Game': '8game', 'HOSE': 'hose'}
games = { # base, category
"Hold'em" : ('hold','holdem'),
'Omaha' : ('hold','omahahi'),
'Omaha Hi/Lo' : ('hold','omahahilo'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi'),
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
'Badugi' : ('draw','badugi')
games = { # base, category
"Hold'em" : ('hold','holdem'),
'Omaha' : ('hold','omahahi'),
'Omaha Hi/Lo' : ('hold','omahahilo'),
'Razz' : ('stud','razz'),
'7 Card Stud' : ('stud','studhi'),
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
'Badugi' : ('draw','badugi'),
'Triple Draw 2-7 Lowball' : ('draw','27_3draw'),
}
currencies = { u'':'EUR', '$':'USD', '':'T$' }
# I don't think this is doing what we think. mg will always have all
@ -131,17 +142,13 @@ follow : whether to tail -f the input"""
info['bb'] = mg['BB']
if 'CURRENCY' in mg:
info['currency'] = currencies[mg['CURRENCY']]
if 'MIXED' in mg and mg['MIXED'] != None:
info['mixedType'] = mixes[mg['MIXED']]
info['tourNo'] = mg['TOURNO']
if info['tourNo'] == None:
if 'TOURNO' in mg and mg['TOURNO'] == None:
info['type'] = 'ring'
else:
info['type'] = 'tour'
info['buyin'] = mg['BUYIN']
info['level'] = mg['LEVEL']
# NB: SB, BB must be interpreted as blinds or bets depending on limit type.
return info
@ -150,14 +157,13 @@ follow : whether to tail -f the input"""
m = self.re_HandInfo.search(hand.handText,re.DOTALL)
if m:
info.update(m.groupdict())
# TODO: Be less lazy and parse maxseats from the HandInfo regex
if m.group('TABLEATTRIBUTES'):
m2 = re.search("\s*(\d+)-max", m.group('TABLEATTRIBUTES'))
hand.maxseats = int(m2.group(1))
# hand.maxseats = int(m2.group(1))
else:
pass # throw an exception here, eh?
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())
# 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:
@ -174,7 +180,21 @@ follow : whether to tail -f the input"""
hand.tablename = info[key]
if key == 'BUTTON':
hand.buttonpos = info[key]
if key == 'MAX':
hand.maxseats = info[key]
if key == 'MIXED':
hand.mixed = info[key]
if key == 'TOURNO':
hand.tourNo = info[key]
if key == 'BUYIN':
hand.buyin = info[key]
if key == 'LEVEL':
hand.level = info[key]
if key == 'PLAY' and info['PLAY'] != None:
hand.currency = 'play' # overrides previously set value
def readButton(self, hand):
m = self.re_Button.search(hand.handText)
if m:
@ -314,6 +334,7 @@ follow : whether to tail -f the input"""
def readAction(self, hand, street):
m = self.re_Action.finditer(hand.streets[street])
for action in m:
acts = action.groupdict()
if action.group('ATYPE') == ' raises':
hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
elif action.group('ATYPE') == ' calls':
@ -325,7 +346,7 @@ follow : whether to tail -f the input"""
elif action.group('ATYPE') == ' checks':
hand.addCheck( street, action.group('PNAME'))
elif action.group('ATYPE') == ' discards':
hand.addDiscard(street, action.group('PNAME'), action.group('NODISCARDED'), action.group('DISCARDED'))
hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('DISCARDED'))
elif action.group('ATYPE') == ' stands pat':
hand.addStandsPat( street, action.group('PNAME'))
else: