Coverage for BovadaToFpdb.py: 0%
461 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +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
26from HandHistoryConverter import HandHistoryConverter, FpdbParseError, FpdbHandPartial
27from decimal import Decimal
28import re
29import logging
30import datetime
32# Bovada HH Format
33log = logging.getLogger("parser")
36class Bovada(HandHistoryConverter):
37 # Class Variables
39 sitename = "Bovada"
40 filetype = "text"
41 codepage = ("utf8", "cp1252")
42 siteId = 21 # Needs to match id entry in Sites database
43 summaryInFile = True
44 copyGameHeader = True
45 sym = {"USD": "\$", "T$": "", "play": ""} # ADD Euro, Sterling, etc HERE
46 substitutions = {
47 "LEGAL_ISO": "USD", # legal ISO currency codes
48 "LS": "\$|", # legal currency symbols - Euro(cp1252, utf-8)
49 "PLYR": r"(?P<PNAME>.+?)",
50 "CUR": "(\$|)",
51 "NUM": ".,\d",
52 }
54 # translations from captured groups to fpdb info strings
55 Lim_Blinds = {
56 "0.04": ("0.01", "0.02"),
57 "0.08": ("0.02", "0.04"),
58 "0.10": ("0.02", "0.05"),
59 "0.20": ("0.05", "0.10"),
60 "0.25": ("0.05", "0.10"),
61 "0.40": ("0.10", "0.20"),
62 "0.50": ("0.10", "0.25"),
63 "1.00": ("0.25", "0.50"),
64 "1": ("0.25", "0.50"),
65 "2.00": ("0.50", "1.00"),
66 "2": ("0.50", "1.00"),
67 "4.00": ("1.00", "2.00"),
68 "4": ("1.00", "2.00"),
69 "6.00": ("1.50", "3.00"),
70 "6": ("1.50", "3.00"),
71 "8.00": ("2.00", "4.00"),
72 "8": ("2.00", "4.00"),
73 "10.00": ("2.50", "5.00"),
74 "10": ("2.50", "5.00"),
75 "16.00": ("4.00", "8.00"),
76 "16": ("4.00", "8.00"),
77 "20.00": ("5.00", "10.00"),
78 "20": ("5.00", "10.00"),
79 "30.00": ("7.50", "15.00"),
80 "30": ("7.50", "15.00"),
81 "40.00": ("10.00", "20.00"),
82 "40": ("10.00", "20.00"),
83 "60.00": ("15.00", "30.00"),
84 "60": ("15.00", "30.00"),
85 "80.00": ("20.00", "40.00"),
86 "80": ("20.00", "40.00"),
87 "100.00": ("25.00", "50.00"),
88 "100": ("25.00", "50.00"),
89 }
91 limits = {"No Limit": "nl", "Pot Limit": "pl", "Fixed Limit": "fl", "Turbo": "nl"}
92 games = { # base, category
93 "HOLDEM": ("hold", "holdem"),
94 "OMAHA": ("hold", "omahahi"),
95 "OMAHA HiLo": ("hold", "omahahilo"),
96 "OMAHA_HL": ("hold", "omahahilo"),
97 "7CARD": ("stud", "studhi"),
98 "7CARD HiLo": ("stud", "studhilo"),
99 "7CARD_HL": ("hold", "studhilo"),
100 "HOLDEMZonePoker": ("hold", "holdem"),
101 "OMAHAZonePoker": ("hold", "omahahi"),
102 "OMAHA HiLoZonePoker": ("hold", "omahahilo"),
103 }
104 currencies = {"$": "USD", "": "T$"}
106 # Static regexes
107 re_GameInfo = re.compile(
108 """
109 (Ignition|Bovada|Bodog(\.com|\.eu|\sUK|\sCanada|88)?)\sHand\s\#C?(?P<HID>[0-9]+):?\s+
110 ((?P<ZONE>Zone\sPoker\sID|TBL)\#(?P<TABLE>.+?)\s)?
111 (?P<GAME>HOLDEM|OMAHA|OMAHA_HL|7CARD|7CARD\sHiLo|OMAHA\sHiLo|7CARD_HL|HOLDEMZonePoker|OMAHAZonePoker|OMAHA\sHiLoZonePoker)\s+
112 (Tournament\s\#(M\-)? # open paren of tournament info Tournament #2194767 TBL#1,
113 (?P<TOURNO>\d+)\sTBL\#(?P<TABLENO>\d+),
114 \s)?
115 (?P<HU>1\son\s1\s)?
116 (?P<LIMIT>No\sLimit|Fixed\sLimit|Pot\sLimit|Turbo)?
117 (\s?Normal\s?)?
118 (-\sLevel\s\d+?\s
119 \(? # open paren of the stakes
120 (?P<CURRENCY>%(LS)s|)?
121 (?P<SB>[,.0-9]+)/(%(LS)s)?
122 (?P<BB>[,.0-9]+)
123 \s?(?P<ISO>%(LEGAL_ISO)s)?
124 \))?
125 (\s\[(?P<VERSION>MVS)\])?
126 \s-\s
127 (?P<DATETIME>.*$)
128 """
129 % substitutions,
130 re.MULTILINE | re.VERBOSE,
131 )
133 re_PlayerInfo = re.compile(
134 """
135 ^Seat\s(?P<SEAT>[0-9]+):\s
136 %(PLYR)s\s(?P<HERO>\[ME\]\s)?
137 \((%(LS)s)?(?P<CASH>[%(NUM)s]+)\sin\schips\)"""
138 % substitutions,
139 re.MULTILINE | re.VERBOSE,
140 )
142 re_PlayerInfoStud = re.compile(
143 """
144 ^(?P<PNAME>Seat\+(?P<SEAT>[0-9]+))
145 (?P<HERO>\s\[ME\])?:\s
146 (%(LS)s)?(?P<CASH>[%(NUM)s]+)\sin\schips"""
147 % substitutions,
148 re.MULTILINE | re.VERBOSE,
149 )
151 re_PlayerSeat = re.compile("^Seat\+(?P<SEAT>[0-9]+)", re.MULTILINE | re.VERBOSE)
152 re_Identify = re.compile("(Ignition|Bovada|Bodog(\.com|\.eu|\sUK|\sCanada|88)?)\sHand")
153 re_SplitHands = re.compile("\n\n+")
154 re_TailSplitHands = re.compile("(\n\n\n+)")
155 re_Button = re.compile("Dealer : Set dealer\/Bring in spot \[(?P<BUTTON>\d+)\]", re.MULTILINE)
156 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?)?\]")
157 re_Board2 = {
158 "FLOP": re.compile(r"\*\*\* FLOP \*\*\* \[(?P<CARDS>\S\S \S\S \S\S)\]"),
159 "TURN": re.compile(r"\*\*\* TURN \*\*\* \[\S\S \S\S \S\S\] \[(?P<CARDS>\S\S)\]"),
160 "RIVER": re.compile(r"\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S\] \[(?P<CARDS>\S\S)\]"),
161 }
162 re_DateTime = re.compile(
163 """(?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]+)""",
164 re.MULTILINE,
165 )
166 # These used to be compiled per player, but regression tests say
167 # we don't have to, and it makes life faster.
168 re_PostSB = re.compile(
169 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]+)"
170 % substitutions,
171 re.MULTILINE,
172 )
173 re_PostBB = re.compile(
174 r"^%(PLYR)s (\s?\[ME\]\s)?: (Big (B|b)lind\/Bring in|Big (B|b)lind) (?P<CURRENCY>%(CUR)s)(?P<BB>[%(NUM)s]+)"
175 % substitutions,
176 re.MULTILINE,
177 )
178 re_Antes = re.compile(
179 r"^%(PLYR)s (\s?\[ME\]\s)?: Ante chip %(CUR)s(?P<ANTE>[%(NUM)s]+)" % substitutions, re.MULTILINE
180 )
181 re_BringIn = re.compile(
182 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]+)"
183 % substitutions,
184 re.MULTILINE,
185 )
186 re_PostBoth = re.compile(
187 r"^%(PLYR)s (\s?\[ME\]\s)?: Posts dead chip %(CUR)s(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE
188 )
189 re_HeroCards = re.compile(
190 r"^%(PLYR)s ?\[ME\] : Card dealt to a spot \[(?P<NEWCARDS>.+?)\]" % substitutions, re.MULTILINE
191 )
192 re_Action = re.compile(
193 r"""(?P<ACTION>
194 ^%(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)
195 (\schip\sinfo)?(\(timeout\))?(\s%(CUR)s(?P<BET>[%(NUM)s]+)(\sto\s%(CUR)s(?P<BETTO>[%(NUM)s]+))?|\s\[(?P<NEWCARDS>.+?)\])?)"""
196 % substitutions,
197 re.MULTILINE | re.VERBOSE,
198 )
199 re_ShowdownAction = re.compile(
200 r"^%(PLYR)s (?P<HERO>\s?\[ME\]\s)?: Card dealt to a spot \[(?P<CARDS>.*)\]" % substitutions, re.MULTILINE
201 )
202 # 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)
203 re_CollectPot1 = re.compile(
204 r"^%(PLYR)s (\s?\[ME\]\s)?: Hand (R|r)esult(\-Side (P|p)ot)? %(CUR)s(?P<POT1>[%(NUM)s]+)" % substitutions,
205 re.MULTILINE,
206 )
207 re_Bounty = re.compile(
208 r"^%(PLYR)s (\s?\[ME\]\s)?: BOUNTY PRIZE \[%(CUR)s(?P<BOUNTY>[%(NUM)s]+)\]" % substitutions, re.MULTILINE
209 )
210 re_Dealt = re.compile(r"^%(PLYR)s (\s?\[ME\]\s)?: Card dealt to a spot" % substitutions, re.MULTILINE)
211 re_Buyin = re.compile(
212 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"
213 % substitutions
214 )
215 re_Knockout = re.compile(r"\s\((?P<BOUNTY>[%(LS)s\d\.,]+)\sKnockout" % substitutions)
216 re_Stakes = re.compile(
217 r"(RING|ZONE)\s-\s(?P<CURRENCY>%(LS)s|)?(?P<SB>[%(NUM)s]+)-(%(LS)s)?(?P<BB>[%(NUM)s]+)" % substitutions
218 )
219 re_Summary = re.compile(r"\*\*\*\sSUMMARY\s\*\*\*")
220 re_Hole_Third = re.compile(r"\*\*\*\s(3RD\sSTREET|HOLE\sCARDS)\s\*\*\*")
221 re_ReturnBet = re.compile(r"Return\suncalled\sportion", re.MULTILINE)
222 # Small Blind : Hand result $19
224 def compilePlayerRegexs(self, hand):
225 subst = self.substitutions
226 players = set(self.playersMap.keys())
227 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
228 self.compiledPlayers = players
229 subst["PLYR"] = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
230 self.re_CollectPot2 = re.compile(
231 """
232 Seat[\+ ](?P<SEAT>[0-9]+):\s?%(PLYR)s
233 (\sHI)?\s(%(LS)s)?(?P<POT1>[%(NUM)s]+)?
234 (?P<STRING>[a-zA-Z ]+)
235 (?P<CARDS1>\[[-a-zA-Z0-9 ]+\])
236 (\sLOW?\s(%(LS)s)?(?P<POT2>[%(NUM)s]+))?
237 """
238 % subst,
239 re.MULTILINE | re.VERBOSE,
240 )
242 def readSupportedGames(self):
243 return [
244 ["ring", "hold", "nl"],
245 ["ring", "hold", "pl"],
246 ["ring", "hold", "fl"],
247 ["ring", "stud", "fl"],
248 ["tour", "hold", "nl"],
249 ["tour", "hold", "pl"],
250 ["tour", "hold", "fl"],
251 ["tour", "stud", "fl"],
252 ]
254 def parseHeader(self, handText, whole_file):
255 gametype = self.determineGameType(handText)
256 if gametype["type"] == "tour":
257 handlist = re.split(self.re_SplitHands, whole_file)
258 result = re.findall(self.re_PlayerSeat, handlist[0])
259 gametype["maxSeats"] = len(result)
260 return gametype
262 def determineGameType(self, handText):
263 info = {}
264 m = self.re_GameInfo.search(handText)
265 if not m:
266 tmp = handText[0:200]
267 log.error(("BovadaToFpdb.determineGameType: '%s'") % tmp)
268 raise FpdbParseError
270 m1 = self.re_Dealt.search(handText)
271 m2 = self.re_Summary.split(handText)
272 m3 = self.re_Hole_Third.split(handText)
273 if not m1 or len(m2) != 2 or len(m3) > 3:
274 raise FpdbHandPartial("BovadaToFpdb.determineGameType: " + ("Partial hand history"))
276 mg = m.groupdict()
277 m = self.re_Stakes.search(self.in_path)
278 if m:
279 mg.update(m.groupdict())
281 if "LIMIT" in mg:
282 if not mg["LIMIT"]:
283 info["limitType"] = "nl"
284 else:
285 info["limitType"] = self.limits[mg["LIMIT"]]
286 if "GAME" in mg:
287 (info["base"], info["category"]) = self.games[mg["GAME"]]
289 if "SB" in mg:
290 info["sb"] = self.clearMoneyString(mg["SB"])
291 if "BB" in mg:
292 info["bb"] = self.clearMoneyString(mg["BB"])
294 if "TOURNO" in mg and mg["TOURNO"] is not None:
295 info["type"] = "tour"
296 info["currency"] = "T$"
297 else:
298 info["type"] = "ring"
299 info["currency"] = "USD"
301 if "CURRENCY" in mg and mg["CURRENCY"] is not None:
302 info["currency"] = self.currencies[mg["CURRENCY"]]
304 if "Zone" in mg["GAME"]:
305 info["fast"] = True
306 else:
307 info["fast"] = False
309 if info["limitType"] == "fl" and info["bb"] is not None:
310 if info["type"] == "ring":
311 try:
312 info["sb"] = self.Lim_Blinds[info["bb"]][0]
313 info["bb"] = self.Lim_Blinds[info["bb"]][1]
314 except KeyError:
315 tmp = handText[0:200]
316 log.error(
317 ("BovadaToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (mg["BB"], tmp)
318 )
319 raise FpdbParseError
320 else:
321 info["sb"] = str((Decimal(info["sb"]) / 2).quantize(Decimal("0.01")))
322 info["bb"] = str(Decimal(info["sb"]).quantize(Decimal("0.01")))
324 return info
326 def readHandInfo(self, hand):
327 info = {}
328 m = self.re_GameInfo.search(hand.handText)
329 if m is None:
330 tmp = hand.handText[0:200]
331 log.error(("BovadaToFpdb.readHandInfo: '%s'") % tmp)
332 raise FpdbParseError
334 info.update(m.groupdict())
335 m = self.re_Buyin.search(self.in_path)
336 if m:
337 info.update(m.groupdict())
338 hand.allInBlind = False
339 m2 = self.re_Knockout.search(self.in_path)
340 if m2:
341 info.update(m2.groupdict())
343 if "Hyper Turbo" in self.in_path:
344 hand.speed = "Hyper"
345 elif "Turbo" in self.in_path:
346 hand.speed = "Turbo"
348 for key in info:
349 if key == "DATETIME":
350 m1 = self.re_DateTime.finditer(info[key])
351 datetimestr = "2000/01/01 00:00:00" # default used if time not found
352 for a in m1:
353 datetimestr = "%s/%s/%s %s:%s:%s" % (
354 a.group("Y"),
355 a.group("M"),
356 a.group("D"),
357 a.group("H"),
358 a.group("MIN"),
359 a.group("S"),
360 )
361 # tz = a.group('TZ') # just assume ET??
362 # print " tz = ", tz, " datetime =", datetimestr
363 hand.startTime = datetime.datetime.strptime(
364 datetimestr, "%Y/%m/%d %H:%M:%S"
365 ) # also timezone at end, e.g. " ET"
366 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC")
367 if key == "HID":
368 hand.handid = info[key]
369 if key == "TOURNO":
370 hand.tourNo = info[key]
371 if key == "BUYIN":
372 if info["TOURNO"] is not None:
373 if info[key] == "Freeroll":
374 hand.buyin = 0
375 hand.fee = 0
376 hand.buyinCurrency = "FREE"
377 else:
378 if info[key].find("$") != -1:
379 hand.buyinCurrency = "USD"
380 elif re.match("^[0-9+]*$", info[key]):
381 hand.buyinCurrency = "play"
382 else:
383 # FIXME: handle other currencies, play money
384 log.error(
385 ("BovadaToFpdb.readHandInfo: Failed to detect currency.")
386 + " Hand ID: %s: '%s'" % (hand.handid, info[key])
387 )
388 raise FpdbParseError
390 if info.get("BOUNTY") is not None:
391 info["BOUNTY"] = self.clearMoneyString(
392 info["BOUNTY"].strip("$")
393 ) # Strip here where it isn't 'None'
394 hand.koBounty = int(100 * Decimal(info["BOUNTY"]))
395 hand.isKO = True
396 else:
397 hand.isKO = False
399 info["BIAMT"] = self.clearMoneyString(info["BIAMT"].strip("$"))
401 if info["BIRAKE"]:
402 info["BIRAKE"] = self.clearMoneyString(info["BIRAKE"].strip("$"))
403 else:
404 info["BIRAKE"] = "0"
406 if info["TICKET"] is None:
407 hand.buyin = int(100 * Decimal(info["BIAMT"]))
408 hand.fee = int(100 * Decimal(info["BIRAKE"]))
409 else:
410 hand.buyin = 0
411 hand.fee = 0
412 if key == "TABLE":
413 if info.get("TABLENO"):
414 hand.tablename = info.get("TABLENO")
415 elif info["ZONE"] and "Zone" in info["ZONE"]:
416 hand.tablename = info["ZONE"] + " " + info[key]
417 else:
418 hand.tablename = info[key]
419 if key == "MAX" and info[key] is not None:
420 hand.maxseats = int(info[key])
421 if key == "HU" and info[key] is not None:
422 hand.maxseats = 2
423 if key == "VERSION":
424 hand.version = info[key]
426 if not hand.maxseats:
427 hand.maxseats = 9
429 if not hand.version:
430 hand.version = "LEGACY"
432 def readButton(self, hand):
433 m = self.re_Button.search(hand.handText)
434 if m:
435 hand.buttonpos = int(m.group("BUTTON"))
437 def readPlayerStacks(self, hand):
438 self.playersMap, seatNo = {}, 1
439 if hand.gametype["base"] in ("stud"):
440 m = self.re_PlayerInfoStud.finditer(hand.handText)
441 else:
442 m = self.re_PlayerInfo.finditer(hand.handText)
443 for a in m:
444 if (
445 re.search(r"%s (\s?\[ME\]\s)?: Card dealt to a spot" % re.escape(a.group("PNAME")), hand.handText)
446 or hand.version == "MVS"
447 ):
448 if not hand.buttonpos and a.group("PNAME") == "Dealer":
449 hand.buttonpos = int(a.group("SEAT"))
450 if a.group("HERO"):
451 self.playersMap[a.group("PNAME")] = "Hero"
452 else:
453 self.playersMap[a.group("PNAME")] = "Seat %s" % a.group("SEAT")
454 hand.addPlayer(seatNo, self.playersMap[a.group("PNAME")], self.clearMoneyString(a.group("CASH")))
455 seatNo += 1
456 if len(hand.players) == 0:
457 tmp = hand.handText[0:200]
458 log.error(("BovadaToFpdb.readPlayerStacks: '%s'") % tmp)
459 raise FpdbParseError
460 elif len(hand.players) == 10:
461 hand.maxseats = 10
463 def playerSeatFromPosition(self, source, handid, position):
464 player = self.playersMap.get(position)
465 if player is None:
466 log.error(("Hand.%s: '%s' unknown player seat from position: '%s'") % (source, handid, position))
467 raise FpdbParseError
468 return player
470 def markStreets(self, hand):
471 # PREFLOP = ** Dealing down cards **
472 # This re fails if, say, river is missing; then we don't get the ** that starts the river.
473 if hand.gametype["base"] == "hold":
474 street, firststreet = "PREFLOP", "PREFLOP"
475 else:
476 street, firststreet = "THIRD", "THIRD"
477 m = self.re_Action.finditer(self.re_Hole_Third.split(hand.handText)[0])
478 allinblind = 0
479 for action in m:
480 if action.group("ATYPE") == " All-in":
481 allinblind += 1
482 m = self.re_Action.finditer(self.re_Hole_Third.split(hand.handText)[-1])
483 dealtIn = len(hand.players) - allinblind
484 streetactions, streetno, players, contenders, bets, acts = 0, 1, dealtIn, 0, dealtIn, 0, None
485 for action in m:
486 if action.groupdict() != acts or streetactions == 0:
487 acts = action.groupdict()
488 # print "DEBUG: %s, %s, %s" % (street, acts['PNAME'], acts['ATYPE']), action.group('BET'), streetactions, players, contenders
489 # player = self.playerSeatFromPosition("BovadaToFpdb.markStreets", hand.handid, action.group("PNAME"))
490 if action.group("ATYPE") == " Fold":
491 contenders -= 1
492 elif action.group("ATYPE") in (" Raises", " raises"):
493 if streetno == 1:
494 bets = 1
495 streetactions, players = 0, contenders
496 elif action.group("ATYPE") in (" Bets", " bets", " Double bets"):
497 streetactions, players, bets = 0, contenders, 1
498 elif action.group("ATYPE") in (" All-in(raise)", "All-in(raise-timeout)"):
499 streetactions, players = 0, contenders
500 contenders -= 1
501 elif action.group("ATYPE") == " All-in":
502 if bets == 0 and streetno > 1:
503 streetactions, players, bets = 0, contenders, 1
504 contenders -= 1
505 if action.group("ATYPE") != " Card dealt to a spot":
506 if action.group("ATYPE") != " Big blind/Bring in" or hand.gametype["base"] == "stud":
507 streetactions += 1
508 hand.streets[street] += action.group("ACTION") + "\n"
509 # print street, action.group('PNAME'), action.group('ATYPE'), streetactions, players, contenders
510 if streetactions == players:
511 streetno += 1
512 if streetno < len(hand.actionStreets):
513 street = hand.actionStreets[streetno]
514 streetactions, players, bets = 0, contenders, 0
516 if not hand.streets.get(firststreet):
517 hand.streets[firststreet] = hand.handText
518 if hand.gametype["base"] == "hold":
519 if hand.gametype["fast"]:
520 for street in ("FLOP", "TURN", "RIVER"):
521 m1 = self.re_Board2[street].search(hand.handText)
522 if m1 and m1.group("CARDS") and not hand.streets.get(street):
523 hand.streets[street] = m1.group("CARDS")
524 else:
525 m1 = self.re_Board1.search(hand.handText)
526 for street in ("FLOP", "TURN", "RIVER"):
527 if m1 and m1.group(street) and not hand.streets.get(street):
528 hand.streets[street] = m1.group(street)
530 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
531 if hand.gametype["fast"]:
532 m = self.re_Board2[street].search(hand.handText)
533 if m and m.group("CARDS"):
534 hand.setCommunityCards(street, m.group("CARDS").split(" "))
535 else:
536 if street in (
537 "FLOP",
538 "TURN",
539 "RIVER",
540 ): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
541 m = self.re_Board1.search(hand.handText)
542 if m and m.group(street):
543 cards = m.group(street).split(" ")
544 hand.setCommunityCards(street, cards)
546 def readAntes(self, hand):
547 antes = None
548 m = self.re_Antes.finditer(hand.handText)
549 for a in m:
550 if a.groupdict() != antes:
551 antes = a.groupdict()
552 player = self.playerSeatFromPosition("BovadaToFpdb.readAntes", hand.handid, antes["PNAME"])
553 hand.addAnte(player, self.clearMoneyString(antes["ANTE"]))
555 def readBringIn(self, hand):
556 m = self.re_BringIn.search(hand.handText, re.DOTALL)
557 if m:
558 # ~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
559 player = self.playerSeatFromPosition("BovadaToFpdb.readBringIn", hand.handid, m.group("PNAME"))
560 hand.addBringIn(player, self.clearMoneyString(m.group("BRINGIN")))
562 if hand.gametype["sb"] is None and hand.gametype["bb"] is None:
563 hand.gametype["sb"] = "1"
564 hand.gametype["bb"] = "2"
566 def readBlinds(self, hand):
567 # sb, bb,
568 acts, postsb, postbb, both = None, None, None, None # , None, None
569 if not self.re_ReturnBet.search(hand.handText):
570 hand.setUncalledBets(True)
571 for a in self.re_PostSB.finditer(hand.handText):
572 if postsb != a.groupdict():
573 postsb = a.groupdict()
574 player = self.playerSeatFromPosition("BovadaToFpdb.readBlinds.postSB", hand.handid, postsb["PNAME"])
575 hand.addBlind(player, "small blind", self.clearMoneyString(postsb["SB"]))
576 if not hand.gametype["sb"]:
577 hand.gametype["sb"] = self.clearMoneyString(postsb["SB"])
578 # sb = self.clearMoneyString(postsb["SB"])
579 self.allInBlind(hand, "PREFLOP", a, "small blind")
580 for a in self.re_PostBB.finditer(hand.handText):
581 if postbb != a.groupdict():
582 postbb = a.groupdict()
583 player = self.playerSeatFromPosition("BovadaToFpdb.readBlinds.postBB", hand.handid, "Big Blind")
584 hand.addBlind(player, "big blind", self.clearMoneyString(postbb["BB"]))
585 self.allInBlind(hand, "PREFLOP", a, "big blind")
586 if not hand.gametype["bb"]:
587 hand.gametype["bb"] = self.clearMoneyString(postbb["BB"])
588 # bb = self.clearMoneyString(postbb["BB"])
589 if not hand.gametype["currency"]:
590 if postbb["CURRENCY"].find("$") != -1:
591 hand.gametype["currency"] = "USD"
592 elif re.match("^[0-9+]*$", postbb["CURRENCY"]):
593 hand.gametype["currency"] = "play"
594 for a in self.re_Action.finditer(self.re_Hole_Third.split(hand.handText)[0]):
595 if acts != a.groupdict():
596 acts = a.groupdict()
597 if acts["ATYPE"] == " All-in":
598 re_Ante_Plyr = re.compile(
599 r"^"
600 + re.escape(acts["PNAME"])
601 + " (\s?\[ME\]\s)?: Ante chip %(CUR)s(?P<ANTE>[%(NUM)s]+)" % self.substitutions,
602 re.MULTILINE,
603 )
604 m = self.re_Antes.search(hand.handText)
605 m1 = re_Ante_Plyr.search(hand.handText)
606 if not m or m1:
607 player = self.playerSeatFromPosition(
608 "BovadaToFpdb.readBlinds.postBB", hand.handid, acts["PNAME"]
609 )
610 if acts["PNAME"] == "Big Blind":
611 hand.addBlind(player, "big blind", self.clearMoneyString(acts["BET"]))
612 self.allInBlind(hand, "PREFLOP", a, "big blind")
613 elif acts["PNAME"] == "Small Blind" or (acts["PNAME"] == "Dealer" and len(hand.players) == 2):
614 hand.addBlind(player, "small blind", self.clearMoneyString(acts["BET"]))
615 self.allInBlind(hand, "PREFLOP", a, "small blind")
616 elif m:
617 player = self.playerSeatFromPosition("BovadaToFpdb.readAntes", hand.handid, acts["PNAME"])
618 hand.addAnte(player, self.clearMoneyString(acts["BET"]))
619 self.allInBlind(hand, "PREFLOP", a, "antes")
620 self.fixBlinds(hand)
621 for a in self.re_PostBoth.finditer(hand.handText):
622 if both != a.groupdict():
623 both = a.groupdict()
624 player = self.playerSeatFromPosition("BovadaToFpdb.readBlinds.postBoth", hand.handid, both["PNAME"])
625 hand.addBlind(player, "both", self.clearMoneyString(both["SBBB"]))
626 self.allInBlind(hand, "PREFLOP", a, "both")
628 def fixBlinds(self, hand):
629 if hand.gametype["sb"] is None and hand.gametype["bb"] is not None:
630 BB = str(Decimal(hand.gametype["bb"]) * 2)
631 if self.Lim_Blinds.get(BB) is not None:
632 hand.gametype["sb"] = self.Lim_Blinds.get(BB)[0]
633 elif hand.gametype["bb"] is None and hand.gametype["sb"] is not None:
634 for k, v in list(self.Lim_Blinds.items()):
635 if hand.gametype["sb"] == v[0]:
636 hand.gametype["bb"] = v[1]
637 if hand.gametype["sb"] is None or hand.gametype["bb"] is None:
638 log.error(("BovadaToFpdb.fixBlinds: Failed to fix blinds") + " Hand ID: %s" % (hand.handid,))
639 raise FpdbParseError
640 hand.sb = hand.gametype["sb"]
641 hand.bb = hand.gametype["bb"]
643 def readHoleCards(self, hand):
644 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
645 # we need to grab hero's cards
646 for street in ("PREFLOP", "DEAL"):
647 if street in hand.streets.keys():
648 foundDict = None
649 m = self.re_HeroCards.finditer(hand.handText)
650 for found in m:
651 if found.groupdict() != foundDict:
652 foundDict = found.groupdict()
653 hand.hero = "Hero"
654 newcards = found.group("NEWCARDS").split(" ")
655 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
657 for street, text in list(hand.streets.items()):
658 if not text or street in ("PREFLOP", "DEAL"):
659 continue # already done these
660 m = self.re_ShowdownAction.finditer(hand.streets[street])
661 foundDict = None
662 for found in m:
663 if foundDict != found.groupdict():
664 foundDict = found.groupdict()
665 player = self.playerSeatFromPosition(
666 "BovadaToFpdb.readHoleCards", hand.handid, found.group("PNAME")
667 )
668 if street != "SEVENTH" or found.group("HERO"):
669 newcards = found.group("CARDS").split(" ")
670 oldcards = []
671 else:
672 oldcards = found.group("CARDS").split(" ")
673 newcards = []
675 if street == "THIRD" and found.group("HERO"): # hero in stud game
676 hand.hero = "Hero"
677 hand.dealt.add(player) # need this for stud??
678 hand.addHoleCards(
679 street,
680 player,
681 closed=newcards[0:2],
682 open=[newcards[2]],
683 shown=False,
684 mucked=False,
685 dealt=False,
686 )
687 else:
688 hand.addHoleCards(
689 street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False
690 )
692 def readAction(self, hand, street):
693 acts = None
694 m = self.re_Action.finditer(hand.streets[street])
695 for action in m:
696 if acts != action.groupdict():
697 acts = action.groupdict()
698 player = self.playerSeatFromPosition("BovadaToFpdb.readAction", hand.handid, action.group("PNAME"))
699 # print "DEBUG: %s, %s, %s, %s, %s" % (street, acts['PNAME'], player, acts['ATYPE'], action.group('BET'))
700 if (
701 action.group("ATYPE") not in (" Checks", " Fold", " Card dealt to a spot", " Big blind/Bring in")
702 and not hand.allInBlind
703 ):
704 hand.setUncalledBets(False)
705 if action.group("ATYPE") == " Fold":
706 hand.addFold(street, player)
707 elif action.group("ATYPE") == " Checks":
708 hand.addCheck(street, player)
709 elif action.group("ATYPE") == " Calls" or action.group("ATYPE") == " Call":
710 if not action.group("BET"):
711 log.error("BovadaToFpdb.readAction: Amount not found in Call %s" % hand.handid)
712 raise FpdbParseError
713 hand.addCall(street, player, self.clearMoneyString(action.group("BET")))
714 elif action.group("ATYPE") in (" Raises", " raises", " All-in(raise)", " All-in(raise-timeout)"):
715 if action.group("BETTO"):
716 bet = self.clearMoneyString(action.group("BETTO"))
717 elif action.group("BET"):
718 bet = self.clearMoneyString(action.group("BET"))
719 else:
720 log.error("BovadaToFpdb.readAction: Amount not found in Raise %s" % hand.handid)
721 raise FpdbParseError
722 hand.addRaiseTo(street, player, bet)
723 elif action.group("ATYPE") in (" Bets", " bets", " Double bets"):
724 if not action.group("BET"):
725 log.error("BovadaToFpdb.readAction: Amount not found in Bet %s" % hand.handid)
726 raise FpdbParseError
727 hand.addBet(street, player, self.clearMoneyString(action.group("BET")))
728 elif action.group("ATYPE") == " All-in":
729 if not action.group("BET"):
730 log.error("BovadaToFpdb.readAction: Amount not found in All-in %s" % hand.handid)
731 raise FpdbParseError
732 hand.addAllIn(street, player, self.clearMoneyString(action.group("BET")))
733 self.allInBlind(hand, street, action, action.group("ATYPE"))
734 elif action.group("ATYPE") == " Bring_in chip":
735 pass
736 elif action.group("ATYPE") in (" Card dealt to a spot", " Big blind/Bring in"):
737 pass
738 else:
739 log.debug(
740 ("DEBUG:")
741 + " "
742 + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group("PNAME"), action.group("ATYPE"))
743 )
745 def allInBlind(self, hand, street, action, actiontype):
746 if street in ("PREFLOP", "DEAL"):
747 player = self.playerSeatFromPosition("BovadaToFpdb.allInBlind", hand.handid, action.group("PNAME"))
748 if hand.stacks[player] == 0 and not self.re_ReturnBet.search(hand.handText):
749 hand.setUncalledBets(True)
750 hand.allInBlind = True
752 def readShowdownActions(self, hand):
753 # TODO: pick up mucks also??
754 if hand.gametype["base"] in ("hold"):
755 for shows in self.re_ShowdownAction.finditer(hand.handText):
756 cards = shows.group("CARDS").split(" ")
757 player = self.playerSeatFromPosition(
758 "BovadaToFpdb.readShowdownActions", hand.handid, shows.group("PNAME")
759 )
760 hand.addShownCards(cards, player)
762 def readCollectPot(self, hand):
763 if self.re_CollectPot2.search(hand.handText):
764 re_CollectPot = self.re_CollectPot2
765 else:
766 re_CollectPot = self.re_CollectPot1
767 for m in re_CollectPot.finditer(
768 hand.handText.replace(" [ME]", "") if hand.version == "MVS" else hand.handText
769 ): # [ME]
770 collect, pot = m.groupdict(), 0
771 if "POT1" in collect and collect["POT1"] is not None:
772 pot += Decimal(self.clearMoneyString(collect["POT1"]))
773 if "POT2" in collect and collect["POT2"] is not None:
774 pot += Decimal(self.clearMoneyString(collect["POT2"]))
775 if pot > 0:
776 player = self.playerSeatFromPosition("BovadaToFpdb.readCollectPot", hand.handid, collect["PNAME"])
777 hand.addCollectPot(player=player, pot=str(pot))
779 def readShownCards(self, hand):
780 pass
782 def readTourneyResults(self, hand):
783 """Reads knockout bounties and add them to the koCounts dict"""
784 for a in self.re_Bounty.finditer(hand.handText):
785 player = self.playerSeatFromPosition("BovadaToFpdb.readCollectPot", hand.handid, a.group("PNAME"))
786 if player not in hand.koCounts:
787 hand.koCounts[player] = 0
788 hand.koCounts[player] += 1