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:
parent
4e952de825
commit
d8820ae1f7
|
@ -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)))
|
||||
|
||||
|
|
|
@ -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,8 +114,7 @@ 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
|
||||
|
@ -114,7 +124,8 @@ follow : whether to tail -f the input"""
|
|||
'Razz' : ('stud','razz'),
|
||||
'7 Card Stud' : ('stud','studhi'),
|
||||
'7 Card Stud Hi/Lo' : ('stud','studhilo'),
|
||||
'Badugi' : ('draw','badugi')
|
||||
'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.
|
||||
|
||||
# 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,6 +180,20 @@ 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)
|
||||
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue
Block a user