Coverage for BovadaToFpdb.py: 0%

456 statements  

« 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######################################################################## 

20 

21#import L10n 

22#_ = L10n.get_translation() 

23 

24# TODO: straighten out discards for draw games 

25 

26import sys, copy 

27from HandHistoryConverter import * 

28import Card 

29from decimal_wrapper import Decimal 

30 

31# Bovada HH Format 

32 

33class Bovada(HandHistoryConverter): 

34 

35 # Class Variables 

36 

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 } 

51 

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 } 

71 

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'), 

84 

85 } 

86 currencies = {'$':'USD', '':'T$'} 

87 

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) 

110 

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) 

116 

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) 

122 

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 

159 

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) 

173 

174 def readSupportedGames(self): 

175 return [["ring", "hold", "nl"], 

176 ["ring", "hold", "pl"], 

177 ["ring", "hold", "fl"], 

178 

179 ["ring", "stud", "fl"], 

180 

181 ["tour", "hold", "nl"], 

182 ["tour", "hold", "pl"], 

183 ["tour", "hold", "fl"], 

184 

185 ["tour", "stud", "fl"], 

186 ] 

187 

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 

195 

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 

203 

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")) 

209 

210 mg = m.groupdict() 

211 m = self.re_Stakes.search(self.in_path) 

212 if m: mg.update(m.groupdict()) 

213 

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']] 

221 

222 if 'SB' in mg: 

223 info['sb'] = self.clearMoneyString(mg['SB']) 

224 if 'BB' in mg: 

225 info['bb'] = self.clearMoneyString(mg['BB']) 

226 

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' 

233 

234 if 'CURRENCY' in mg and mg['CURRENCY'] is not None: 

235 info['currency'] = self.currencies[mg['CURRENCY']] 

236 

237 if 'Zone' in mg['GAME']: 

238 info['fast'] = True 

239 else: 

240 info['fast'] = False 

241 

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"))) 

254 

255 return info 

256 

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 

264 

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()) 

271 

272 if "Hyper Turbo" in self.in_path: 

273 hand.speed = "Hyper" 

274 elif "Turbo" in self.in_path: 

275 hand.speed = "Turbo" 

276 

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 

306 

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 

313 

314 info['BIAMT'] = self.clearMoneyString(info['BIAMT'].strip(u'$')) 

315 

316 if info['BIRAKE']: 

317 info['BIRAKE'] = self.clearMoneyString(info['BIRAKE'].strip(u'$')) 

318 else: 

319 info['BIRAKE'] = '0' 

320 

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] 

340 

341 if not hand.maxseats: 

342 hand.maxseats = 9 

343 

344 if not hand.version: 

345 hand.version = 'LEGACY' 

346 

347 def readButton(self, hand): 

348 m = self.re_Button.search(hand.handText) 

349 if m: 

350 hand.buttonpos = int(m.group('BUTTON')) 

351 

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 

374 

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 

381 

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 

426 

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) 

440 

441 

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) 

453 

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'])) 

462 

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'))) 

469 

470 if hand.gametype['sb'] == None and hand.gametype['bb'] == None: 

471 hand.gametype['sb'] = "1" 

472 hand.gametype['bb'] = "2" 

473 

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') 

527 

528 

529 

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'] 

544 

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) 

558 

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 = [] 

573 

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) 

580 

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'))) 

626 

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 

633 

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) 

641 

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)) 

656 

657 def readShownCards(self,hand): 

658 pass 

659 

660 

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 

668