Coverage for PokerStarsToFpdb.py: 0%
473 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-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
27import re
28import logging
29import datetime
31# PokerStars HH Format
32log = logging.getLogger("parser")
35class PokerStars(HandHistoryConverter):
36 # Class Variables
38 sitename = "PokerStars"
39 filetype = "text"
40 codepage = ("utf8", "cp1252", "ISO-8859-1")
41 siteId = 2 # Needs to match id entry in Sites database
42 sym = {
43 "USD": "\$",
44 "CAD": "\$",
45 "T$": "",
46 "EUR": "\xe2\x82\xac",
47 "GBP": "\£",
48 "play": "",
49 "INR": "\₹",
50 "CNY": "\¥",
51 } # ADD Euro, Sterling, etc HERE
52 substitutions = {
53 "LEGAL_ISO": "USD|EUR|GBP|CAD|FPP|SC|INR|CNY", # legal ISO currency codes
54 "LS": r"\$|\xe2\x82\xac|\u20ac|\£|\u20b9|\¥|Rs\.\s|", # legal currency symbols - Euro(cp1252, utf-8)
55 "PLYR": r"\s?(?P<PNAME>.+?)",
56 "CUR": r"(\$|\xe2\x82\xac|\u20ac||\£|\u20b9|\¥|Rs\.\s|)",
57 "BRKTS": r"(\(button\) |\(small blind\) |\(big blind\) |\(button blind\) |\(button\) \(small blind\) |\(small blind\) \(button\) |\(big blind\) \(button\) |\(small blind/button\) |\(button\) \(big blind\) )?",
58 }
60 # translations from captured groups to fpdb info strings
61 Lim_Blinds = {
62 "0.04": ("0.01", "0.02"),
63 "0.08": ("0.02", "0.04"),
64 "0.10": ("0.02", "0.05"),
65 "0.20": ("0.05", "0.10"),
66 "0.40": ("0.10", "0.20"),
67 "0.50": ("0.10", "0.25"),
68 "1.00": ("0.25", "0.50"),
69 "1": ("0.25", "0.50"),
70 "2.00": ("0.50", "1.00"),
71 "2": ("0.50", "1.00"),
72 "4.00": ("1.00", "2.00"),
73 "4": ("1.00", "2.00"),
74 "6.00": ("1.00", "3.00"),
75 "6": ("1.00", "3.00"),
76 "8.00": ("2.00", "4.00"),
77 "8": ("2.00", "4.00"),
78 "10.00": ("2.00", "5.00"),
79 "10": ("2.00", "5.00"),
80 "16.00": ("4.00", "8.00"),
81 "16": ("4.00", "8.00"),
82 "20.00": ("5.00", "10.00"),
83 "20": ("5.00", "10.00"),
84 "30.00": ("10.00", "15.00"),
85 "30": ("10.00", "15.00"),
86 "40.00": ("10.00", "20.00"),
87 "40": ("10.00", "20.00"),
88 "50.00": ("10.00", "25.00"),
89 "50": ("10.00", "25.00"),
90 "60.00": ("15.00", "30.00"),
91 "60": ("15.00", "30.00"),
92 "80.00": ("20.00", "40.00"),
93 "80": ("20.00", "40.00"),
94 "100.00": ("25.00", "50.00"),
95 "100": ("25.00", "50.00"),
96 "150.00": ("50.00", "75.00"),
97 "150": ("50.00", "75.00"),
98 "200.00": ("50.00", "100.00"),
99 "200": ("50.00", "100.00"),
100 "400.00": ("100.00", "200.00"),
101 "400": ("100.00", "200.00"),
102 "500.00": ("100.00", "250.00"),
103 "500": ("100.00", "250.00"),
104 "600.00": ("150.00", "300.00"),
105 "600": ("150.00", "300.00"),
106 "800.00": ("200.00", "400.00"),
107 "800": ("200.00", "400.00"),
108 "1000.00": ("250.00", "500.00"),
109 "1000": ("250.00", "500.00"),
110 "2000.00": ("500.00", "1000.00"),
111 "2000": ("500.00", "1000.00"),
112 "4000.00": ("1000.00", "2000.00"),
113 "4000": ("1000.00", "2000.00"),
114 "10000.00": ("2500.00", "5000.00"),
115 "10000": ("2500.00", "5000.00"),
116 "20000.00": ("5000.00", "10000.00"),
117 "20000": ("5000.00", "10000.00"),
118 "40000.00": ("10000.00", "20000.00"),
119 "40000": ("10000.00", "20000.00"),
120 }
122 limits = {
123 "No Limit": "nl",
124 "NO LIMIT": "nl",
125 "Pot Limit": "pl",
126 "POT LIMIT": "pl",
127 "Fixed Limit": "fl",
128 "Limit": "fl",
129 "LIMIT": "fl",
130 "Pot Limit Pre-Flop, No Limit Post-Flop": "pn",
131 }
132 games = { # base, category
133 "Hold'em": ("hold", "holdem"),
134 "HOLD'EM": ("hold", "holdem"),
135 "6+ Hold'em": ("hold", "6_holdem"),
136 "Omaha": ("hold", "omahahi"),
137 "Fusion": ("hold", "fusion"),
138 "OMAHA": ("hold", "omahahi"),
139 "Omaha Hi/Lo": ("hold", "omahahilo"),
140 "OMAHA HI/LO": ("hold", "omahahilo"),
141 "5 Card Omaha": ("hold", "5_omahahi"),
142 "Omaha 5 Cards": ("hold", "5_omahahi"),
143 "5 Card Omaha Hi/Lo": ("hold", "5_omaha8"),
144 "6 Card Omaha": ("hold", "6_omahahi"),
145 "6 Card Omaha Hi/Lo": ("hold", "6_omaha8"),
146 "Omaha 6 Cards": ("hold", "6_omahahi"),
147 "Courchevel": ("hold", "cour_hi"),
148 "Courchevel Hi/Lo": ("hold", "cour_hilo"),
149 "Razz": ("stud", "razz"),
150 "RAZZ": ("stud", "razz"),
151 "7 Card Stud": ("stud", "studhi"),
152 "7 CARD STUD": ("stud", "studhi"),
153 "7 Card Stud Hi/Lo": ("stud", "studhilo"),
154 "7 CARD STUD HI/LO": ("stud", "studhilo"),
155 "Badugi": ("draw", "badugi"),
156 "Triple Draw 2-7 Lowball": ("draw", "27_3draw"),
157 "Single Draw 2-7 Lowball": ("draw", "27_1draw"),
158 "5 Card Draw": ("draw", "fivedraw"),
159 }
160 mixes = {
161 "HORSE": "horse",
162 "8-Game": "8game",
163 "8-GAME": "8game",
164 "HOSE": "hose",
165 "Mixed PLH/PLO": "plh_plo",
166 "Mixed NLH/PLO": "nlh_plo",
167 "Mixed Omaha H/L": "plo_lo",
168 "Mixed Hold'em": "mholdem",
169 "Mixed Omaha": "momaha",
170 "Triple Stud": "3stud",
171 } # Legal mixed games
172 currencies = {"€": "EUR", "$": "USD", "": "T$", "£": "GBP", "¥": "CNY", "₹": "INR", "Rs. ": "INR"}
174 # Static regexes
175 re_GameInfo = re.compile(
176 """
177 (?P<SITE>PokerStars|POKERSTARS|Hive\sPoker|Full\sTilt|PokerMaster|Run\sIt\sOnce\sPoker|BetOnline|PokerBros|MPLPoker|SupremaPoker)(?P<TITLE>\sGame|\sHand|\sHome\sGame|\sHome\sGame\sHand|Game|\s(Zoom|Rush)\sHand|\sGAME)\s\#(?P<HID>[0-9]+):\s+
178 (\{.*\}\s+)?((?P<TOUR>((Zoom|Rush)\s)?(Tournament|TOURNAMENT))\s\# # open paren of tournament info
179 (?P<TOURNO>\d+),\s(Table\s\#(?P<HIVETABLE>\d+),\s)?
180 # here's how I plan to use LS
181 (?P<BUYIN>(?P<BIAMT>[%(LS)s\d\.]+)?\+?(?P<BIRAKE>[%(LS)s\d\.]+)?\+?(?P<BOUNTY>[%(LS)s\d\.]+)?\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?|Freeroll|)(\s+)?(-\s)?
182 (\s.+?,)?
183 )?
184 # close paren of tournament info
185 (?P<MIXED>HORSE|8\-Game|8\-GAME|HOSE|Mixed\sOmaha\sH/L|Mixed\sHold\'em|Mixed\sPLH/PLO|Mixed\sNLH/PLO|Mixed\sOmaha|Triple\sStud)?\s?\(?
186 (?P<SPLIT>Split)?\s?
187 (?P<GAME>Hold\'em|HOLD\'EM|Hold\'em|6\+\sHold\'em|Razz|RAZZ|Fusion|7\sCard\sStud|7\sCARD\sSTUD|7\sCard\sStud\sHi/Lo|7\sCARD\sSTUD\sHI/LO|Omaha|OMAHA|Omaha\sHi/Lo|OMAHA\sHI/LO|Badugi|Triple\sDraw\s2\-7\sLowball|Single\sDraw\s2\-7\sLowball|5\sCard\sDraw|(5|6)\sCard\sOmaha(\sHi/Lo)?|Omaha\s(5|6)\sCards|Courchevel(\sHi/Lo)?)\s
188 (?P<LIMIT>No\sLimit|NO\sLIMIT|Fixed\sLimit|Limit|LIMIT|Pot\sLimit|POT\sLIMIT|Pot\sLimit\sPre\-Flop,\sNo\sLimit\sPost\-Flop)\)?,?\s
189 (-\s)?
190 (?P<SHOOTOUT>Match.*,\s)?
191 ((Level|LEVEL)\s(?P<LEVEL>[IVXLC\d]+)\s)?
192 \(? # open paren of the stakes
193 (?P<CURRENCY>%(LS)s|)?
194 (ante\s\d+,\s)?
195 ((?P<SB>[.0-9]+)/(%(LS)s)?(?P<BB>[.0-9]+)|Button\sBlind\s(?P<CURRENCY1>%(LS)s|)(?P<BUB>[.0-9]+)\s\-\sAnte\s(%(LS)s)?[.0-9]+\s)
196 (?P<CAP>\s-\s[%(LS)s]?(?P<CAPAMT>[.0-9]+)\sCap\s-\s)? # Optional Cap part
197 \s?(?P<ISO>%(LEGAL_ISO)s)?
198 \) # close paren of the stakes
199 (?P<BLAH2>\s\[(ADM|AAMS)\sID:\s[A-Z0-9]+\])? # AAMS/ADM ID: in .it HH's
200 \s-\s
201 (?P<DATETIME>.*$)
202 """
203 % substitutions,
204 re.MULTILINE | re.VERBOSE,
205 )
207 re_PlayerInfo = re.compile(
208 """
209 \s?Seat\s(?P<SEAT>[0-9]+):\s
210 (?P<PNAME>.*)\s
211 \((%(LS)s)?(?P<CASH>[,.0-9]+)\sin\schips
212 (,\s(%(LS)s)?(?P<BOUNTY>[,.0-9]+)\sbounty)?
213 \)
214 (?P<SITOUT>\sis\ssitting\sout)?"""
215 % substitutions,
216 re.MULTILINE | re.VERBOSE,
217 )
219 re_HandInfo = re.compile(
220 """
221 \s?Table\s(ID\s)?\'(?P<TABLE>.+?)\'(\(\d+\))?\s
222 ((?P<MAX>\d+)-[Mm]ax\s)?
223 (?P<PLAY>\(Play\sMoney\)\s)?
224 (\(Real\sMoney\)\s)?
225 (Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""",
226 re.MULTILINE | re.VERBOSE,
227 )
229 re_Identify = re.compile(
230 "(PokerStars|POKERSTARS|Hive\sPoker|Full\sTilt|PokerMaster|Run\sIt\sOnce\sPoker|BetOnline|PokerBros|MPLPoker|SupremaPoker)(\sGame|\sHand|\sHome\sGame|\sHome\sGame\sHand|Game|\s(Zoom|Rush)\sHand|\sGAME)\s\#\d+:"
231 )
232 re_SplitHands = re.compile("(?:\s?\n){2,}")
233 re_TailSplitHands = re.compile("(\n\n\n+)")
234 re_Button = re.compile("Seat #(?P<BUTTON>\d+) is the button", re.MULTILINE)
235 re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
236 re_Board2 = re.compile(r"\[(?P<C1>\S\S)\] \[(\S\S)?(?P<C2>\S\S) (?P<C3>\S\S)\]")
237 re_DateTime1 = re.compile(
238 """(?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]+)""",
239 re.MULTILINE,
240 )
241 re_DateTime2 = re.compile(
242 """(?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
243 )
244 # revised re including timezone (not currently used):
245 # 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)
247 # These used to be compiled per player, but regression tests say
248 # we don't have to, and it makes life faster.
249 re_PostSB = re.compile(r"%(PLYR)s: posts small blind %(CUR)s(?P<SB>[,.0-9]+)" % substitutions, re.MULTILINE)
250 re_PostBB = re.compile(r"%(PLYR)s: posts big blind %(CUR)s(?P<BB>[,.0-9]+)" % substitutions, re.MULTILINE)
251 re_PostBUB = re.compile(r"%(PLYR)s: posts button blind %(CUR)s(?P<BUB>[,.0-9]+)" % substitutions, re.MULTILINE)
252 re_Antes = re.compile(r"%(PLYR)s: posts the ante %(CUR)s(?P<ANTE>[,.0-9]+)" % substitutions, re.MULTILINE)
253 re_BringIn = re.compile(
254 r"%(PLYR)s: brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[,.0-9]+)" % substitutions, re.MULTILINE
255 )
256 re_PostBoth = re.compile(
257 r"%(PLYR)s: posts small \& big blinds %(CUR)s(?P<SBBB>[,.0-9]+)" % substitutions, re.MULTILINE
258 )
259 re_PostStraddle = re.compile(
260 r"%(PLYR)s: posts straddle %(CUR)s(?P<STRADDLE>[,.0-9]+)" % substitutions, re.MULTILINE
261 )
262 try:
263 re_Action = re.compile(
264 r"""%(PLYR)s:(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat)(\s%(CUR)s(?P<BET>[,.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[,.\d]+))?\s*(and\sis\sall.in)?(and\shas\sreached\sthe\s[%(CUR)s\d\.,]+\scap)?(\son|\scards?)?(\s\(disconnect\))?(\s\[(?P<CARDS>.+?)\])?\s*$"""
265 % substitutions,
266 re.MULTILINE | re.VERBOSE,
267 )
268 except Exception as e:
269 print(f"Error compiling re_Action: {e}")
271 re_ShowdownAction = re.compile(
272 r"%s: (shows|mucks|mucked|showed) \[(?P<CARDS>.*)\]" % substitutions["PLYR"], re.MULTILINE
273 )
274 re_sitsOut = re.compile("%s sits out" % substitutions["PLYR"], re.MULTILINE)
275 # 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)
276 re_CollectPot = re.compile(
277 r"Seat (?P<SEAT>[0-9]+): %(PLYR)s %(BRKTS)s(collected|showed \[.*\] and (won|collected)) \(?%(CUR)s(?P<POT>[,.\d]+)\)?(, mucked| with.*|)"
278 % substitutions,
279 re.MULTILINE,
280 )
281 # Vinsand88 cashed out the hand for $2.19 | Cash Out Fee $0.02
282 re_CollectPot2 = re.compile(
283 r"%(PLYR)s (collected|cashed out the hand for) %(CUR)s(?P<POT>[,.\d]+)" % substitutions, re.MULTILINE
284 )
285 re_CashedOut = re.compile(r"cashed\sout\sthe\shand")
286 re_WinningRankOne = re.compile(
287 r"%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[,\.0-9]+) - congratulations!$" % substitutions,
288 re.MULTILINE,
289 )
290 re_WinningRankOther = re.compile(
291 r"%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place and received %(CUR)s(?P<AMT>[,.0-9]+)\.$"
292 % substitutions,
293 re.MULTILINE,
294 )
295 re_RankOther = re.compile(
296 r"%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place$" % substitutions, re.MULTILINE
297 )
298 re_Cancelled = re.compile("Hand\scancelled", re.MULTILINE)
299 re_Uncalled = re.compile(
300 "Uncalled bet \(%(CUR)s(?P<BET>[,.\d]+)\) returned to %(PLYR)s$" % substitutions, re.MULTILINE
301 )
302 re_EmptyCard = re.compile("\[\]", re.MULTILINE)
303 # APTEM-89 wins the $0.27 bounty for eliminating Hero
304 # ChazDazzle wins the 22000 bounty for eliminating berkovich609
305 # JKuzja, vecenta split the $50 bounty for eliminating ODYSSES
306 re_Bounty = re.compile(
307 r"%(PLYR)s (?P<SPLIT>split|wins) the %(CUR)s(?P<AMT>[,\.0-9]+) bounty for eliminating (?P<ELIMINATED>.+?)$"
308 % substitutions,
309 re.MULTILINE,
310 )
311 # Amsterdam71 wins $19.90 for eliminating MuKoJla and their own bounty increases by $19.89 to $155.32
312 # Amsterdam71 wins $4.60 for splitting the elimination of Frimble11 and their own bounty increases by $4.59 to $41.32
313 # Amsterdam71 wins the tournament and receives $230.36 - congratulations!
314 re_Progressive = re.compile(
315 r"""
316 %(PLYR)s\swins\s%(CUR)s(?P<AMT>[,\.0-9]+)\s
317 for\s(splitting\sthe\selimination\sof|eliminating)\s(?P<ELIMINATED>.+?)\s
318 and\stheir\sown\sbounty\sincreases\sby\s%(CUR)s(?P<INCREASE>[\.0-9]+)\sto\s%(CUR)s(?P<ENDAMT>[\.0-9]+)$"""
319 % substitutions,
320 re.MULTILINE | re.VERBOSE,
321 )
322 re_Rake = re.compile(
323 r"""
324 Total\spot\s%(CUR)s(?P<POT>[,\.0-9]+)(.+?)?\s\|\sRake\s%(CUR)s(?P<RAKE>[,\.0-9]+)"""
325 % substitutions,
326 re.MULTILINE | re.VERBOSE,
327 )
329 re_STP = re.compile(
330 r"""
331 STP\sadded:\s%(CUR)s(?P<AMOUNT>[,\.0-9]+)"""
332 % substitutions,
333 re.MULTILINE | re.VERBOSE,
334 )
336 def compilePlayerRegexs(self, hand):
337 players = set([player[1] for player in hand.players])
338 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
339 self.compiledPlayers = players
340 player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
341 subst = {
342 "PLYR": player_re,
343 "BRKTS": r"(\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) |\(button\) \(big blind\) )?",
344 "CUR": "(\$|\xe2\x82\xac|\u20ac||\£|)",
345 }
346 if self.siteId == 26:
347 self.re_HeroCards = re.compile(
348 r"Dealt\sto\s(?P<PNAME>(?![A-Z][a-z]+\s[A-Z]).+?)(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])"
349 % subst,
350 re.MULTILINE,
351 )
352 self.re_ShownCards = re.compile(
353 "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(lost|won\s%(CUR)s[\.\d]+\swith\s(?P<STRING2>.*)))?)?$"
354 % subst,
355 re.MULTILINE,
356 )
357 else:
358 self.re_HeroCards = re.compile(
359 r"Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE
360 )
361 self.re_ShownCards = re.compile(
362 "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>.*))?)?$"
363 % subst,
364 re.MULTILINE,
365 )
367 def readSupportedGames(self):
368 return [
369 ["ring", "hold", "nl"],
370 ["ring", "hold", "pl"],
371 ["ring", "hold", "fl"],
372 ["ring", "hold", "pn"],
373 ["ring", "stud", "fl"],
374 ["ring", "draw", "fl"],
375 ["ring", "draw", "pl"],
376 ["ring", "draw", "nl"],
377 ["tour", "hold", "nl"],
378 ["tour", "hold", "pl"],
379 ["tour", "hold", "fl"],
380 ["tour", "hold", "pn"],
381 ["tour", "stud", "fl"],
382 ["tour", "draw", "fl"],
383 ["tour", "draw", "pl"],
384 ["tour", "draw", "nl"],
385 ]
387 def determineGameType(self, handText):
388 info = {}
389 m = self.re_GameInfo.search(handText)
390 if not m:
391 tmp = handText[0:200]
392 log.error(("PokerStarsToFpdb.determineGameType: '%s'") % tmp)
393 raise FpdbParseError
395 mg = m.groupdict()
396 if "LIMIT" in mg:
397 info["limitType"] = self.limits[mg["LIMIT"]]
398 if "GAME" in mg:
399 (info["base"], info["category"]) = self.games[mg["GAME"]]
400 if "SB" in mg and mg["SB"] is not None:
401 info["sb"] = mg["SB"]
402 if "BB" in mg and mg["BB"] is not None:
403 info["bb"] = mg["BB"]
404 if "BUB" in mg and mg["BUB"] is not None:
405 info["sb"] = "0"
406 info["bb"] = mg["BUB"]
407 if "CURRENCY1" in mg and mg["CURRENCY1"] is not None:
408 info["currency"] = self.currencies[mg["CURRENCY1"]]
409 elif "CURRENCY" in mg:
410 info["currency"] = self.currencies[mg["CURRENCY"]]
411 if "MIXED" in mg:
412 if mg["MIXED"] is not None:
413 info["mix"] = self.mixes[mg["MIXED"]]
414 if "Zoom" in mg["TITLE"] or "Rush" in mg["TITLE"]:
415 info["fast"] = True
416 else:
417 info["fast"] = False
418 if "Home" in mg["TITLE"]:
419 info["homeGame"] = True
420 else:
421 info["homeGame"] = False
422 if "CAP" in mg and mg["CAP"] is not None:
423 info["buyinType"] = "cap"
424 else:
425 info["buyinType"] = "regular"
426 if "SPLIT" in mg and mg["SPLIT"] == "Split":
427 info["split"] = True
428 else:
429 info["split"] = False
430 if "SITE" in mg:
431 if mg["SITE"] == "PokerMaster":
432 self.sitename = "PokerMaster"
433 self.siteId = 25
434 m1 = self.re_HandInfo.search(handText, re.DOTALL)
435 if m1 and "_5Cards_" in m1.group("TABLE"):
436 info["category"] = "5_omahahi"
437 elif mg["SITE"] == "Run It Once Poker":
438 self.sitename = "Run It Once Poker"
439 self.siteId = 26
440 elif mg["SITE"] == "BetOnline":
441 self.sitename = "BetOnline"
442 self.siteId = 19
443 elif mg["SITE"] == "PokerBros":
444 self.sitename = "PokerBros"
445 self.siteId = 29
447 if "TOURNO" in mg and mg["TOURNO"] is None:
448 info["type"] = "ring"
449 else:
450 info["type"] = "tour"
451 if "ZOOM" in mg["TOUR"]:
452 info["fast"] = True
454 if info.get("currency") in ("T$", None) and info["type"] == "ring":
455 info["currency"] = "play"
457 if info["limitType"] == "fl" and info["bb"] is not None:
458 if info["type"] == "ring":
459 try:
460 info["sb"] = self.Lim_Blinds[mg["BB"]][0]
461 info["bb"] = self.Lim_Blinds[mg["BB"]][1]
462 except KeyError:
463 tmp = handText[0:200]
464 log.error(
465 ("PokerStarsToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'")
466 % (mg["BB"], tmp)
467 )
468 raise FpdbParseError
469 else:
470 info["sb"] = str((float(mg["SB"]) / 2).quantize(float("0.01")))
471 info["bb"] = str(float(mg["SB"]).quantize(float("0.01")))
473 return info
475 def readHandInfo(self, hand):
476 # First check if partial
477 if hand.handText.count("*** SUMMARY ***") != 1:
478 raise FpdbHandPartial(("Hand is not cleanly split into pre and post Summary"))
480 info = {}
481 m = self.re_HandInfo.search(hand.handText, re.DOTALL)
482 m2 = self.re_GameInfo.search(hand.handText)
483 if m is None or m2 is None:
484 tmp = hand.handText[0:200]
485 log.error(("PokerStarsToFpdb.readHandInfo: '%s'") % tmp)
486 raise FpdbParseError
488 info.update(m.groupdict())
489 info.update(m2.groupdict())
491 # log.debug("readHandInfo: %s" % info)
492 for key in info:
493 if key == "DATETIME":
494 # 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET] # (both dates are parsed so ET date overrides the other)
495 # 2008/08/17 - 01:14:43 (ET)
496 # 2008/09/07 06:23:14 ET
497 datetimestr = "2000/01/01 00:00:00" # default used if time not found
498 if self.siteId == 26:
499 m2 = self.re_DateTime2.finditer(info[key])
500 for a in m2:
501 datetimestr = "%s/%s/%s %s:%s:%s" % (
502 a.group("Y"),
503 a.group("M"),
504 a.group("D"),
505 a.group("H"),
506 a.group("MIN"),
507 "00",
508 )
509 # tz = a.group('TZ') # just assume ET??
510 # print " tz = ", tz, " datetime =", datetimestr
511 hand.startTime = datetime.datetime.strptime(
512 datetimestr, "%Y/%m/%d %H:%M:%S"
513 ) # also timezone at end, e.g. " ET"
514 else:
515 m1 = self.re_DateTime1.finditer(info[key])
516 for a in m1:
517 datetimestr = "%s/%s/%s %s:%s:%s" % (
518 a.group("Y"),
519 a.group("M"),
520 a.group("D"),
521 a.group("H"),
522 a.group("MIN"),
523 a.group("S"),
524 )
525 # tz = a.group('TZ') # just assume ET??
526 # print " tz = ", tz, " datetime =", datetimestr
527 hand.startTime = datetime.datetime.strptime(
528 datetimestr, "%Y/%m/%d %H:%M:%S"
529 ) # also timezone at end, e.g. " ET"
530 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC")
532 if key == "HID":
533 hand.handid = info[key]
534 if key == "TOURNO" and info[key] is not None:
535 hand.tourNo = info[key][-18:]
536 if key == "BUYIN":
537 if hand.tourNo is not None:
538 # print "DEBUG: info['BUYIN']: %s" % info['BUYIN']
539 # print "DEBUG: info['BIAMT']: %s" % info['BIAMT']
540 # print "DEBUG: info['BIRAKE']: %s" % info['BIRAKE']
541 # print "DEBUG: info['BOUNTY']: %s" % info['BOUNTY']
542 if info[key].strip() == "Freeroll":
543 hand.buyin = 0
544 hand.fee = 0
545 hand.buyinCurrency = "FREE"
546 elif info[key].strip() == "":
547 hand.buyin = 0
548 hand.fee = 0
549 hand.buyinCurrency = "NA"
550 else:
551 if info[key].find("$") != -1:
552 hand.buyinCurrency = "USD"
553 elif info[key].find("£") != -1:
554 hand.buyinCurrency = "GBP"
555 elif info[key].find("€") != -1:
556 hand.buyinCurrency = "EUR"
557 elif info[key].find("₹") != -1:
558 hand.buyinCurrency = "INR"
559 elif info[key].find("Rs. ") != -1:
560 hand.buyinCurrency = "INR"
561 elif info[key].find("¥") != -1:
562 hand.buyinCurrency = "CNY"
563 elif info[key].find("FPP") != -1:
564 hand.buyinCurrency = "PSFP"
565 elif info[key].find("SC") != -1:
566 hand.buyinCurrency = "PSFP"
567 elif re.match("[0-9+]*$", info[key].strip()):
568 hand.buyinCurrency = "play"
569 else:
570 # FIXME: handle other currencies, play money
571 log.error(
572 ("PokerStarsToFpdb.readHandInfo: Failed to detect currency.")
573 + " Hand ID: %s: '%s'" % (hand.handid, info[key])
574 )
575 raise FpdbParseError
577 info["BIAMT"] = info["BIAMT"].strip("$€£FPPSC₹")
579 if hand.buyinCurrency != "PSFP":
580 if info["BOUNTY"] is not None:
581 # There is a bounty, Which means we need to switch BOUNTY and BIRAKE values
582 tmp = info["BOUNTY"]
583 info["BOUNTY"] = info["BIRAKE"]
584 info["BIRAKE"] = tmp
585 info["BOUNTY"] = info["BOUNTY"].strip("$€£₹") # Strip here where it isn't 'None'
586 hand.koBounty = int(100 * float(info["BOUNTY"]))
587 hand.isKO = True
588 else:
589 hand.isKO = False
591 info["BIRAKE"] = info["BIRAKE"].strip("$€£₹")
593 hand.buyin = int(100 * float(info["BIAMT"])) + hand.koBounty
594 hand.fee = int(100 * float(info["BIRAKE"]))
595 else:
596 hand.buyin = int(100 * float(info["BIAMT"]))
597 hand.fee = 0
598 if "Zoom" in info["TITLE"] or "Rush" in info["TITLE"]:
599 hand.isFast = True
600 else:
601 hand.isFast = False
602 if "Home" in info["TITLE"]:
603 hand.isHomeGame = True
604 else:
605 hand.isHomeGame = False
606 if key == "LEVEL":
607 hand.level = info[key]
608 if key == "SHOOTOUT" and info[key] is not None:
609 hand.isShootout = True
610 if key == "TABLE":
611 tablesplit = re.split(" ", info[key])
612 if info["TOURNO"] is not None and info["HIVETABLE"] is not None:
613 hand.tablename = info["HIVETABLE"]
614 elif hand.tourNo is not None and len(tablesplit) > 1:
615 hand.tablename = tablesplit[1]
616 else:
617 hand.tablename = info[key]
618 if key == "BUTTON":
619 hand.buttonpos = info[key]
620 if key == "MAX" and info[key] is not None:
621 hand.maxseats = int(info[key])
623 if "Zoom" in self.in_path or "Rush" in self.in_path:
624 (hand.gametype["fast"], hand.isFast) = (True, True)
626 if self.re_Cancelled.search(hand.handText):
627 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid)
629 def readButton(self, hand):
630 m = self.re_Button.search(hand.handText)
631 if m:
632 hand.buttonpos = int(m.group("BUTTON"))
633 else:
634 log.info("readButton: " + ("not found"))
636 def readPlayerStacks(self, hand):
637 pre, post = hand.handText.split("*** SUMMARY ***")
638 m = self.re_PlayerInfo.finditer(pre)
639 for a in m:
640 hand.addPlayer(
641 int(a.group("SEAT")),
642 a.group("PNAME"),
643 self.clearMoneyString(a.group("CASH")),
644 None,
645 a.group("SITOUT"),
646 self.clearMoneyString(a.group("BOUNTY")),
647 )
649 def markStreets(self, hand):
650 # There is no marker between deal and draw in Stars single draw games
651 # this upsets the accounting, incorrectly sets handsPlayers.cardxx and
652 # in consequence the mucked-display is incorrect.
653 # Attempt to fix by inserting a DRAW marker into the hand text attribute
655 if hand.gametype["category"] in ("27_1draw", "fivedraw"):
656 # isolate the first discard/stand pat line (thanks Carl for the regex)
657 discard_split = re.split(r"(?:(.+(?: stands pat|: discards).+))", hand.handText, re.DOTALL)
658 if len(hand.handText) == len(discard_split[0]):
659 # handText was not split, no DRAW street occurred
660 pass
661 else:
662 # DRAW street found, reassemble, with DRAW marker added
663 discard_split[0] += "*** DRAW ***\r\n"
664 hand.handText = ""
665 for i in discard_split:
666 hand.handText += i
668 # PREFLOP = ** Dealing down cards **
669 # This re fails if, say, river is missing; then we don't get the ** that starts the river.
670 if hand.gametype["split"]:
671 m = re.search(
672 r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FIRST\sFLOP \*\*\*)|.+)"
673 r"(\*\*\* FIRST FLOP \*\*\* (?P<FLOP1>\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* SECOND\sFLOP \*\*\*)|.+))?"
674 r"(\*\*\* SECOND FLOP \*\*\* (?P<FLOP2>\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* FIRST\sTURN \*\*\*)|.+))?"
675 r"(\*\*\* FIRST TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN1>\[\S\S\].+(?=\*\*\* SECOND TURN \*\*\*)|.+))?"
676 r"(\*\*\* SECOND TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN2>\[\S\S\].+(?=\*\*\* FIRST RIVER \*\*\*)|.+))?"
677 r"(\*\*\* FIRST RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER1>\[\S\S\].+?(?=\*\*\* SECOND RIVER \*\*\*)|.+))?"
678 r"(\*\*\* SECOND RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?",
679 hand.handText,
680 re.DOTALL,
681 )
682 elif hand.gametype["base"] in ("hold"):
683 if self.siteId == 19:
684 m = re.search(
685 r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>(.+(?P<FLOPET>\[\S\S\]))?.+(?=\*\*\* FLOP \*\*\*)|.+)"
686 r"(\*\*\* FLOP \*\*\*(?P<FLOP> (\[\S\S\] )?\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
687 r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S\] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
688 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S\]? \[?\S\S\] (?P<RIVER>\[\S\S\].+))?",
689 hand.handText,
690 re.DOTALL,
691 )
692 else:
693 m = re.search(
694 r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>(.+(?P<FLOPET>\[\S\S\]))?.+(?=\*\*\* (FIRST\s)?FLOP \*\*\*)|.+)"
695 r"(\*\*\* FLOP \*\*\*(?P<FLOP> (\[\S\S\] )?\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* (FIRST\s)?TURN \*\*\*)|.+))?"
696 r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* (FIRST\s)?RIVER \*\*\*)|.+))?"
697 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?"
698 r"(\*\*\* FIRST FLOP \*\*\*(?P<FLOP1> (\[\S\S\] )?\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* FIRST TURN \*\*\*)|.+))?"
699 r"(\*\*\* FIRST TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN1>\[\S\S\].+(?=\*\*\* FIRST RIVER \*\*\*)|.+))?"
700 r"(\*\*\* FIRST RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER1>\[\S\S\].+?(?=\*\*\* SECOND (FLOP|TURN|RIVER) \*\*\*)|.+))?"
701 r"(\*\*\* SECOND FLOP \*\*\*(?P<FLOP2> (\[\S\S\] )?\[\S\S ?\S\S \S\S\].+(?=\*\*\* SECOND TURN \*\*\*)|.+))?"
702 r"(\*\*\* SECOND TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN2>\[\S\S\].+(?=\*\*\* SECOND RIVER \*\*\*)|.+))?"
703 r"(\*\*\* SECOND RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?",
704 hand.handText,
705 re.DOTALL,
706 )
707 elif hand.gametype["base"] in ("stud"):
708 m = re.search(
709 r"(?P<ANTES>.+(?=\*\*\* 3rd STREET \*\*\*)|.+)"
710 r"(\*\*\* 3rd STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4th STREET \*\*\*)|.+))?"
711 r"(\*\*\* 4th STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5th STREET \*\*\*)|.+))?"
712 r"(\*\*\* 5th STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6th STREET \*\*\*)|.+))?"
713 r"(\*\*\* 6th STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* RIVER \*\*\*)|.+))?"
714 r"(\*\*\* RIVER \*\*\*(?P<SEVENTH>.+))?",
715 hand.handText,
716 re.DOTALL,
717 )
718 elif hand.gametype["base"] in ("draw"):
719 if hand.gametype["category"] in ("27_1draw", "fivedraw"):
720 m = re.search(
721 r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)"
722 r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* DRAW \*\*\*)|.+))?"
723 r"(\*\*\* DRAW \*\*\*(?P<DRAWONE>.+))?",
724 hand.handText,
725 re.DOTALL,
726 )
727 else:
728 m = re.search(
729 r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)"
730 r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* FIRST DRAW \*\*\*)|.+))?"
731 r"(\*\*\* FIRST DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* SECOND DRAW \*\*\*)|.+))?"
732 r"(\*\*\* SECOND DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* THIRD DRAW \*\*\*)|.+))?"
733 r"(\*\*\* THIRD DRAW \*\*\*(?P<DRAWTHREE>.+))?",
734 hand.handText,
735 re.DOTALL,
736 )
737 print("type", type(m), m)
738 mg = m.groupdict()
739 print("type mg", type(mg), mg)
740 hand.addStreets(m)
742 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
743 if self.re_EmptyCard.search(hand.streets[street]):
744 raise FpdbHandPartial(("Blank community card"))
745 if (
746 street != "FLOPET" or hand.streets.get("FLOP") is None
747 ): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
748 m2 = self.re_Board2.search(hand.streets[street])
749 if m2:
750 hand.setCommunityCards(street, [m2.group("C1"), m2.group("C2"), m2.group("C3")])
751 else:
752 m = self.re_Board.search(hand.streets[street])
753 hand.setCommunityCards(street, m.group("CARDS").split(" "))
754 if street in ("FLOP1", "TURN1", "RIVER1", "FLOP2", "TURN2", "RIVER2"):
755 hand.runItTimes = 2
757 def readSTP(self, hand):
758 # log.debug(_("read Splash the Pot"))
759 m = self.re_STP.search(hand.handText)
760 if m:
761 hand.addSTP(m.group("AMOUNT"))
763 def readAntes(self, hand):
764 log.debug(("reading antes"))
765 m = self.re_Antes.finditer(hand.handText)
766 for player in m:
767 # ~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
768 hand.addAnte(player.group("PNAME"), self.clearMoneyString(player.group("ANTE")))
770 def readBringIn(self, hand):
771 m = self.re_BringIn.search(hand.handText, re.DOTALL)
772 if m:
773 # ~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
774 hand.addBringIn(m.group("PNAME"), self.clearMoneyString(m.group("BRINGIN")))
776 def readBlinds(self, hand):
777 liveBlind = True
778 for a in self.re_PostSB.finditer(hand.handText):
779 if liveBlind:
780 hand.addBlind(a.group("PNAME"), "small blind", self.clearMoneyString(a.group("SB")))
781 liveBlind = False
782 else:
783 names = [p[1] for p in hand.players]
784 if "Big Blind" in names or "Small Blind" in names or "Dealer" in names:
785 hand.addBlind(a.group("PNAME"), "small blind", self.clearMoneyString(a.group("SB")))
786 else:
787 # Post dead blinds as ante
788 hand.addBlind(a.group("PNAME"), "secondsb", self.clearMoneyString(a.group("SB")))
789 for a in self.re_PostBB.finditer(hand.handText):
790 hand.addBlind(a.group("PNAME"), "big blind", self.clearMoneyString(a.group("BB")))
791 for a in self.re_PostBoth.finditer(hand.handText):
792 hand.addBlind(a.group("PNAME"), "both", self.clearMoneyString(a.group("SBBB")))
793 for a in self.re_PostStraddle.finditer(hand.handText):
794 hand.addBlind(a.group("PNAME"), "straddle", self.clearMoneyString(a.group("STRADDLE")))
795 for a in self.re_PostBUB.finditer(hand.handText):
796 hand.addBlind(a.group("PNAME"), "button blind", self.clearMoneyString(a.group("BUB")))
798 def readHoleCards(self, hand):
799 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
800 # we need to grab hero's cards
801 for street in ("PREFLOP", "DEAL"):
802 if street in hand.streets.keys():
803 m = self.re_HeroCards.finditer(hand.streets[street])
804 for found in m:
805 # if m == None:
806 # hand.involved = False
807 # else:
808 hand.hero = found.group("PNAME")
809 if "cards" not in found.group("NEWCARDS"):
810 newcards = found.group("NEWCARDS").split(" ")
811 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
813 for street, text in list(hand.streets.items()):
814 print("text", text)
815 print(list(hand.streets.items()))
816 if not text or street in ("PREFLOP", "DEAL"):
817 continue # already done these
818 m = self.re_HeroCards.finditer(hand.streets[street])
819 for found in m:
820 player = found.group("PNAME")
821 if found.group("NEWCARDS") is None:
822 newcards = []
823 else:
824 newcards = found.group("NEWCARDS").split(" ")
825 if found.group("OLDCARDS") is None:
826 oldcards = []
827 else:
828 oldcards = found.group("OLDCARDS").split(" ")
830 if street == "THIRD" and len(newcards) == 3: # hero in stud game
831 hand.hero = player
832 hand.dealt.add(player) # need this for stud??
833 hand.addHoleCards(
834 street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False
835 )
836 else:
837 hand.addHoleCards(
838 street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False
839 )
841 def readAction(self, hand, street):
842 if hand.gametype["split"] and street in hand.communityStreets:
843 s = street + "2"
844 else:
845 s = street
846 if not hand.streets[s]:
847 return
848 m = self.re_Action.finditer(hand.streets[s])
849 for action in m:
850 # acts = action.groupdict()
851 # log.error("DEBUG: %s acts: %s" % (street, acts))
852 if action.group("ATYPE") == " folds":
853 hand.addFold(street, action.group("PNAME"))
854 elif action.group("ATYPE") == " checks":
855 hand.addCheck(street, action.group("PNAME"))
856 elif action.group("ATYPE") == " calls":
857 hand.addCall(street, action.group("PNAME"), self.clearMoneyString(action.group("BET")))
858 elif action.group("ATYPE") == " raises":
859 if action.group("BETTO") is not None:
860 hand.addRaiseTo(street, action.group("PNAME"), self.clearMoneyString(action.group("BETTO")))
861 elif action.group("BET") is not None:
862 hand.addCallandRaise(street, action.group("PNAME"), self.clearMoneyString(action.group("BET")))
863 elif action.group("ATYPE") == " bets":
864 hand.addBet(street, action.group("PNAME"), self.clearMoneyString(action.group("BET")))
865 elif action.group("ATYPE") == " discards":
866 hand.addDiscard(street, action.group("PNAME"), action.group("BET"), action.group("CARDS"))
867 elif action.group("ATYPE") == " stands pat":
868 hand.addStandsPat(street, action.group("PNAME"), action.group("CARDS"))
869 else:
870 log.debug(
871 ("DEBUG:")
872 + " "
873 + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group("PNAME"), action.group("ATYPE"))
874 )
875 m = self.re_Uncalled.search(hand.streets[s])
876 if m:
877 hand.addUncalled(street, m.group("PNAME"), m.group("BET"))
879 def readShowdownActions(self, hand):
880 # TODO: pick up mucks also??
881 for shows in self.re_ShowdownAction.finditer(hand.handText):
882 cards = shows.group("CARDS").split(" ")
883 hand.addShownCards(cards, shows.group("PNAME"))
885 def readTourneyResults(self, hand):
886 """Reads knockout bounties and add them to the koCounts dict"""
887 if self.re_Bounty.search(hand.handText) is None:
888 koAmounts = {}
889 winner = None
890 # %(PLYR)s wins %(CUR)s(?P<AMT>[\.0-9]+) for eliminating (?P<ELIMINATED>.+?) and their own bounty increases by %(CUR)s(?P<INCREASE>[\.0-9]+) to %(CUR)s(?P<ENDAMT>[\.0-9]+)
891 # re_WinningRankOne = re.compile(u"^%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[\.0-9]+) - congratulations!$" % substitutions, re.MULTILINE)
892 for a in self.re_Progressive.finditer(hand.handText):
893 if a.group("PNAME") not in koAmounts:
894 koAmounts[a.group("PNAME")] = 0
895 koAmounts[a.group("PNAME")] += 100 * float(a.group("AMT"))
896 hand.endBounty[a.group("PNAME")] = 100 * float(a.group("ENDAMT"))
897 hand.isProgressive = True
899 m = self.re_WinningRankOne.search(hand.handText)
900 if m:
901 winner = m.group("PNAME")
903 if hand.koBounty > 0:
904 for pname, amount in list(koAmounts.items()):
905 if pname == winner:
906 # end = amount + hand.endBounty[pname]
907 hand.koCounts[pname] = (amount + hand.endBounty[pname]) / float(hand.koBounty)
908 else:
909 # end = 0
910 hand.koCounts[pname] = amount / float(hand.koBounty)
911 else:
912 for a in self.re_Bounty.finditer(hand.handText):
913 if a.group("SPLIT") == "split":
914 pnames = a.group("PNAME").split(", ")
915 for pname in pnames:
916 if pname not in hand.koCounts:
917 hand.koCounts[pname] = 0
918 hand.koCounts[pname] += 1 / float(len(pnames))
919 else:
920 if a.group("PNAME") not in hand.koCounts:
921 hand.koCounts[a.group("PNAME")] = 0
922 hand.koCounts[a.group("PNAME")] += 1
924 def readCollectPot(self, hand):
925 # Bovada walks are calculated incorrectly in converted PokerStars hands
926 acts, bovadaUncalled_v1, bovadaUncalled_v2, blindsantes, adjustment = (
927 hand.actions.get("PREFLOP"),
928 False,
929 False,
930 0,
931 0,
932 )
933 names = [p[1] for p in hand.players]
934 if "Big Blind" in names or "Small Blind" in names or "Dealer" in names or self.siteId == 26:
935 if acts is not None and len([a for a in acts if a[1] != "folds"]) == 0:
936 m0 = self.re_Uncalled.search(hand.handText)
937 if m0 and float(m0.group("BET")) == float(hand.bb):
938 bovadaUncalled_v2 = True
939 elif m0 is None:
940 bovadaUncalled_v1 = True
941 has_sb = len([a[2] for a in hand.actions.get("BLINDSANTES") if a[1] == "small blind"]) > 0
942 adjustment = (float(hand.bb) - float(hand.sb)) if has_sb else float(hand.bb)
943 blindsantes = sum([a[2] for a in hand.actions.get("BLINDSANTES")])
944 i = 0
945 pre, post = hand.handText.split("*** SUMMARY ***")
946 hand.cashedOut = self.re_CashedOut.search(pre) is not None
947 if hand.runItTimes == 0 and hand.cashedOut is False:
948 for m in self.re_CollectPot.finditer(post):
949 pot = self.clearMoneyString(m.group("POT"))
950 if bovadaUncalled_v1 and float(pot) == (blindsantes + hand.pot.stp):
951 hand.addCollectPot(player=m.group("PNAME"), pot=str(float(pot) - adjustment))
952 elif bovadaUncalled_v2:
953 hand.addCollectPot(player=m.group("PNAME"), pot=str(float(pot) * 2))
954 else:
955 hand.addCollectPot(player=m.group("PNAME"), pot=pot)
956 i += 1
957 if i == 0:
958 for m in self.re_CollectPot2.finditer(pre):
959 pot = self.clearMoneyString(m.group("POT"))
960 if bovadaUncalled_v1 and float(pot) == (blindsantes + hand.pot.stp):
961 hand.addCollectPot(player=m.group("PNAME"), pot=str(float(pot) - adjustment))
962 elif bovadaUncalled_v2:
963 hand.addCollectPot(player=m.group("PNAME"), pot=str(float(pot) * 2))
964 else:
965 hand.addCollectPot(player=m.group("PNAME"), pot=pot)
967 def readShownCards(self, hand):
968 if self.siteId == 26:
969 re_RevealedCards = re.compile(
970 r"Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % self.substitutions,
971 re.MULTILINE,
972 )
973 m = re_RevealedCards.finditer(hand.handText)
974 for found in m:
975 cards = found.group("NEWCARDS").split(" ")
976 hand.addShownCards(cards=cards, player=found.group("PNAME"), shown=True, mucked=False)
978 for m in self.re_ShownCards.finditer(hand.handText):
979 if m.group("CARDS") is not None:
980 cards = m.group("CARDS")
981 cards = cards.split(" ") # needs to be a list, not a set--stud needs the order
982 string = m.group("STRING")
983 if m.group("STRING2"):
984 string += "|" + m.group("STRING2")
986 (shown, mucked) = (False, False)
987 if m.group("SHOWED") == "showed":
988 shown = True
989 elif m.group("SHOWED") == "mucked":
990 mucked = True
992 # print "DEBUG: hand.addShownCards(%s, %s, %s, %s)" %(cards, m.group('PNAME'), shown, mucked)
993 hand.addShownCards(cards=cards, player=m.group("PNAME"), shown=shown, mucked=mucked, string=string)
995 def readSummaryInfo(self, summaryInfoList):
996 """Implement the abstract method from HandHistoryConverter."""
997 # Add the actual implementation here, or use a placeholder if not needed
998 log.info("Reading summary info for PokerStars.")
999 return True
1001 @staticmethod
1002 def getTableTitleRe(type, table_name=None, tournament=None, table_number=None):
1003 "Returns string to search in windows titles"
1004 regex = re.escape(str(table_name))
1005 print("regex cash ", regex)
1006 if type == "tour":
1007 regex = re.escape(str(tournament)) + " (Table|Tisch) " + re.escape(str(table_number))
1008 print("regex tour: ", regex)
1009 log.info(
1010 "Stars.getTableTitleRe: table_name='%s' tournament='%s' table_number='%s'"
1011 % (table_name, tournament, table_number)
1012 )
1013 log.info("Stars.getTableTitleRe: returns: '%s'" % (regex))
1014 print("regex:")
1015 print(regex)
1016 return regex