Coverage for UnibetToFpdb.py: 0%
335 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-14 11:07 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-14 11:07 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Copyright 2008-2011, Carl Gherardi
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# Unibet HH Format
33log = logging.getLogger("parser")
36class Unibet(HandHistoryConverter):
37 # Class Variables
39 sitename = "Unibet"
40 filetype = "text"
41 codepage = ("utf8", "cp1252", "ISO-8859-1")
42 siteId = 30 # Needs to match id entry in Sites database
43 sym = {
44 "USD": "\$",
45 "CAD": "\$",
46 "T$": "",
47 "EUR": "\xe2\x82\xac",
48 "GBP": "\£",
49 "play": "",
50 "INR": "\₹",
51 "CNY": "\¥",
52 } # ADD Euro, Sterling, etc HERE
53 substitutions = {
54 "LEGAL_ISO": "USD|EUR|GBP|CAD|FPP|SC|INR|CNY", # legal ISO currency codes
55 "LS": "\$|\xe2\x82\xac|\u20ac|\£|\u20b9|\¥|", # legal currency symbols - Euro(cp1252, utf-8)
56 "PLYR": r"\s?(?P<PNAME>.+?)",
57 "CUR": "(\$|\xe2\x82\xac|\u20ac||\£|\u20b9|\¥|)",
58 "BRKTS": r"(\(button\) |\(small blind\) |\(big blind\) |\(button blind\) |\(button\) \(small blind\) |\(small blind/button\) |\(button\) \(big blind\) )?",
59 }
61 # translations from captured groups to fpdb info strings
62 Lim_Blinds = {
63 "0.04": ("0.01", "0.02"),
64 "0.08": ("0.02", "0.04"),
65 "0.10": ("0.02", "0.05"),
66 "0.20": ("0.05", "0.10"),
67 "0.40": ("0.10", "0.20"),
68 "0.50": ("0.10", "0.25"),
69 "1.00": ("0.25", "0.50"),
70 "1": ("0.25", "0.50"),
71 "2.00": ("0.50", "1.00"),
72 "2": ("0.50", "1.00"),
73 "4.00": ("1.00", "2.00"),
74 "4": ("1.00", "2.00"),
75 "6.00": ("1.00", "3.00"),
76 "6": ("1.00", "3.00"),
77 "8.00": ("2.00", "4.00"),
78 "8": ("2.00", "4.00"),
79 "10.00": ("2.00", "5.00"),
80 "10": ("2.00", "5.00"),
81 "16.00": ("4.00", "8.00"),
82 "16": ("4.00", "8.00"),
83 "20.00": ("5.00", "10.00"),
84 "20": ("5.00", "10.00"),
85 "30.00": ("10.00", "15.00"),
86 "30": ("10.00", "15.00"),
87 "40.00": ("10.00", "20.00"),
88 "40": ("10.00", "20.00"),
89 "50.00": ("10.00", "25.00"),
90 "50": ("10.00", "25.00"),
91 "60.00": ("15.00", "30.00"),
92 "60": ("15.00", "30.00"),
93 "80.00": ("20.00", "40.00"),
94 "80": ("20.00", "40.00"),
95 "100.00": ("25.00", "50.00"),
96 "100": ("25.00", "50.00"),
97 "150.00": ("50.00", "75.00"),
98 "150": ("50.00", "75.00"),
99 "200.00": ("50.00", "100.00"),
100 "200": ("50.00", "100.00"),
101 "400.00": ("100.00", "200.00"),
102 "400": ("100.00", "200.00"),
103 "500.00": ("100.00", "250.00"),
104 "500": ("100.00", "250.00"),
105 "600.00": ("150.00", "300.00"),
106 "600": ("150.00", "300.00"),
107 "800.00": ("200.00", "400.00"),
108 "800": ("200.00", "400.00"),
109 "1000.00": ("250.00", "500.00"),
110 "1000": ("250.00", "500.00"),
111 "2000.00": ("500.00", "1000.00"),
112 "2000": ("500.00", "1000.00"),
113 "4000.00": ("1000.00", "2000.00"),
114 "4000": ("1000.00", "2000.00"),
115 "10000.00": ("2500.00", "5000.00"),
116 "10000": ("2500.00", "5000.00"),
117 "20000.00": ("5000.00", "10000.00"),
118 "20000": ("5000.00", "10000.00"),
119 "40000.00": ("10000.00", "20000.00"),
120 "40000": ("10000.00", "20000.00"),
121 }
123 limits = {"No Limit": "nl", "Pot Limit": "pl", "Fixed Limit": "fl", "Limit": "fl"}
124 games = { # base, category
125 "Hold'em": ("hold", "holdem"),
126 "Omaha": ("hold", "omahahi"),
127 "Omaha Hi/Lo": ("hold", "omahahilo"),
128 }
129 currencies = {"€": "EUR", "$": "USD", "": "T$", "£": "GBP", "¥": "CNY", "₹": "INR"}
131 # Static regexes
132 re_GameInfo = re.compile(
133 """
134 Game\s\#(?P<HID>[0-9]+):\s+Table\s(?P<CURRENCY>€|$|£)[0-9]+\s(?P<LIMIT>PL|NL|FL)\s-\s(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)\s-\s(?P<GAME>Pot\sLimit\sOmaha|No\sLimit\sHold\'Em\sBanzai)\s-\s(?P<DATETIME>.*$)
135 """
136 % substitutions,
137 re.MULTILINE | re.VERBOSE,
138 )
140 re_PlayerInfo = re.compile(
141 """
142 Seat\s(?P<SEAT>[0-9]+):\s(?P<PNAME>\w+)\s\((€|$|£)(?P<CASH>[,.0-9]+)\)"""
143 % substitutions,
144 re.MULTILINE | re.VERBOSE,
145 )
147 re_PlayerInfo2 = re.compile(
148 """
149 (?P<SITOUT>\w+)\s\((€|$|£)[,.0-9]+\)\s\(sitting\sout\)"""
150 % substitutions,
151 re.MULTILINE | re.VERBOSE,
152 )
154 re_HandInfo = re.compile(
155 """
156 (?P<TABLE>\sTable\s(€|$|£)[0-9]+\s(PL|NL|FL))""",
157 re.MULTILINE | re.VERBOSE,
158 )
160 re_Identify = re.compile("Game\s\#\d+:\sTable\s(€|$|£)[0-9]+\s(PL|NL|FL)")
161 re_SplitHands = re.compile("(?:\s?\n){2,}")
162 re_TailSplitHands = re.compile("(\n\n\n+)")
163 re_Button = re.compile("(?P<BUTTON>\w+)\shas\sthe\sbutton", re.MULTILINE)
164 re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
165 re_Board2 = re.compile(r"\[(?P<C1>\S\S)\] \[(\S\S)?(?P<C2>\S\S) (?P<C3>\S\S)\]")
166 re_DateTime1 = re.compile(
167 """(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)\s(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})""",
168 re.MULTILINE,
169 )
170 re_DateTime2 = re.compile(
171 """(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+)""", re.MULTILINE
172 )
173 # revised re including timezone (not currently used):
174 # 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]+) \(?(?P<TZ>[A-Z0-9]+)""", re.MULTILINE)
176 # These used to be compiled per player, but regression tests say
177 # we don't have to, and it makes life faster.
178 re_PostSB = re.compile(r"%(PLYR)s:\sposts\ssmall\sblind\s%(CUR)s(?P<SB>[,.0-9]+)" % substitutions, re.MULTILINE)
179 re_PostBB = re.compile(r"%(PLYR)s:\sposts\sbig\sblind\s%(CUR)s(?P<BB>[,.0-9]+)" % substitutions, re.MULTILINE)
180 re_PostBUB = re.compile(r"%(PLYR)s:\sposts\sbutton\sblind\s%(CUR)s(?P<BUB>[,.0-9]+)" % substitutions, re.MULTILINE)
181 re_Antes = re.compile(r"%(PLYR)s:\sposts\sthe\sant\s%(CUR)s(?P<ANTE>[,.0-9]+)" % substitutions, re.MULTILINE)
182 re_BringIn = re.compile(
183 r"%(PLYR)s:\sbrings[- ]in(\slow|)\sfo/%(CUR)s(?P<BRINGIN>[,.0-9]+)" % substitutions, re.MULTILINE
184 )
185 re_PostBoth = re.compile(
186 r"%(PLYR)s:\sposts\ssmall\s\&\sbig\sblinds\s%(CUR)s(?P<SBBB>[,.0-9]+)" % substitutions, re.MULTILINE
187 )
188 re_PostStraddle = re.compile(
189 r"%(PLYR)s:\sposts\sstraddle\s%(CUR)s(?P<STRADDLE>[,.0-9]+)" % substitutions, re.MULTILINE
190 )
191 re_Action = re.compile(
192 r"""
193 %(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)
194 (\s%(CUR)s(?P<BET>[,.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[,.\d]+))?
195 \s*(and\sis\sall.in)?
196 (and\shas\sreached\sthe\s[%(CUR)s\d\.,]+\scap)?
197 (\son|\scards?)?
198 (\s\(disconnect\))?
199 (\s\[(?P<CARDS>.+?)\])?\s*$"""
200 % substitutions,
201 re.MULTILINE | re.VERBOSE,
202 )
203 re_ShowdownAction = re.compile(r"%s: shows \[(?P<CARDS>.*)\]" % substitutions["PLYR"], re.MULTILINE)
204 re_sitsOut = re.compile("^%s sits out" % substitutions["PLYR"], re.MULTILINE)
205 re_HeroCards = re.compile(
206 r"Dealt\sto\s%(PLYR)s\s(?:\[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % substitutions, re.MULTILINE
207 )
208 # re_ShownCards = re.compile("^Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and (lost|(won|collected) \(%(CUR)s(?P<POT>[.\d]+)\)) with (?P<STRING>.+?)(,\sand\s(won\s\(%(CUR)s[.\d]+\)|lost)\swith\s(?P<STRING2>.*))?)?$" % substitutions, re.MULTILINE)
209 # re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(collected|showed \[.*\] and (won|collected)) \(?%(CUR)s(?P<POT>[,.\d]+)\)?(, mucked| with.*|)" % substitutions, re.MULTILINE)
210 re_CollectPot = re.compile(
211 r"Seat (?P<SEAT>[0-9]+):\s%(PLYR)s:\sbet\s(€|$|£)(?P<BET>[,.\d]+)\sand\swon\s(€|$|£)[\.0-9]+\W\snet\sresult:\s(€|$|£)(?P<POT>[,.\d]+)"
212 % substitutions,
213 re.MULTILINE,
214 )
215 # Vinsand88 cashed out the hand for $2.19 | Cash Out Fee $0.02
216 re_CollectPot2 = re.compile(
217 "%(PLYR)s (collected|cashed out the hand for) %(CUR)s(?P<POT>[,.\d]+)" % substitutions, re.MULTILINE
218 )
219 re_CashedOut = re.compile(r"cashed\sout\sthe\shand")
220 re_WinningRankOne = re.compile(
221 "%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[,\.0-9]+) - congratulations!$" % substitutions,
222 re.MULTILINE,
223 )
224 re_WinningRankOther = re.compile(
225 "%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place and received %(CUR)s(?P<AMT>[,.0-9]+)\.$"
226 % substitutions,
227 re.MULTILINE,
228 )
229 re_RankOther = re.compile(
230 "%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place$" % substitutions, re.MULTILINE
231 )
232 re_Cancelled = re.compile("Hand\scancelled", re.MULTILINE)
233 re_Uncalled = re.compile("Uncalled\sbet\s\(%(CUR)s(?P<BET>[,.\d]+)\)\sreturned\sto" % substitutions, re.MULTILINE)
234 # APTEM-89 wins the $0.27 bounty for eliminating Hero
235 # ChazDazzle wins the 22000 bounty for eliminating berkovich609
236 # JKuzja, vecenta split the $50 bounty for eliminating ODYSSES
237 re_Bounty = re.compile(
238 "%(PLYR)s (?P<SPLIT>split|wins) the %(CUR)s(?P<AMT>[,\.0-9]+) bounty for eliminating (?P<ELIMINATED>.+?)$"
239 % substitutions,
240 re.MULTILINE,
241 )
242 # Amsterdam71 wins $19.90 for eliminating MuKoJla and their own bounty increases by $19.89 to $155.32
243 # Amsterdam71 wins $4.60 for splitting the elimination of Frimble11 and their own bounty increases by $4.59 to $41.32
244 # Amsterdam71 wins the tournament and receives $230.36 - congratulations!
245 re_Progressive = re.compile(
246 """
247 %(PLYR)s\swins\s%(CUR)s(?P<AMT>[,\.0-9]+)\s
248 for\s(splitting\sthe\selimination\sof|eliminating)\s(?P<ELIMINATED>.+?)\s
249 and\stheir\sown\sbounty\sincreases\sby\s%(CUR)s(?P<INCREASE>[\.0-9]+)\sto\s%(CUR)s(?P<ENDAMT>[\.0-9]+)$"""
250 % substitutions,
251 re.MULTILINE | re.VERBOSE,
252 )
253 re_Rake = re.compile(
254 """
255 Total\spot\s%(CUR)s(?P<POT>[,\.0-9]+)(.+?)?\s\|\sRake\s%(CUR)s(?P<RAKE>[,\.0-9]+)"""
256 % substitutions,
257 re.MULTILINE | re.VERBOSE,
258 )
260 re_STP = re.compile(
261 """
262 STP\sadded:\s%(CUR)s(?P<AMOUNT>[,\.0-9]+)"""
263 % substitutions,
264 re.MULTILINE | re.VERBOSE,
265 )
267 def compilePlayerRegexs(self, hand):
268 players = set([player[1] for player in hand.players])
269 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
270 self.compiledPlayers = players
271 player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
272 subst = {
273 "PLYR": player_re,
274 "BRKTS": r"(\(button\) |\(small\sblind\) |\(big\blind\) |\(button\) \(small\sblind\) |\(button\) \(big\sblind\) )?",
275 "CUR": "(\$|\xe2\x82\xac|\u20ac||\£|)",
276 }
278 self.re_HeroCards = re.compile(
279 r"Dealt\sto\s%(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE
280 )
281 self.re_ShownCards = re.compile(
282 "Seat\s(?P<SEAT>[0-9]+):\s%(PLYR)s\s%(BRKTS)s(?P<SHOWED>showed|mucked)\s\[(?P<CARDS>.*)\](\sand\s(lost|(won|collected)\s \(%(CUR)s(?P<POT>[,\.\d]+)\))\swith\s(?P<STRING>.+?)(,\sand\s(won\s\(%(CUR)s[\.\d]+\)|lost)\swith\s(?P<STRING2>.*))?)?$"
283 % subst,
284 re.MULTILINE,
285 )
287 def readSupportedGames(self):
288 return [
289 ["ring", "hold", "nl"],
290 ["ring", "hold", "pl"],
291 ["ring", "hold", "fl"],
292 ["ring", "hold", "pn"],
293 ["ring", "stud", "fl"],
294 ["ring", "draw", "fl"],
295 ["ring", "draw", "pl"],
296 ["ring", "draw", "nl"],
297 ["tour", "hold", "nl"],
298 ["tour", "hold", "pl"],
299 ["tour", "hold", "fl"],
300 ["tour", "hold", "pn"],
301 ["tour", "stud", "fl"],
302 ["tour", "draw", "fl"],
303 ["tour", "draw", "pl"],
304 ["tour", "draw", "nl"],
305 ]
307 def determineGameType(self, handText):
308 info = {}
309 m = self.re_GameInfo.search(handText)
310 if not m:
311 tmp = handText[0:200]
312 log.error(("UnibetToFpdb.determineGameType: '%s'") % tmp)
313 raise FpdbParseError
315 mg = m.groupdict()
316 if "LIMIT" in mg:
317 # print(mg['LIMIT'])
318 if mg["LIMIT"] == "NL":
319 info["limitType"] = self.limits["No Limit"]
320 elif mg["LIMIT"] == "PL":
321 info["limitType"] = self.limits["Pot Limit"]
323 # info['limitType'] = self.limits[mg['LIMIT']]
324 if "GAME" in mg:
325 print(mg["GAME"])
326 if mg["GAME"] == "No Limit Hold'Em Banzai":
327 info["base"] = "hold"
328 info["category"] = "holdem"
329 info["type"] = "ring"
330 info["split"] = False
331 elif mg["GAME"] == "Pot Limit Omaha":
332 info["base"] = "hold"
333 info["category"] = "omahahi"
334 info["type"] = "ring"
335 info["split"] = False
336 # (info['base'], info['category']) = self.games[mg['GAME']]
337 if "SB" in mg and mg["SB"] is not None:
338 info["sb"] = mg["SB"]
339 if "BB" in mg and mg["BB"] is not None:
340 info["bb"] = mg["BB"]
341 if "BUB" in mg and mg["BUB"] is not None:
342 info["sb"] = "0"
343 info["bb"] = mg["BUB"]
344 if "CURRENCY1" in mg and mg["CURRENCY1"] is not None:
345 info["currency"] = self.currencies[mg["CURRENCY1"]]
346 elif "CURRENCY" in mg:
347 info["currency"] = self.currencies[mg["CURRENCY"]]
349 # if 'Zoom' in mg['TITLE'] or 'Rush' in mg['TITLE']:
350 # info['fast'] = True
351 # else:
352 # info['fast'] = False
353 # if 'Home' in mg['TITLE']:
354 # info['homeGame'] = True
355 # else:
356 # info['homeGame'] = False
357 # if 'CAP' in mg and mg['CAP'] is not None:
358 # info['buyinType'] = 'cap'
359 # else:
360 # info['buyinType'] = 'regular'
361 # if 'SPLIT' in mg and mg['SPLIT'] == 'Split':
362 # info['split'] = True
363 # else:
364 # info['split'] = False
365 # if 'SITE' in mg:
366 # if mg['SITE'] == 'PokerMaster':
367 # self.sitename = "PokerMaster"
368 # self.siteId = 25
369 # m1 = self.re_HandInfo.search(handText,re.DOTALL)
370 # if m1 and '_5Cards_' in m1.group('TABLE'):
371 # info['category'] = '5_omahahi'
372 # elif mg['SITE'] == 'Run It Once Poker':
373 # self.sitename = "Run It Once Poker"
374 # self.siteId = 26
375 # elif mg['SITE'] == 'BetOnline':
376 # self.sitename = 'BetOnline'
377 # self.siteId = 19
378 # elif mg['SITE'] == 'PokerBros':
379 # self.sitename = 'PokerBros'
380 # self.siteId = 29
382 # if 'TOURNO' in mg and mg['TOURNO'] is None:
383 # info['type'] = 'ring'
384 # else:
385 # info['type'] = 'tour'
386 # if 'ZOOM' in mg['TOUR']:
387 # info['fast'] = True
389 if info.get("currency") in ("T$", None) and info["type"] == "ring":
390 info["currency"] = "play"
392 if info["limitType"] == "fl" and info["bb"] is not None:
393 if info["type"] == "ring":
394 try:
395 info["sb"] = self.Lim_Blinds[mg["BB"]][0]
396 info["bb"] = self.Lim_Blinds[mg["BB"]][1]
397 except KeyError:
398 tmp = handText[0:200]
399 log.error(
400 ("UnibetToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (mg["BB"], tmp)
401 )
402 raise FpdbParseError
403 else:
404 info["sb"] = str((Decimal(mg["SB"]) / 2).quantize(Decimal("0.01")))
405 info["bb"] = str(Decimal(mg["SB"]).quantize(Decimal("0.01")))
406 log.info(("UnibetToFpdb.determineGameType: '%s'") % info)
407 return info
409 def readHandInfo(self, hand):
410 # First check if partial
411 if hand.handText.count("*** Summary ***") != 1:
412 raise FpdbHandPartial(("Hand is not cleanly split into pre and post Summary"))
414 info = {}
415 m = self.re_HandInfo.search(hand.handText, re.DOTALL)
416 m2 = self.re_GameInfo.search(hand.handText)
417 if m is None or m2 is None:
418 tmp = hand.handText[0:200]
419 log.error(("UnibetToFpdb.readHandInfo: '%s'") % tmp)
420 raise FpdbParseError
422 info.update(m.groupdict())
423 info.update(m2.groupdict())
425 log.debug("readHandInfo: %s" % info)
426 for key in info:
427 if key == "DATETIME":
428 # 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET] # (both dates are parsed so ET date overrides the other)
429 # 2008/08/17 - 01:14:43 (ET)
430 # 2008/09/07 06:23:14 ET
431 datetimestr = "00:00:00 2000/01/01" # default used if time not found
432 if self.siteId == 26:
433 m2 = self.re_DateTime2.finditer(info[key])
435 else:
436 m1 = self.re_DateTime1.finditer(info[key])
437 for a in m1:
438 datetimestr1 = str(a.group("H")) + ":" + str(a.group("MIN")) + ":" + str(a.group("S"))
439 datetimestr2 = str(a.group("Y")) + "/" + str(a.group("M")) + "/" + str(a.group("D"))
440 datetimestr = datetimestr2 + " " + datetimestr1
441 print("datetimestr", datetimestr)
442 # tz = a.group('TZ') # just assume ET??
443 # print (" tz = ", tz, " datetime =", datetimestr)
444 hand.startTime = datetime.datetime.strptime(
445 datetimestr, "%Y/%m/%d %H:%M:%S"
446 ) # also timezone at end, e.g. " ET"
447 # hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC")
449 if key == "HID":
450 hand.handid = info[key]
451 if key == "TOURNO":
452 hand.tourNo = info[key]
453 if key == "BUYIN":
454 if hand.tourNo is not None:
455 print("DEBUG: info['BUYIN']: %s" % info["BUYIN"])
456 print("DEBUG: info['BIAMT']: %s" % info["BIAMT"])
457 print("DEBUG: info['BIRAKE']: %s" % info["BIRAKE"])
458 print("DEBUG: info['BOUNTY']: %s" % info["BOUNTY"])
459 if info[key].strip() == "Freeroll":
460 hand.buyin = 0
461 hand.fee = 0
462 hand.buyinCurrency = "FREE"
463 elif info[key].strip() == "":
464 hand.buyin = 0
465 hand.fee = 0
466 hand.buyinCurrency = "NA"
467 else:
468 if info[key].find("$") != -1:
469 hand.buyinCurrency = "USD"
470 elif info[key].find("£") != -1:
471 hand.buyinCurrency = "GBP"
472 elif info[key].find("€") != -1:
473 hand.buyinCurrency = "EUR"
474 elif info[key].find("₹") != -1:
475 hand.buyinCurrency = "INR"
476 elif info[key].find("¥") != -1:
477 hand.buyinCurrency = "CNY"
478 elif info[key].find("FPP") != -1:
479 hand.buyinCurrency = "PSFP"
480 elif info[key].find("SC") != -1:
481 hand.buyinCurrency = "PSFP"
482 elif re.match("^[0-9+]*$", info[key].strip()):
483 hand.buyinCurrency = "play"
484 else:
485 # FIXME: handle other currencies, play money
486 log.error(
487 ("UnibetToFpdb.readHandInfo: Failed to detect currency.")
488 + " Hand ID: %s: '%s'" % (hand.handid, info[key])
489 )
490 raise FpdbParseError
492 info["BIAMT"] = info["BIAMT"].strip("$€£FPPSC₹")
494 if hand.buyinCurrency != "PSFP":
495 if info["BOUNTY"] is not None:
496 # There is a bounty, Which means we need to switch BOUNTY and BIRAKE values
497 tmp = info["BOUNTY"]
498 info["BOUNTY"] = info["BIRAKE"]
499 info["BIRAKE"] = tmp
500 info["BOUNTY"] = info["BOUNTY"].strip("$€£₹") # Strip here where it isn't 'None'
501 hand.koBounty = int(100 * Decimal(info["BOUNTY"]))
502 hand.isKO = True
503 else:
504 hand.isKO = False
506 info["BIRAKE"] = info["BIRAKE"].strip("$€£₹")
508 hand.buyin = int(100 * Decimal(info["BIAMT"])) + hand.koBounty
509 hand.fee = int(100 * Decimal(info["BIRAKE"]))
510 else:
511 hand.buyin = int(100 * Decimal(info["BIAMT"]))
512 hand.fee = 0
513 if "Zoom" in info["TITLE"] or "Rush" in info["TITLE"]:
514 hand.isFast = True
515 else:
516 hand.isFast = False
517 if "Home" in info["TITLE"]:
518 hand.isHomeGame = True
519 else:
520 hand.isHomeGame = False
521 if key == "LEVEL":
522 hand.level = info[key]
523 if key == "SHOOTOUT" and info[key] is not None:
524 hand.isShootout = True
525 if key == "TABLE":
526 hand.tablename = info[key]
527 # if info['TOURNO'] is not None and info['HIVETABLE'] is not None:
528 # hand.tablename = info['HIVETABLE']
529 # elif hand.tourNo != None and len(tablesplit)>1:
530 # hand.tablename = tablesplit[1]
531 # else:
532 # hand.tablename = info[key]
533 if key == "BUTTON":
534 hand.buttonpos = info[key]
535 if key == "MAX" and info[key] is not None:
536 hand.maxseats = int(info[key])
537 log.info("readHandInfo.hand: %s" % hand)
538 if self.re_Cancelled.search(hand.handText):
539 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid)
541 def readButton(self, hand):
542 pre, post = hand.handText.split("*** Summary ***")
543 m = self.re_Button.search(hand.handText)
544 m2 = self.re_PlayerInfo.finditer(pre)
545 if m:
546 for b in m2:
547 if b.group("PNAME") == m.group("BUTTON"):
548 hand.buttonpos = int(b.group("SEAT"))
549 log.info("readHandInfo.readbutton: %s" % int(b.group("SEAT")))
550 else:
551 log.info("readButton: " + ("not found"))
553 def readPlayerStacks(self, hand):
554 pre, post = hand.handText.split("*** Summary ***")
555 m = self.re_PlayerInfo.finditer(pre)
556 m2 = self.re_PlayerInfo2.finditer(pre)
557 for b in m2:
558 for a in m:
559 if a.group("PNAME") == b.group("SITOUT"):
560 hand.addPlayer(
561 int(a.group("SEAT")),
562 a.group("PNAME"),
563 self.clearMoneyString(a.group("CASH")),
564 None,
565 int(a.group("SEAT")),
566 # self.clearMoneyString(a.group('BOUNTY'))
567 )
568 log.info(
569 "readPlayerStacks: '%s' '%s' '%s' '%s' '%s'" % int(a.group("SEAT")),
570 a.group("PNAME"),
571 self.clearMoneyString(a.group("CASH")),
572 None,
573 int(a.group("SEAT")),
574 )
575 break
576 elif a.group("PNAME") != b.group("SITOUT"):
577 hand.addPlayer(
578 int(a.group("SEAT")),
579 a.group("PNAME"),
580 self.clearMoneyString(a.group("CASH")),
581 None,
582 )
583 log.info(
584 "readPlayerStacks: '%s' '%s' '%s' '%s' '%s'" % int(a.group("SEAT")),
585 a.group("PNAME"),
586 self.clearMoneyString(a.group("CASH")),
587 None,
588 )
590 def markStreets(self, hand):
591 # There is no marker between deal and draw in Stars single draw games
592 # this upsets the accounting, incorrectly sets handsPlayers.cardxx and
593 # in consequence the mucked-display is incorrect.
594 # Attempt to fix by inserting a DRAW marker into the hand text attribute
595 # PREFLOP = ** Dealing down cards **
596 # This re fails if, say, river is missing; then we don't get the ** that starts the river.
597 m = re.search(
598 r"\*\*\*\sHole\scards\s\*\*\*(?P<PREFLOP>(.+(?P<FLOPET>\[\S\S\]))?.+(?=\*\*\*\sFlop\s\*\*\*)|.+)"
599 r"(\*\*\*\sFlop\s\*\*\*(?P<FLOP>(\[\S\S\s])?\[(\S\S?)?\S\S\S\S\].+(?=\*\*\*\sTurn\s\*\*\*)|.+))?"
600 r"(\*\*\*\sTurn\s\*\*\*\s\[\S\S \S\S \S\S\](?P<TURN>\[\S\S\].+(?=\*\*\*\sRiver\s\*\*\*)|.+))?"
601 r"(\*\*\*\sRiver\s\*\*\*\s\[\S\S \S\S \S\S\]?\s\[?\S\S\]\s(?P<RIVER>\[\S\S\].+))?",
602 hand.handText,
603 re.DOTALL,
604 )
605 hand.addStreets(m)
606 log.info("markStreets.handaddstreets: %s" % hand)
608 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
609 m = self.re_Board.search(hand.streets[street])
610 if m:
611 hand.setCommunityCards(street, m.group("CARDS").split(" "))
612 log.info("readCommunityCards.setCommunityCards:' %s' " % street)
613 else:
614 log.error("readCommunityCards.setCommunityCards: none")
616 def readAntes(self, hand):
617 log.debug(("reading antes"))
618 m = self.re_Antes.finditer(hand.handText)
619 for player in m:
620 logging.info("hand.addAnte(%s,%s)" % (player.group("PNAME"), player.group("ANTE")))
621 hand.addAnte(player.group("PNAME"), self.clearMoneyString(player.group("ANTE")))
623 def readBringIn(self, hand):
624 m = self.re_BringIn.search(hand.handText, re.DOTALL)
625 if m:
626 logging.info("readBringIn: %s for %s" % (m.group("PNAME"), m.group("BRINGIN")))
627 hand.addBringIn(m.group("PNAME"), self.clearMoneyString(m.group("BRINGIN")))
629 def readBlinds(self, hand):
630 liveBlind = True
631 for a in self.re_PostSB.finditer(hand.handText):
632 if liveBlind:
633 hand.addBlind(a.group("PNAME"), "small blind", self.clearMoneyString(a.group("SB")))
634 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("SB"))))
635 liveBlind = False
636 else:
637 names = [p[1] for p in hand.players]
638 if "Big Blind" in names or "Small Blind" in names or "Dealer" in names:
639 hand.addBlind(a.group("PNAME"), "small blind", self.clearMoneyString(a.group("SB")))
640 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("SB"))))
641 else:
642 # Post dead blinds as ante
643 hand.addBlind(a.group("PNAME"), "secondsb", self.clearMoneyString(a.group("SB")))
644 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("SB"))))
645 for a in self.re_PostBB.finditer(hand.handText):
646 hand.addBlind(a.group("PNAME"), "big blind", self.clearMoneyString(a.group("BB")))
647 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("BB"))))
648 for a in self.re_PostBoth.finditer(hand.handText):
649 hand.addBlind(a.group("PNAME"), "both", self.clearMoneyString(a.group("SBBB")))
650 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("SBBB"))))
652 for a in self.re_PostStraddle.finditer(hand.handText):
653 hand.addBlind(a.group("PNAME"), "straddle", self.clearMoneyString(a.group("STRADDLE")))
654 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("STRADDLE"))))
655 for a in self.re_PostBUB.finditer(hand.handText):
656 hand.addBlind(a.group("PNAME"), "button blind", self.clearMoneyString(a.group("BUB")))
657 logging.info("readBlinds: '%s' for '%s'" % (a.group("PNAME"), self.clearMoneyString(a.group("BUB"))))
659 def readHoleCards(self, hand):
660 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
661 # we need to grab hero's cards
662 for street in ("PREFLOP", "DEAL"):
663 if street in hand.streets.keys():
664 print(street)
665 m = self.re_HeroCards.finditer(hand.streets[street])
666 print(m)
667 for found in m:
668 # if m == None:
669 # hand.involved = False
670 # else:
671 hand.hero = found.group("PNAME")
672 logging.info("readHoleCards: '%s'" % (found.group("PNAME")))
673 if "cards" not in found.group("NEWCARDS"):
674 newcards = found.group("NEWCARDS").split(" ")
675 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
677 def readAction(self, hand, street):
678 if hand.gametype["split"] and street in hand.communityStreets:
679 s = street + "2"
680 else:
681 s = street
682 if not hand.streets[s]:
683 return
684 m = self.re_Action.finditer(hand.streets[s])
685 for action in m:
686 acts = action.groupdict()
687 log.error("DEBUG: %s acts: %s" % (street, acts))
688 if action.group("ATYPE") == " folds":
689 hand.addFold(street, action.group("PNAME"))
690 elif action.group("ATYPE") == " checks":
691 hand.addCheck(street, action.group("PNAME"))
692 elif action.group("ATYPE") == " calls":
693 hand.addCall(street, action.group("PNAME"), self.clearMoneyString(action.group("BET")))
694 elif action.group("ATYPE") == " raises":
695 if action.group("BETTO") is not None:
696 hand.addRaiseTo(street, action.group("PNAME"), self.clearMoneyString(action.group("BETTO")))
697 elif action.group("BET") is not None:
698 hand.addCallandRaise(street, action.group("PNAME"), self.clearMoneyString(action.group("BET")))
699 elif action.group("ATYPE") == " bets":
700 hand.addBet(street, action.group("PNAME"), self.clearMoneyString(action.group("BET")))
701 elif action.group("ATYPE") == " discards":
702 hand.addDiscard(street, action.group("PNAME"), action.group("BET"), action.group("CARDS"))
703 elif action.group("ATYPE") == " stands pat":
704 hand.addStandsPat(street, action.group("PNAME"), action.group("CARDS"))
705 else:
706 log.info(
707 ("DEBUG:")
708 + " "
709 + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group("PNAME"), action.group("ATYPE"))
710 )
712 def readShowdownActions(self, hand):
713 for shows in self.re_ShowdownAction.finditer(hand.handText):
714 cards = shows.group("CARDS").split(" ")
715 logging.debug("hand.readShowdownActions('%s','%s')" % cards, shows.group("PNAME"))
716 hand.addShownCards(cards, shows.group("PNAME"))
717 logging.info("hand.readShowdownActions('%s','%s')" % cards, shows.group("PNAME"))
719 def readTourneyResults(self, hand):
720 """Reads knockout bounties and add them to the koCounts dict"""
721 pass
723 def readCollectPot(self, hand):
724 hand.setUncalledBets(True)
725 for m in self.re_CollectPot.finditer(hand.handText):
726 hand.addCollectPot(player=m.group("PNAME"), pot=str(Decimal((m.group("POT")))))
727 logging.info("readCollectPot: '%s' for '%s'" % (m.group("PNAME"), str(Decimal((m.group("POT"))))))
729 def readShownCards(self, hand):
730 pass
732 @staticmethod
733 def getTableTitleRe(type, table_name=None, tournament=None, table_number=None):
734 pass