Coverage for BovadaToFpdb.py: 0%
456 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright 2008-2012, Chaz Littlejohn
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19########################################################################
21#import L10n
22#_ = L10n.get_translation()
24# TODO: straighten out discards for draw games
26import sys, copy
27from HandHistoryConverter import *
28import Card
29from decimal_wrapper import Decimal
31# Bovada HH Format
33class Bovada(HandHistoryConverter):
35 # Class Variables
37 sitename = "Bovada"
38 filetype = "text"
39 codepage = ("utf8", "cp1252")
40 siteId = 21 # Needs to match id entry in Sites database
41 summaryInFile = True
42 copyGameHeader = True
43 sym = {'USD': "\$", 'T$': "", "play": ""} # ADD Euro, Sterling, etc HERE
44 substitutions = {
45 'LEGAL_ISO' : "USD", # legal ISO currency codes
46 'LS' : u"\$|", # legal currency symbols - Euro(cp1252, utf-8)
47 'PLYR': r'(?P<PNAME>.+?)',
48 'CUR': u"(\$|)",
49 'NUM' :u".,\d",
50 }
52 # translations from captured groups to fpdb info strings
53 Lim_Blinds = { '0.04': ('0.01', '0.02'),
54 '0.08': ('0.02', '0.04'), '0.10': ('0.02', '0.05'),
55 '0.20': ('0.05', '0.10'), '0.25': ('0.05', '0.10'),
56 '0.40': ('0.10', '0.20'), '0.50': ('0.10', '0.25'),
57 '1.00': ('0.25', '0.50'), '1': ('0.25', '0.50'),
58 '2.00': ('0.50', '1.00'), '2': ('0.50', '1.00'),
59 '4.00': ('1.00', '2.00'), '4': ('1.00', '2.00'),
60 '6.00': ('1.50', '3.00'), '6': ('1.50', '3.00'),
61 '8.00': ('2.00', '4.00'), '8': ('2.00', '4.00'),
62 '10.00': ('2.50', '5.00'), '10': ('2.50', '5.00'),
63 '16.00': ('4.00', '8.00'), '16': ('4.00', '8.00'),
64 '20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'),
65 '30.00': ('7.50', '15.00'), '30': ('7.50', '15.00'),
66 '40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'),
67 '60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'),
68 '80.00': ('20.00', '40.00'), '80': ('20.00', '40.00'),
69 '100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'),
70 }
72 limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Fixed Limit':'fl', 'Turbo': 'nl'}
73 games = { # base, category
74 "HOLDEM" : ('hold','holdem'),
75 'OMAHA' : ('hold','omahahi'),
76 'OMAHA HiLo' : ('hold','omahahilo'),
77 'OMAHA_HL' : ('hold','omahahilo'),
78 '7CARD' : ('stud','studhi'),
79 '7CARD HiLo' : ('stud','studhilo'),
80 '7CARD_HL' : ('hold','studhilo'),
81 'HOLDEMZonePoker' : ('hold','holdem'),
82 'OMAHAZonePoker' : ('hold','omahahi'),
83 'OMAHA HiLoZonePoker' : ('hold','omahahilo'),
85 }
86 currencies = {'$':'USD', '':'T$'}
88 # Static regexes
89 re_GameInfo = re.compile(u"""
90 (Ignition|Bovada|Bodog(\.com|\.eu|\sUK|\sCanada|88)?)\sHand\s\#C?(?P<HID>[0-9]+):?\s+
91 ((?P<ZONE>Zone\sPoker\sID|TBL)\#(?P<TABLE>.+?)\s)?
92 (?P<GAME>HOLDEM|OMAHA|OMAHA_HL|7CARD|7CARD\sHiLo|OMAHA\sHiLo|7CARD_HL|HOLDEMZonePoker|OMAHAZonePoker|OMAHA\sHiLoZonePoker)\s+
93 (Tournament\s\#(M\-)? # open paren of tournament info Tournament #2194767 TBL#1,
94 (?P<TOURNO>\d+)\sTBL\#(?P<TABLENO>\d+),
95 \s)?
96 (?P<HU>1\son\s1\s)?
97 (?P<LIMIT>No\sLimit|Fixed\sLimit|Pot\sLimit|Turbo)?
98 (\s?Normal\s?)?
99 (-\sLevel\s\d+?\s
100 \(? # open paren of the stakes
101 (?P<CURRENCY>%(LS)s|)?
102 (?P<SB>[,.0-9]+)/(%(LS)s)?
103 (?P<BB>[,.0-9]+)
104 \s?(?P<ISO>%(LEGAL_ISO)s)?
105 \))?
106 (\s\[(?P<VERSION>MVS)\])?
107 \s-\s
108 (?P<DATETIME>.*$)
109 """ % substitutions, re.MULTILINE|re.VERBOSE)
111 re_PlayerInfo = re.compile(u"""
112 ^Seat\s(?P<SEAT>[0-9]+):\s
113 %(PLYR)s\s(?P<HERO>\[ME\]\s)?
114 \((%(LS)s)?(?P<CASH>[%(NUM)s]+)\sin\schips\)""" % substitutions,
115 re.MULTILINE|re.VERBOSE)
117 re_PlayerInfoStud = re.compile(u"""
118 ^(?P<PNAME>Seat\+(?P<SEAT>[0-9]+))
119 (?P<HERO>\s\[ME\])?:\s
120 (%(LS)s)?(?P<CASH>[%(NUM)s]+)\sin\schips""" % substitutions,
121 re.MULTILINE|re.VERBOSE)
123 re_PlayerSeat = re.compile(u"^Seat\+(?P<SEAT>[0-9]+)", re.MULTILINE|re.VERBOSE)
124 re_Identify = re.compile(u'(Ignition|Bovada|Bodog(\.com|\.eu|\sUK|\sCanada|88)?)\sHand')
125 re_SplitHands = re.compile('\n\n+')
126 re_TailSplitHands = re.compile('(\n\n\n+)')
127 re_Button = re.compile('Dealer : Set dealer\/Bring in spot \[(?P<BUTTON>\d+)\]', re.MULTILINE)
128 re_Board1 = re.compile(r"Board \[(?P<FLOP>\S\S\S? \S\S\S? \S\S\S?)?\s+?(?P<TURN>\S\S\S?)?\s+?(?P<RIVER>\S\S\S?)?\]")
129 re_Board2 = {
130 "FLOP": re.compile(r"\*\*\* FLOP \*\*\* \[(?P<CARDS>\S\S \S\S \S\S)\]"),
131 "TURN": re.compile(r"\*\*\* TURN \*\*\* \[\S\S \S\S \S\S\] \[(?P<CARDS>\S\S)\]"),
132 "RIVER": re.compile(r"\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S\] \[(?P<CARDS>\S\S)\]")
133 }
134 re_DateTime = re.compile("""(?P<Y>[0-9]{4})\-(?P<M>[0-9]{2})\-(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)""", re.MULTILINE)
135 # These used to be compiled per player, but regression tests say
136 # we don't have to, and it makes life faster.
137 re_PostSB = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: (Ante\/Small (B|b)lind|Posts chip|Small (B|b)lind) (?P<CURRENCY>%(CUR)s)(?P<SB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
138 re_PostBB = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: (Big (B|b)lind\/Bring in|Big (B|b)lind) (?P<CURRENCY>%(CUR)s)(?P<BB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
139 re_Antes = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: Ante chip %(CUR)s(?P<ANTE>[%(NUM)s]+)" % substitutions, re.MULTILINE)
140 re_BringIn = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: (Bring_in chip|Big (B|b)lind\/Bring in|Bring in)\s?(\(timeout\) )?%(CUR)s(?P<BRINGIN>[%(NUM)s]+)" % substitutions, re.MULTILINE)
141 re_PostBoth = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: Posts dead chip %(CUR)s(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
142 re_HeroCards = re.compile(r"^%(PLYR)s ?\[ME\] : Card dealt to a spot \[(?P<NEWCARDS>.+?)\]" % substitutions, re.MULTILINE)
143 re_Action = re.compile(r"""(?P<ACTION>
144 ^%(PLYR)s\s(\s?\[ME\]\s)?:(\sD)?(?P<ATYPE>\s(B|b)ets|\sDouble\sbets|\sChecks|\s(R|r)aises|\sCalls?|\sFold|\sBring_in\schip|\sBig\sblind\/Bring\sin|\sAll\-in(\((raise|raise\-timeout)\))?|\sCard\sdealt\sto\sa\sspot)
145 (\schip\sinfo)?(\(timeout\))?(\s%(CUR)s(?P<BET>[%(NUM)s]+)(\sto\s%(CUR)s(?P<BETTO>[%(NUM)s]+))?|\s\[(?P<NEWCARDS>.+?)\])?)"""
146 % substitutions, re.MULTILINE|re.VERBOSE)
147 re_ShowdownAction = re.compile(r"^%(PLYR)s (?P<HERO>\s?\[ME\]\s)?: Card dealt to a spot \[(?P<CARDS>.*)\]" % substitutions, re.MULTILINE)
148 #re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and won \([.\d]+\) with (?P<STRING>.*))?" % substitutions, re.MULTILINE)
149 re_CollectPot1 = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: Hand (R|r)esult(\-Side (P|p)ot)? %(CUR)s(?P<POT1>[%(NUM)s]+)" % substitutions, re.MULTILINE)
150 re_Bounty = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: BOUNTY PRIZE \[%(CUR)s(?P<BOUNTY>[%(NUM)s]+)\]" % substitutions, re.MULTILINE)
151 re_Dealt = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: Card dealt to a spot" % substitutions, re.MULTILINE)
152 re_Buyin = re.compile(r"(\s-\s\d+\s-\s(?P<TOURNAME>.+?))?\s-\s(?P<BUYIN>(?P<TICKET>TT)?(?P<BIAMT>[%(LS)s\d\.,]+)-(?P<BIRAKE>[%(LS)s\d\.,]+)?)\s-\s" % substitutions)
153 re_Knockout = re.compile(r"\s\((?P<BOUNTY>[%(LS)s\d\.,]+)\sKnockout" % substitutions)
154 re_Stakes = re.compile(r"(RING|ZONE)\s-\s(?P<CURRENCY>%(LS)s|)?(?P<SB>[%(NUM)s]+)-(%(LS)s)?(?P<BB>[%(NUM)s]+)" % substitutions)
155 re_Summary = re.compile(r"\*\*\*\sSUMMARY\s\*\*\*")
156 re_Hole_Third = re.compile(r"\*\*\*\s(3RD\sSTREET|HOLE\sCARDS)\s\*\*\*")
157 re_ReturnBet = re.compile(r"Return\suncalled\sportion", re.MULTILINE)
158 #Small Blind : Hand result $19
160 def compilePlayerRegexs(self, hand):
161 subst = self.substitutions
162 players = set(self.playersMap.keys())
163 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
164 self.compiledPlayers = players
165 subst['PLYR'] = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
166 self.re_CollectPot2 = re.compile(u"""
167 Seat[\+ ](?P<SEAT>[0-9]+):\s?%(PLYR)s
168 (\sHI)?\s(%(LS)s)?(?P<POT1>[%(NUM)s]+)?
169 (?P<STRING>[a-zA-Z ]+)
170 (?P<CARDS1>\[[-a-zA-Z0-9 ]+\])
171 (\sLOW?\s(%(LS)s)?(?P<POT2>[%(NUM)s]+))?
172 """ % subst, re.MULTILINE|re.VERBOSE)
174 def readSupportedGames(self):
175 return [["ring", "hold", "nl"],
176 ["ring", "hold", "pl"],
177 ["ring", "hold", "fl"],
179 ["ring", "stud", "fl"],
181 ["tour", "hold", "nl"],
182 ["tour", "hold", "pl"],
183 ["tour", "hold", "fl"],
185 ["tour", "stud", "fl"],
186 ]
188 def parseHeader(self, handText, whole_file):
189 gametype = self.determineGameType(handText)
190 if gametype['type'] == 'tour':
191 handlist = re.split(self.re_SplitHands, whole_file)
192 result = re.findall(self.re_PlayerSeat, handlist[0])
193 #gametype['maxSeats'] = len(result)
194 return gametype
196 def determineGameType(self, handText):
197 info = {}
198 m = self.re_GameInfo.search(handText)
199 if not m:
200 tmp = handText[0:200]
201 log.error(("BovadaToFpdb.determineGameType: '%s'") % tmp)
202 raise FpdbParseError
204 m1 = self.re_Dealt.search(handText)
205 m2 = self.re_Summary.split(handText)
206 m3 = self.re_Hole_Third.split(handText)
207 if not m1 or len(m2)!=2 or len(m3)>3:
208 raise FpdbHandPartial("BovadaToFpdb.determineGameType: " + ("Partial hand history"))
210 mg = m.groupdict()
211 m = self.re_Stakes.search(self.in_path)
212 if m: mg.update(m.groupdict())
214 if 'LIMIT' in mg:
215 if not mg['LIMIT']:
216 info['limitType'] = 'nl'
217 else:
218 info['limitType'] = self.limits[mg['LIMIT']]
219 if 'GAME' in mg:
220 (info['base'], info['category']) = self.games[mg['GAME']]
222 if 'SB' in mg:
223 info['sb'] = self.clearMoneyString(mg['SB'])
224 if 'BB' in mg:
225 info['bb'] = self.clearMoneyString(mg['BB'])
227 if 'TOURNO' in mg and mg['TOURNO'] is not None:
228 info['type'] = 'tour'
229 info['currency'] = 'T$'
230 else:
231 info['type'] = 'ring'
232 info['currency'] = 'USD'
234 if 'CURRENCY' in mg and mg['CURRENCY'] is not None:
235 info['currency'] = self.currencies[mg['CURRENCY']]
237 if 'Zone' in mg['GAME']:
238 info['fast'] = True
239 else:
240 info['fast'] = False
242 if info['limitType'] == 'fl' and info['bb'] is not None:
243 if info['type'] == 'ring':
244 try:
245 info['sb'] = self.Lim_Blinds[info['bb']][0]
246 info['bb'] = self.Lim_Blinds[info['bb']][1]
247 except KeyError:
248 tmp = handText[0:200]
249 log.error(("BovadaToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (mg['BB'], tmp))
250 raise FpdbParseError
251 else:
252 info['sb'] = str((Decimal(info['sb'])/2).quantize(Decimal("0.01")))
253 info['bb'] = str(Decimal(info['sb']).quantize(Decimal("0.01")))
255 return info
257 def readHandInfo(self, hand):
258 info = {}
259 m = self.re_GameInfo.search(hand.handText)
260 if m is None:
261 tmp = hand.handText[0:200]
262 log.error(("BovadaToFpdb.readHandInfo: '%s'") % tmp)
263 raise FpdbParseError
265 info.update(m.groupdict())
266 m = self.re_Buyin.search(self.in_path)
267 if m: info.update(m.groupdict())
268 hand.allInBlind = False
269 m2 = self.re_Knockout.search(self.in_path)
270 if m2: info.update(m2.groupdict())
272 if "Hyper Turbo" in self.in_path:
273 hand.speed = "Hyper"
274 elif "Turbo" in self.in_path:
275 hand.speed = "Turbo"
277 for key in info:
278 if key == 'DATETIME':
279 m1 = self.re_DateTime.finditer(info[key])
280 datetimestr = "2000/01/01 00:00:00" # default used if time not found
281 for a in m1:
282 datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),a.group('S'))
283 #tz = a.group('TZ') # just assume ET??
284 #print " tz = ", tz, " datetime =", datetimestr
285 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET"
286 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC")
287 if key == 'HID':
288 hand.handid = info[key]
289 if key == 'TOURNO':
290 hand.tourNo = info[key]
291 if key == 'BUYIN':
292 if info['TOURNO']!=None:
293 if info[key] == 'Freeroll':
294 hand.buyin = 0
295 hand.fee = 0
296 hand.buyinCurrency = "FREE"
297 else:
298 if info[key].find("$")!=-1:
299 hand.buyinCurrency="USD"
300 elif re.match("^[0-9+]*$", info[key]):
301 hand.buyinCurrency="play"
302 else:
303 #FIXME: handle other currencies, play money
304 log.error(("BovadaToFpdb.readHandInfo: Failed to detect currency.") + " Hand ID: %s: '%s'" % (hand.handid, info[key]))
305 raise FpdbParseError
307 if info.get('BOUNTY') != None:
308 info['BOUNTY'] = self.clearMoneyString(info['BOUNTY'].strip(u'$')) # Strip here where it isn't 'None'
309 hand.koBounty = int(100*Decimal(info['BOUNTY']))
310 hand.isKO = True
311 else:
312 hand.isKO = False
314 info['BIAMT'] = self.clearMoneyString(info['BIAMT'].strip(u'$'))
316 if info['BIRAKE']:
317 info['BIRAKE'] = self.clearMoneyString(info['BIRAKE'].strip(u'$'))
318 else:
319 info['BIRAKE'] = '0'
321 if info['TICKET'] == None:
322 hand.buyin = int(100*Decimal(info['BIAMT']))
323 hand.fee = int(100*Decimal(info['BIRAKE']))
324 else:
325 hand.buyin = 0
326 hand.fee = 0
327 if key == 'TABLE':
328 if info.get('TABLENO'):
329 hand.tablename = info.get('TABLENO')
330 elif info['ZONE'] and 'Zone' in info['ZONE']:
331 hand.tablename = info['ZONE'] + ' ' +info[key]
332 else:
333 hand.tablename = info[key]
334 if key == 'MAX' and info[key] != None:
335 hand.maxseats = int(info[key])
336 if key == 'HU' and info[key] != None:
337 hand.maxseats = 2
338 if key == 'VERSION':
339 hand.version = info[key]
341 if not hand.maxseats:
342 hand.maxseats = 9
344 if not hand.version:
345 hand.version = 'LEGACY'
347 def readButton(self, hand):
348 m = self.re_Button.search(hand.handText)
349 if m:
350 hand.buttonpos = int(m.group('BUTTON'))
352 def readPlayerStacks(self, hand):
353 self.playersMap, seatNo = {}, 1
354 if hand.gametype['base'] in ("stud"):
355 m = self.re_PlayerInfoStud.finditer(hand.handText)
356 else:
357 m = self.re_PlayerInfo.finditer(hand.handText)
358 for a in m:
359 if re.search(r"%s (\s?\[ME\]\s)?: Card dealt to a spot" % re.escape(a.group('PNAME')), hand.handText) or hand.version == 'MVS':
360 if not hand.buttonpos and a.group('PNAME')=='Dealer':
361 hand.buttonpos = int(a.group('SEAT'))
362 if a.group('HERO'):
363 self.playersMap[a.group('PNAME')] = 'Hero'
364 else:
365 self.playersMap[a.group('PNAME')] = 'Seat %s' % a.group('SEAT')
366 hand.addPlayer(seatNo, self.playersMap[a.group('PNAME')], self.clearMoneyString(a.group('CASH')))
367 seatNo += 1
368 if len(hand.players)==0:
369 tmp = hand.handText[0:200]
370 log.error(("BovadaToFpdb.readPlayerStacks: '%s'") % tmp)
371 raise FpdbParseError
372 elif len(hand.players)==10:
373 hand.maxseats = 10
375 def playerSeatFromPosition(self, source, handid, position):
376 player = self.playersMap.get(position)
377 if player is None:
378 log.error(("Hand.%s: '%s' unknown player seat from position: '%s'") % (source, handid, position))
379 raise FpdbParseError
380 return player
382 def markStreets(self, hand):
383 # PREFLOP = ** Dealing down cards **
384 # This re fails if, say, river is missing; then we don't get the ** that starts the river.
385 if hand.gametype['base'] == "hold":
386 street, firststreet = 'PREFLOP', 'PREFLOP'
387 else:
388 street, firststreet = 'THIRD', 'THIRD'
389 m = self.re_Action.finditer(self.re_Hole_Third.split(hand.handText)[0])
390 allinblind = 0
391 for action in m:
392 if action.group('ATYPE') == ' All-in':
393 allinblind+=1
394 m = self.re_Action.finditer(self.re_Hole_Third.split(hand.handText)[-1])
395 dealtIn = len(hand.players) - allinblind
396 streetactions, streetno, players, i, contenders, bets, acts = 0, 1, dealtIn, 0, dealtIn, 0, None
397 for action in m:
398 if action.groupdict()!=acts or streetactions == 0:
399 acts = action.groupdict()
400 #print "DEBUG: %s, %s, %s" % (street, acts['PNAME'], acts['ATYPE']), action.group('BET'), streetactions, players, contenders
401 player = self.playerSeatFromPosition('BovadaToFpdb.markStreets', hand.handid, action.group('PNAME'))
402 if action.group('ATYPE') == ' Fold':
403 contenders -= 1
404 elif action.group('ATYPE') in (' Raises', ' raises'):
405 if streetno==1: bets = 1
406 streetactions, players = 0, contenders
407 elif action.group('ATYPE') in (' Bets', ' bets', ' Double bets'):
408 streetactions, players, bets = 0, contenders, 1
409 elif action.group('ATYPE') in (' All-in(raise)', 'All-in(raise-timeout)'):
410 streetactions, players = 0, contenders
411 contenders -= 1
412 elif action.group('ATYPE') == ' All-in':
413 if bets == 0 and streetno>1:
414 streetactions, players, bets = 0, contenders, 1
415 contenders -= 1
416 if action.group('ATYPE') != ' Card dealt to a spot':
417 if action.group('ATYPE')!=' Big blind/Bring in' or hand.gametype['base'] == 'stud':
418 streetactions += 1
419 hand.streets[street] += action.group('ACTION') + '\n'
420 #print street, action.group('PNAME'), action.group('ATYPE'), streetactions, players, contenders
421 if streetactions == players:
422 streetno += 1
423 if streetno < len(hand.actionStreets):
424 street = hand.actionStreets[streetno]
425 streetactions, players, bets = 0, contenders, 0
427 if not hand.streets.get(firststreet):
428 hand.streets[firststreet] = hand.handText
429 if hand.gametype['base'] == "hold":
430 if hand.gametype['fast']:
431 for street in ('FLOP', 'TURN', 'RIVER'):
432 m1 = self.re_Board2[street].search(hand.handText)
433 if m1 and m1.group('CARDS') and not hand.streets.get(street):
434 hand.streets[street] = m1.group('CARDS')
435 else:
436 m1 = self.re_Board1.search(hand.handText)
437 for street in ('FLOP', 'TURN', 'RIVER'):
438 if m1 and m1.group(street) and not hand.streets.get(street):
439 hand.streets[street] = m1.group(street)
442 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
443 if hand.gametype['fast']:
444 m = self.re_Board2[street].search(hand.handText)
445 if m and m.group('CARDS'):
446 hand.setCommunityCards(street, m.group('CARDS').split(' '))
447 else:
448 if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
449 m = self.re_Board1.search(hand.handText)
450 if m and m.group(street):
451 cards = m.group(street).split(' ')
452 hand.setCommunityCards(street, cards)
454 def readAntes(self, hand):
455 antes = None
456 m = self.re_Antes.finditer(hand.handText)
457 for a in m:
458 if a.groupdict()!=antes:
459 antes = a.groupdict()
460 player = self.playerSeatFromPosition('BovadaToFpdb.readAntes', hand.handid, antes['PNAME'])
461 hand.addAnte(player, self.clearMoneyString(antes['ANTE']))
463 def readBringIn(self, hand):
464 m = self.re_BringIn.search(hand.handText,re.DOTALL)
465 if m:
466 #~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
467 player = self.playerSeatFromPosition('BovadaToFpdb.readBringIn', hand.handid, m.group('PNAME'))
468 hand.addBringIn(player, self.clearMoneyString(m.group('BRINGIN')))
470 if hand.gametype['sb'] == None and hand.gametype['bb'] == None:
471 hand.gametype['sb'] = "1"
472 hand.gametype['bb'] = "2"
474 def readBlinds(self, hand):
475 sb, bb, acts, postsb, postbb, both = None, None, None, None, None, None
476 if not self.re_ReturnBet.search(hand.handText):
477 hand.setUncalledBets(True)
478 for a in self.re_PostSB.finditer(hand.handText):
479 if postsb!=a.groupdict():
480 postsb = a.groupdict()
481 player = self.playerSeatFromPosition('BovadaToFpdb.readBlinds.postSB', hand.handid, postsb['PNAME'])
482 hand.addBlind(player, 'small blind', self.clearMoneyString(postsb['SB']))
483 if not hand.gametype['sb']:
484 hand.gametype['sb'] = self.clearMoneyString(postsb['SB'])
485 sb = self.clearMoneyString(postsb['SB'])
486 self.allInBlind(hand, 'PREFLOP', a, 'small blind')
487 for a in self.re_PostBB.finditer(hand.handText):
488 if postbb!=a.groupdict():
489 postbb = a.groupdict()
490 player = self.playerSeatFromPosition('BovadaToFpdb.readBlinds.postBB', hand.handid, 'Big Blind')
491 hand.addBlind(player, 'big blind', self.clearMoneyString(postbb['BB']))
492 self.allInBlind(hand, 'PREFLOP', a, 'big blind')
493 if not hand.gametype['bb']:
494 hand.gametype['bb'] = self.clearMoneyString(postbb['BB'])
495 bb = self.clearMoneyString(postbb['BB'])
496 if not hand.gametype['currency']:
497 if postbb['CURRENCY'].find("$")!=-1:
498 hand.gametype['currency']="USD"
499 elif re.match("^[0-9+]*$", postbb['CURRENCY']):
500 hand.gametype['currency']="play"
501 for a in self.re_Action.finditer(self.re_Hole_Third.split(hand.handText)[0]):
502 if acts!=a.groupdict():
503 acts = a.groupdict()
504 if acts['ATYPE'] == ' All-in':
505 re_Ante_Plyr = re.compile(r"^" + re.escape(acts['PNAME']) + " (\s?\[ME\]\s)?: Ante chip %(CUR)s(?P<ANTE>[%(NUM)s]+)" % self.substitutions, re.MULTILINE)
506 m = self.re_Antes.search(hand.handText)
507 m1 = re_Ante_Plyr.search(hand.handText)
508 if (not m or m1):
509 player = self.playerSeatFromPosition('BovadaToFpdb.readBlinds.postBB', hand.handid, acts['PNAME'])
510 if acts['PNAME'] == 'Big Blind':
511 hand.addBlind(player, 'big blind', self.clearMoneyString(acts['BET']))
512 self.allInBlind(hand, 'PREFLOP', a, 'big blind')
513 elif acts['PNAME'] == 'Small Blind' or (acts['PNAME'] == 'Dealer' and len(hand.players)==2):
514 hand.addBlind(player, 'small blind', self.clearMoneyString(acts['BET']))
515 self.allInBlind(hand, 'PREFLOP', a, 'small blind')
516 elif m:
517 player = self.playerSeatFromPosition('BovadaToFpdb.readAntes', hand.handid, acts['PNAME'])
518 hand.addAnte(player, self.clearMoneyString(acts['BET']))
519 self.allInBlind(hand, 'PREFLOP', a, 'antes')
520 self.fixBlinds(hand)
521 for a in self.re_PostBoth.finditer(hand.handText):
522 if both!=a.groupdict():
523 both = a.groupdict()
524 player = self.playerSeatFromPosition('BovadaToFpdb.readBlinds.postBoth', hand.handid, both['PNAME'])
525 hand.addBlind(player, 'both', self.clearMoneyString(both['SBBB']))
526 self.allInBlind(hand, 'PREFLOP', a, 'both')
530 def fixBlinds(self, hand):
531 if hand.gametype['sb'] == None and hand.gametype['bb'] != None:
532 BB = str(Decimal(hand.gametype['bb']) * 2)
533 if self.Lim_Blinds.get(BB) != None:
534 hand.gametype['sb'] = self.Lim_Blinds.get(BB)[0]
535 elif hand.gametype['bb'] == None and hand.gametype['sb'] != None:
536 for k, v in list(self.Lim_Blinds.items()):
537 if hand.gametype['sb'] == v[0]:
538 hand.gametype['bb'] = v[1]
539 if hand.gametype['sb'] == None or hand.gametype['bb'] == None:
540 log.error(("BovadaToFpdb.fixBlinds: Failed to fix blinds") + " Hand ID: %s" % (hand.handid, ))
541 raise FpdbParseError
542 hand.sb = hand.gametype['sb']
543 hand.bb = hand.gametype['bb']
545 def readHoleCards(self, hand):
546# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
547# we need to grab hero's cards
548 for street in ('PREFLOP', 'DEAL'):
549 if street in hand.streets.keys():
550 foundDict = None
551 m = self.re_HeroCards.finditer(hand.handText)
552 for found in m:
553 if found.groupdict()!=foundDict:
554 foundDict = found.groupdict()
555 hand.hero = 'Hero'
556 newcards = found.group('NEWCARDS').split(' ')
557 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
559 for street, text in list(hand.streets.items()):
560 if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
561 m = self.re_ShowdownAction.finditer(hand.streets[street])
562 foundDict = None
563 for found in m:
564 if foundDict!=found.groupdict():
565 foundDict = found.groupdict()
566 player = self.playerSeatFromPosition('BovadaToFpdb.readHoleCards', hand.handid, found.group('PNAME'))
567 if street != 'SEVENTH' or found.group('HERO'):
568 newcards = found.group('CARDS').split(' ')
569 oldcards = []
570 else:
571 oldcards = found.group('CARDS').split(' ')
572 newcards = []
574 if street == 'THIRD' and found.group('HERO'): # hero in stud game
575 hand.hero = 'Hero'
576 hand.dealt.add(player) # need this for stud??
577 hand.addHoleCards(street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False)
578 else:
579 hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
581 def readAction(self, hand, street):
582 acts = None
583 m = self.re_Action.finditer(hand.streets[street])
584 for action in m:
585 if acts!=action.groupdict():
586 acts = action.groupdict()
587 player = self.playerSeatFromPosition('BovadaToFpdb.readAction', hand.handid, action.group('PNAME'))
588 #print "DEBUG: %s, %s, %s, %s, %s" % (street, acts['PNAME'], player, acts['ATYPE'], action.group('BET'))
589 if action.group('ATYPE') not in (' Checks', ' Fold', ' Card dealt to a spot', ' Big blind/Bring in') and not hand.allInBlind:
590 hand.setUncalledBets(False)
591 if action.group('ATYPE') == ' Fold':
592 hand.addFold( street, player)
593 elif action.group('ATYPE') == ' Checks':
594 hand.addCheck( street, player)
595 elif action.group('ATYPE') == ' Calls' or action.group('ATYPE') == ' Call':
596 if not action.group('BET'):
597 log.error("BovadaToFpdb.readAction: Amount not found in Call %s" % hand.handid)
598 raise FpdbParseError
599 hand.addCall( street, player, self.clearMoneyString(action.group('BET')) )
600 elif action.group('ATYPE') in (' Raises', ' raises', ' All-in(raise)', ' All-in(raise-timeout)'):
601 if action.group('BETTO'):
602 bet = self.clearMoneyString(action.group('BETTO'))
603 elif action.group('BET'):
604 bet = self.clearMoneyString(action.group('BET'))
605 else:
606 log.error("BovadaToFpdb.readAction: Amount not found in Raise %s" % hand.handid)
607 raise FpdbParseError
608 hand.addRaiseTo( street, player, bet )
609 elif action.group('ATYPE') in (' Bets', ' bets', ' Double bets'):
610 if not action.group('BET'):
611 log.error("BovadaToFpdb.readAction: Amount not found in Bet %s" % hand.handid)
612 raise FpdbParseError
613 hand.addBet( street, player, self.clearMoneyString(action.group('BET')) )
614 elif action.group('ATYPE') == ' All-in':
615 if not action.group('BET'):
616 log.error("BovadaToFpdb.readAction: Amount not found in All-in %s" % hand.handid)
617 raise FpdbParseError
618 hand.addAllIn( street, player, self.clearMoneyString(action.group('BET')) )
619 self.allInBlind(hand, street, action, action.group('ATYPE'))
620 elif action.group('ATYPE') == ' Bring_in chip':
621 pass
622 elif action.group('ATYPE') in (' Card dealt to a spot', ' Big blind/Bring in'):
623 pass
624 else:
625 log.debug(("DEBUG:") + " " + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group('PNAME'), action.group('ATYPE')))
627 def allInBlind(self, hand, street, action, actiontype):
628 if street in ('PREFLOP', 'DEAL'):
629 player = self.playerSeatFromPosition('BovadaToFpdb.allInBlind', hand.handid, action.group('PNAME'))
630 if hand.stacks[player]==0 and not self.re_ReturnBet.search(hand.handText):
631 hand.setUncalledBets(True)
632 hand.allInBlind = True
634 def readShowdownActions(self, hand):
635# TODO: pick up mucks also??
636 if hand.gametype['base'] in ("hold"):
637 for shows in self.re_ShowdownAction.finditer(hand.handText):
638 cards = shows.group('CARDS').split(' ')
639 player = self.playerSeatFromPosition('BovadaToFpdb.readShowdownActions', hand.handid, shows.group('PNAME'))
640 hand.addShownCards(cards, player)
642 def readCollectPot(self,hand):
643 if self.re_CollectPot2.search(hand.handText):
644 re_CollectPot = self.re_CollectPot2
645 else:
646 re_CollectPot = self.re_CollectPot1
647 for m in re_CollectPot.finditer(hand.handText.replace(" [ME]", "") if hand.version == 'MVS' else hand.handText):# [ME]
648 collect, pot = m.groupdict(), 0
649 if 'POT1' in collect and collect['POT1']!=None:
650 pot += Decimal(self.clearMoneyString(collect['POT1']))
651 if 'POT2' in collect and collect['POT2']!=None:
652 pot += Decimal(self.clearMoneyString(collect['POT2']))
653 if pot>0:
654 player = self.playerSeatFromPosition('BovadaToFpdb.readCollectPot', hand.handid, collect['PNAME'])
655 hand.addCollectPot(player=player,pot=str(pot))
657 def readShownCards(self,hand):
658 pass
661 def readTourneyResults(self, hand):
662 """Reads knockout bounties and add them to the koCounts dict"""
663 for a in self.re_Bounty.finditer(hand.handText):
664 player = self.playerSeatFromPosition('BovadaToFpdb.readCollectPot', hand.handid, a.group('PNAME'))
665 if player not in hand.koCounts:
666 hand.koCounts[player] = 0
667 hand.koCounts[player] += 1