Coverage for WinamaxToFpdb.py: 0%
325 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
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########################################################################
20import sys
21import Exceptions
22# import L10n
23# _ = L10n.get_translation()
24from HandHistoryConverter import *
25from decimal_wrapper import Decimal
26import platform
29# Winamax HH Format
32class Winamax(HandHistoryConverter):
33 def Trace(f):
34 def my_f(*args, **kwds):
35 print(f"entering {f.__name__}")
36 result = f(*args, **kwds)
37 print(f"exiting {f.__name__}")
38 return result
40 my_f.__name = f.__name__
41 my_f.__doc__ = f.__doc__
42 return my_f
44 filter = "Winamax"
45 siteName = "Winamax"
46 filetype = "text"
47 codepage = ("utf8", "cp1252")
48 siteId = 15 # Needs to match id entry in Sites database
50 mixes = {} # Legal mixed games
51 sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": u"\xe2\x82\xac|\u20ac", "GBP": "\xa3", "play": ""}
52 # ADD Euro, Sterling, etc HERE
53 substitutions = {
54 'LEGAL_ISO': "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes
55 'LS': u"\$|\xe2\x82\xac|\u20ac|" # legal currency symbols - Euro(cp1252, utf-8)
56 }
58 limits = {'no limit': 'nl', 'pot limit': 'pl', 'fixed limit': 'fl'}
60 games = { # base, category
61 "Holdem": ('hold', 'holdem'),
62 "Omaha": ('hold', 'omahahi'),
63 "Omaha5": ('hold', '5_omahahi'),
64 "5 Card Omaha": ('hold', '5_omahahi'),
65 "5 Card Omaha Hi/Lo": ('hold', '5_omahahi'), # incorrect in file
66 "Omaha Hi/Lo": ('hold', 'omahahilo'),
67 "Omaha8": ('hold', 'omahahilo'),
68 "7-Card Stud": ('stud', 'studhi'),
69 "7stud": ('stud', 'studhi'),
70 "7-Card Stud Hi/Lo": ('stud', 'studhilo'),
71 "7stud8": ('stud', 'studhilo'),
72 "Razz": ('stud', 'razz'),
73 "2-7 Triple Draw": ('draw', '27_3draw'),
74 "Lowball27": ('draw', '27_3draw')
75 }
76 mixes = {
77 '8games': '8game',
78 '10games': '10game',
79 'horse': 'horse',
80 }
82 # Static regexes
83 # ***** End of hand R5-75443872-57 *****
84 re_Identify = re.compile(u'Winamax\sPoker\s\-\s(CashGame|Go\sFast|HOLD\-UP|Tournament\s\")')
85 re_SplitHands = re.compile(r'\n\n')
88 re_HandInfo = re.compile(u"""
89 \s*Winamax\sPoker\s-\s
90 (?P<RING>(CashGame|Go\sFast\s"[^"]+"|HOLD\-UP\s"[^"]+"))?
91 (?P<TOUR>Tournament\s
92 (?P<TOURNAME>.+)?\s
93 buyIn:\s(?P<BUYIN>(?P<BIAMT>[%(LS)s\d\,.]+)?(\s\+?\s|-)(?P<BIRAKE>[%(LS)s\d\,.]+)?\+?(?P<BOUNTY>[%(LS)s\d\.]+)?\s?(?P<TOUR_ISO>%(LEGAL_ISO)s)?|(?P<FREETICKET>[\sa-zA-Z]+))?\s
94 (level:\s(?P<LEVEL>\d+))?
95 .*)?
96 \s-\sHandId:\s\#(?P<HID1>\d+)-(?P<HID2>\d+)-(?P<HID3>\d+)\s-\s # REB says: HID3 is the correct hand number
97 (?P<GAME>Holdem|Omaha|Omaha5|Omaha8|5\sCard\sOmaha|5\sCard\sOmaha\sHi/Lo|Omaha\sHi/Lo|7\-Card\sStud|7stud|7\-Card\sStud\sHi/Lo|7stud8|Razz|2\-7\sTriple\sDraw|Lowball27)\s
98 (?P<LIMIT>fixed\slimit|no\slimit|pot\slimit)\s
99 \(
100 (((%(LS)s)?(?P<ANTE>[.0-9]+)(%(LS)s)?)/)?
101 ((%(LS)s)?(?P<SB>[.0-9]+)(%(LS)s)?)/
102 ((%(LS)s)?(?P<BB>[.0-9]+)(%(LS)s)?)
103 \)\s-\s
104 (?P<DATETIME>.*)
105 Table:?\s\'(?P<TABLE>[^(]+)
106 (.(?P<TOURNO>\d+).\#(?P<TABLENO>\d+))?.*
107 \'
108 \s(?P<MAXPLAYER>\d+)\-max
109 \s(?P<MONEY>\(real\smoney\))?
110 """ % substitutions, re.MULTILINE | re.DOTALL | re.VERBOSE)
112 re_TailSplitHands = re.compile(r'\n\s*\n')
113 re_Button = re.compile(r'Seat\s#(?P<BUTTON>\d+)\sis\sthe\sbutton')
114 re_Board = re.compile(r"\[(?P<CARDS>.+)\]")
115 re_Total = re.compile(r"Total pot (?P<TOTAL>[\.\d]+).*(No rake|Rake (?P<RAKE>[\.\d]+))" % substitutions)
116 re_Mixed = re.compile(r'_(?P<MIXED>10games|8games|horse)_')
117 re_HUTP = re.compile(r'Hold\-up\sto\sPot:\stotal\s((%(LS)s)?(?P<AMOUNT>[.0-9]+)(%(LS)s)?)' % substitutions,
118 re.MULTILINE | re.VERBOSE)
119 # 2010/09/21 03:10:51 UTC
120 re_DateTime = re.compile("""
121 (?P<Y>[0-9]{4})/
122 (?P<M>[0-9]+)/
123 (?P<D>[0-9]+)\s
124 (?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)\s
125 UTC
126 """, re.MULTILINE | re.VERBOSE)
128 # Seat 1: some_player (5€)
129 # Seat 2: some_other_player21 (6.33€)
130 # Seat 6: I want fold (147894, 29.25€ bounty)
131 re_PlayerInfo = re.compile(
132 u'Seat\s(?P<SEAT>[0-9]+):\s(?P<PNAME>.*)\s\((%(LS)s)?(?P<CASH>[.0-9]+)(%(LS)s)?(,\s(%(LS)s)?(?P<BOUNTY>[.0-9]+)(%(LS)s)?\sbounty)?\)' % substitutions)
133 re_PlayerInfoSummary = re.compile(u'Seat\s(?P<SEAT>[0-9]+):\s(?P<PNAME>.+?)\s' % substitutions)
135 def compilePlayerRegexs(self, hand):
136 players = {player[1] for player in hand.players}
137 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y'
138 # we need to recompile the player regexs.
139 # TODO: should probably rename re_HeroCards and corresponding method,
140 # since they are used to find all cards on lines starting with "Dealt to:"
141 # They still identify the hero.
142 self.compiledPlayers = players
143 # ANTES/BLINDS
144 # helander2222 posts blind ($0.25), lopllopl posts blind ($0.50).
145 player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")"
146 subst = {'PLYR': player_re, 'CUR': self.sym[hand.gametype['currency']]}
147 self.re_PostSB = re.compile(
148 '%(PLYR)s posts small blind (%(CUR)s)?(?P<SB>[\.0-9]+)(%(CUR)s)?(?! out of position)' % subst,
149 re.MULTILINE)
150 self.re_PostBB = re.compile('%(PLYR)s posts big blind (%(CUR)s)?(?P<BB>[\.0-9]+)(%(CUR)s)?' % subst,
151 re.MULTILINE)
152 self.re_DenySB = re.compile('(?P<PNAME>.*) deny SB' % subst, re.MULTILINE)
153 self.re_Antes = re.compile(r"^%(PLYR)s posts ante (%(CUR)s)?(?P<ANTE>[\.0-9]+)(%(CUR)s)?" % subst,
154 re.MULTILINE)
155 self.re_BringIn = re.compile(
156 r"^%(PLYR)s (brings in|bring\-in) (%(CUR)s)?(?P<BRINGIN>[\.0-9]+)(%(CUR)s)?" % subst, re.MULTILINE)
157 self.re_PostBoth = re.compile(
158 '(?P<PNAME>.*): posts small \& big blind \( (%(CUR)s)?(?P<SBBB>[\.0-9]+)(%(CUR)s)?\)' % subst)
159 self.re_PostDead = re.compile(
160 '(?P<PNAME>.*) posts dead blind \((%(CUR)s)?(?P<DEAD>[\.0-9]+)(%(CUR)s)?\)' % subst, re.MULTILINE)
161 self.re_PostSecondSB = re.compile(
162 '%(PLYR)s posts small blind (%(CUR)s)?(?P<SB>[\.0-9]+)(%(CUR)s)? out of position' % subst, re.MULTILINE)
163 self.re_HeroCards = re.compile(
164 'Dealt\sto\s%(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])' % subst)
166 # no discards action observed yet
167 self.re_Action = re.compile(
168 '(, )?(?P<PNAME>.*?)(?P<ATYPE> bets| checks| raises| calls| folds| stands\spat)( \-?(%(CUR)s)?(?P<BET>[\d\.]+)(%(CUR)s)?)?( to (%(CUR)s)?(?P<BETTO>[\d\.]+)(%(CUR)s)?)?( and is all-in)?' % subst)
169 self.re_ShowdownAction = re.compile(
170 '(?P<PNAME>[^\(\)\n]*) (\((small blind|big blind|button)\) )?shows \[(?P<CARDS>.+)\]')
172 self.re_CollectPot = re.compile(
173 '\s*(?P<PNAME>.*)\scollected\s(%(CUR)s)?(?P<POT>[\.\d]+)(%(CUR)s)?.*' % subst)
174 self.re_ShownCards = re.compile(
175 "^Seat (?P<SEAT>[0-9]+): %(PLYR)s (\((small blind|big blind|button)\) )?showed \[(?P<CARDS>.*)\].+? with (?P<STRING>.*)" % subst,
176 re.MULTILINE)
178 def readSupportedGames(self):
179 return [
180 ["ring", "hold", "fl"],
181 ["ring", "hold", "nl"],
182 ["ring", "hold", "pl"],
184 ["ring", "stud", "fl"],
186 ["ring", "draw", "fl"],
187 ["ring", "draw", "pl"],
188 ["ring", "draw", "nl"],
190 ["tour", "hold", "fl"],
191 ["tour", "hold", "nl"],
192 ["tour", "hold", "pl"],
194 ["tour", "stud", "fl"],
196 ["tour", "draw", "fl"],
197 ["tour", "draw", "pl"],
198 ["tour", "draw", "nl"],
199 ]
201 def determineGameType(self, handText):
202 # Inspect the handText and return the gametype dict
203 # gametype dict is: {'limitType': xxx, 'base': xxx, 'category': xxx}
204 info = {}
206 m = self.re_HandInfo.search(handText)
207 if not m:
208 tmp = handText[0:200]
209 log.error("WinamaxToFpdb.determineGameType: '%s'" % tmp)
210 raise FpdbParseError
212 mg = m.groupdict()
214 if mg.get('TOUR'):
215 info['type'] = 'tour'
216 info['currency'] = 'T$'
217 elif mg.get('RING'):
218 info['type'] = 'ring'
220 info['currency'] = 'EUR' if mg.get('MONEY') else 'play'
221 info['fast'] = 'Go Fast' in mg.get('RING')
222 if 'LIMIT' in mg:
223 if mg['LIMIT'] in self.limits:
224 info['limitType'] = self.limits[mg['LIMIT']]
225 else:
226 tmp = handText[0:100]
227 log.error("WinamaxToFpdb.determineGameType: Limit not found in %s." % tmp)
228 raise FpdbParseError
229 if 'GAME' in mg:
230 (info['base'], info['category']) = self.games[mg['GAME']]
231 if m := self.re_Mixed.search(self.in_path):
232 info['mix'] = self.mixes[m.groupdict()['MIXED']]
233 if 'SB' in mg:
234 info['sb'] = mg['SB']
235 if 'BB' in mg:
236 info['bb'] = mg['BB']
238 if info['limitType'] == 'fl' and info['bb'] is not None:
239 info['sb'] = str((Decimal(mg['SB']) / 2).quantize(Decimal("0.01")))
240 info['bb'] = str(Decimal(mg['SB']).quantize(Decimal("0.01")))
242 return info
244 def readHandInfo(self, hand):
245 info = {}
246 m = self.re_HandInfo.search(hand.handText)
247 if m is None:
248 tmp = hand.handText[:200]
249 log.error(f"WinamaxToFpdb.readHandInfo: '{tmp}'")
250 raise FpdbParseError
252 info |= m.groupdict()
253 log.debug(f"readHandInfo: {info}")
254 for key, value in info.items():
255 if key == 'DATETIME':
256 if a := self.re_DateTime.search(value):
257 datetimestr = f"{a.group('Y')}/{a.group('M')}/{a.group('D')}" \
258 f" {a.group('H')}:{a.group('MIN')}:{a.group('S')}"
259 else:
260 datetimestr = "2010/Jan/01 01:01:01"
261 log.error(f"readHandInfo: DATETIME not matched: '{info[key]}'")
262 print(f"DEBUG: readHandInfo: DATETIME not matched: '{info[key]}'")
263 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S")
264 elif key == 'HID1':
265 # Need to remove non-alphanumerics for MySQL
266 # Concatenating all three or just HID2 + HID3 can produce out of range values
267 # HID should not be greater than 14 characters to ensure this
268 hand.handid = f"{int(info['HID1'][:14])}{int(info['HID2'])}"
270 elif key == 'TOURNO':
271 hand.tourNo = info[key]
272 if key == 'TABLE':
273 hand.tablename = info[key]
274 if hand.gametype['type'] == 'tour':
275 hand.tablename = info['TABLENO']
276 hand.roundPenny = True
277 # TODO: long-term solution for table naming on Winamax.
278 if hand.tablename.endswith(u'No Limit Hold\'em'):
279 hand.tablename = hand.tablename[:-len(u'No Limit Hold\'em')] + u'NLHE'
280 if key == 'MAXPLAYER' and info[key] != None:
281 hand.maxseats = int(info[key])
283 if key == 'BUYIN' and hand.tourNo != None:
284 print(f"DEBUG: info['BUYIN']: {info['BUYIN']}")
285 print(f"DEBUG: info['BIAMT']: {info['BIAMT']}")
286 print(f"DEBUG: info['BIRAKE']: {info['BIRAKE']}")
287 print(f"DEBUG: info['BOUNTY']: {info['BOUNTY']}")
288 for k in ['BIAMT', 'BIRAKE']:
289 if k in info and info[k]:
290 info[k] = info[k].replace(',', '.')
292 if info['FREETICKET'] is not None:
293 hand.buyin = 0
294 hand.fee = 0
295 hand.buyinCurrency = "FREE"
296 else:
297 if info[key].find("$") != -1:
298 hand.buyinCurrency = "USD"
299 elif info[key].find(u"€") != -1:
300 hand.buyinCurrency = "EUR"
301 elif info[key].find("FPP") != -1:
302 hand.buyinCurrency = "WIFP"
303 elif info[key].find("Free") != -1:
304 hand.buyinCurrency = "WIFP"
305 elif info['MONEY']:
306 hand.buyinCurrency = "EUR"
307 else:
308 hand.buyinCurrency = "play"
310 info['BIAMT'] = (
311 info['BIAMT'].strip(u'$€FPP')
312 if info['BIAMT'] is not None
313 else 0
314 )
315 if hand.buyinCurrency != "WIFP":
316 if info['BOUNTY'] != None:
317 # There is a bounty, Which means we need to switch BOUNTY and BIRAKE values
318 tmp = info['BOUNTY']
319 info['BOUNTY'] = info['BIRAKE']
320 info['BIRAKE'] = tmp
321 info['BOUNTY'] = info['BOUNTY'].strip(u'$€') # Strip here where it isn't 'None'
322 hand.koBounty = int(100 * Decimal(info['BOUNTY']))
323 hand.isKO = True
324 else:
325 hand.isKO = False
327 info['BIRAKE'] = info['BIRAKE'].strip(u'$€')
329 # TODO: Is this correct? Old code tried to
330 # conditionally multiply by 100, but we
331 # want hand.buyin in 100ths of
332 # dollars/euros (so hand.buyin = 90 for $0.90 BI).
333 hand.buyin = int(100 * Decimal(info['BIAMT']))
334 hand.fee = int(100 * Decimal(info['BIRAKE']))
335 else:
336 hand.buyin = int(Decimal(info['BIAMT']))
337 hand.fee = 0
338 if hand.buyin == 0 and hand.fee == 0:
339 hand.buyinCurrency = "FREE"
341 if key == 'LEVEL':
342 hand.level = info[key]
344 hand.mixed = None
346 def readPlayerStacks(self, hand):
347 # Split hand text for Winamax, as the players listed in the hh preamble and the summary will differ
348 # if someone is sitting out.
349 # Going to parse both and only add players in the summary.
350 handsplit = hand.handText.split('*** SUMMARY ***')
351 if len(handsplit) != 2:
352 raise FpdbHandPartial(
353 f"Hand is not cleanly split into pre and post Summary {hand.handid}."
354 )
355 pre, post = handsplit
356 m = self.re_PlayerInfo.finditer(pre)
357 plist = {}
359 # Get list of players in header.
360 for a in m:
361 if plist.get(a.group('PNAME')) is None:
362 hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
363 plist[a.group('PNAME')] = [int(a.group('SEAT')), a.group('CASH')]
365 if len(plist.keys()) < 2:
366 raise FpdbHandPartial(f"Less than 2 players in hand! {hand.handid}.")
368 def markStreets(self, hand):
369 if hand.gametype['base'] == "hold":
370 m = re.search(r"\*\*\* ANTE/BLINDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
371 r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
372 r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S](?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
373 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S](?P<RIVER>\[\S\S\].+))?", hand.handText,
374 re.DOTALL)
375 elif hand.gametype['base'] == "stud":
376 m = re.search(r"\*\*\* ANTE/BLINDS \*\*\*(?P<ANTES>.+(?=\*\*\* (3rd STREET|THIRD) \*\*\*)|.+)"
377 r"(\*\*\* (3rd STREET|THIRD) \*\*\*(?P<THIRD>.+(?=\*\*\* (4th STREET|FOURTH) \*\*\*)|.+))?"
378 r"(\*\*\* (4th STREET|FOURTH) \*\*\*(?P<FOURTH>.+(?=\*\*\* (5th STREET|FIFTH) \*\*\*)|.+))?"
379 r"(\*\*\* (5th STREET|FIFTH) \*\*\*(?P<FIFTH>.+(?=\*\*\* (6th STREET|SIXTH) \*\*\*)|.+))?"
380 r"(\*\*\* (6th STREET|SIXTH) \*\*\*(?P<SIXTH>.+(?=\*\*\* (7th STREET|SEVENTH) \*\*\*)|.+))?"
381 r"(\*\*\* (7th STREET|SEVENTH) \*\*\*(?P<SEVENTH>.+))?", hand.handText, re.DOTALL)
382 else:
383 m = re.search(r"\*\*\* ANTE/BLINDS \*\*\*(?P<PREDEAL>.+(?=\*\*\* FIRST\-BET \*\*\*)|.+)"
384 r"(\*\*\* FIRST\-BET \*\*\*(?P<DEAL>.+(?=\*\*\* FIRST\-DRAW \*\*\*)|.+))?"
385 r"(\*\*\* FIRST\-DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* SECOND\-DRAW \*\*\*)|.+))?"
386 r"(\*\*\* SECOND\-DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* THIRD\-DRAW \*\*\*)|.+))?"
387 r"(\*\*\* THIRD\-DRAW \*\*\*(?P<DRAWTHREE>.+))?", hand.handText, re.DOTALL)
389 try:
390 hand.addStreets(m)
391 print("adding street", m.group(0))
392 print("---")
393 except Exception:
394 log.info("Failed to add streets. handtext=%s")
396 # Needs to return a list in the format
397 # ['player1name', 'player2name', ...] where player1name is the sb and player2name is bb,
398 # addtional players are assumed to post a bb oop
400 def readButton(self, hand):
401 if m := self.re_Button.search(hand.handText):
402 hand.buttonpos = int(m.group('BUTTON'))
403 log.debug('readButton: button on pos %d' % hand.buttonpos)
404 else:
405 log.info('readButton: ' + 'not found')
407 # def readCommunityCards(self, hand, street):
408 # #print hand.streets.group(street)
409 # if street in ('FLOP','TURN','RIVER'):
410 # a list of streets which get dealt community cards (i.e. all but PREFLOP)
411 # m = self.re_Board.search(hand.streets.group(street))
412 # hand.setCommunityCards(street, m.group('CARDS').split(','))
414 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
415 if street in ('FLOP', 'TURN', 'RIVER'):
416 # a list of streets which get dealt community cards (i.e. all but PREFLOP)
417 # print("DEBUG readCommunityCards:", street, hand.streets.group(street))
418 m = self.re_Board.search(hand.streets[street])
419 hand.setCommunityCards(street, m.group('CARDS').split(' '))
421 def readBlinds(self, hand):
422 found_small, found_big = False, False
423 m = self.re_PostSB.search(hand.handText)
424 if m is not None:
425 hand.addBlind(m.group('PNAME'), 'small blind', m.group('SB'))
426 found_small = True
427 else:
428 log.debug("No small blind")
429 hand.addBlind(None, None, None)
431 for a in self.re_PostBB.finditer(hand.handText):
432 hand.addBlind(a.group('PNAME'), 'big blind', a.group('BB'))
433 amount = Decimal(a.group('BB').replace(u',', u''))
434 hand.lastBet['PREFLOP'] = amount
435 for a in self.re_PostDead.finditer(hand.handText):
436 print(
437 f"DEBUG: Found dead blind: addBlind({a.group('PNAME')}, 'secondsb', {a.group('DEAD')})"
438 )
439 hand.addBlind(a.group('PNAME'), 'secondsb', a.group('DEAD'))
440 for a in self.re_PostSecondSB.finditer(hand.handText):
441 print(
442 f"DEBUG: Found dead blind: addBlind({a.group('PNAME')}, 'secondsb/both', {a.group('SB')}, {hand.sb})"
443 )
444 if Decimal(a.group('SB')) > Decimal(hand.sb):
445 hand.addBlind(a.group('PNAME'), 'both', a.group('SB'))
446 else:
447 hand.addBlind(a.group('PNAME'), 'secondsb', a.group('SB'))
449 def readAntes(self, hand):
450 log.debug("reading antes")
451 m = self.re_Antes.finditer(hand.handText)
452 for player in m:
453 logging.debug(f"hand.addAnte({player.group('PNAME')},{player.group('ANTE')})")
454 hand.addAnte(player.group('PNAME'), player.group('ANTE'))
456 def readBringIn(self, hand):
457 if m := self.re_BringIn.search(hand.handText, re.DOTALL):
458 logging.debug(f"readBringIn: {m.group('PNAME')} for {m.group('BRINGIN')}")
459 hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
461 def readSTP(self, hand):
462 if m := self.re_HUTP.search(hand.handText):
463 hand.addSTP(m.group('AMOUNT'))
465 def readHoleCards(self, hand):
466 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
467 # we need to grab hero's cards
468 for street in ('PREFLOP', 'DEAL', 'BLINDSANTES'):
469 if street in hand.streets.keys():
470 m = self.re_HeroCards.finditer(hand.streets[street])
471 for found in m:
472 if newcards := [
473 c for c in found.group('NEWCARDS').split(' ') if c != 'X'
474 ]:
475 hand.hero = found.group('PNAME')
477 print(f"DEBUG: {hand.handid} addHoleCards({street}, {hand.hero}, {newcards})")
478 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
479 log.debug(f"Hero cards {hand.hero}: {newcards}")
481 for street, text in list(hand.streets.items()):
482 if not text or street in ('PREFLOP', 'DEAL', 'BLINDSANTES'): continue # already done these
483 m = self.re_HeroCards.finditer(hand.streets[street])
484 for found in m:
485 player = found.group('PNAME')
486 if found.group('NEWCARDS') is None:
487 newcards = []
488 else:
489 newcards = [c for c in found.group('NEWCARDS').split(' ') if c != 'X']
490 if found.group('OLDCARDS') is None:
491 oldcards = []
492 else:
493 oldcards = [c for c in found.group('OLDCARDS').split(' ') if c != 'X']
495 if street == 'THIRD' and len(newcards) == 3: # hero in stud game
496 hand.hero = player
497 hand.dealt.add(player) # need this for stud??
498 hand.addHoleCards(
499 street,
500 player,
501 closed=newcards[:2],
502 open=[newcards[2]],
503 shown=False,
504 mucked=False,
505 dealt=False,
506 )
507 else:
508 hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False,
509 dealt=False)
511 def readAction(self, hand, street):
512 streetsplit = hand.streets[street].split('*** SUMMARY ***')
513 m = self.re_Action.finditer(streetsplit[0])
514 for action in m:
515 acts = action.groupdict()
516 print(f"DEBUG: acts: {acts}")
517 if action.group('ATYPE') == ' folds':
518 hand.addFold(street, action.group('PNAME'))
519 elif action.group('ATYPE') == ' checks':
520 hand.addCheck(street, action.group('PNAME'))
521 elif action.group('ATYPE') == ' calls':
522 hand.addCall(street, action.group('PNAME'), action.group('BET'))
523 elif action.group('ATYPE') == ' raises':
524 if bringin := [
525 act[2]
526 for act in hand.actions[street]
527 if act[0] == action.group('PNAME') and act[1] == 'bringin'
528 ]:
529 betto = str(Decimal(action.group('BETTO')) + bringin[0])
530 else:
531 betto = action.group('BETTO')
532 hand.addRaiseTo(street, action.group('PNAME'), betto)
533 elif action.group('ATYPE') == ' bets':
534 if street in ('PREFLOP', 'DEAL', 'THIRD', 'BLINDSANTES'):
535 hand.addRaiseBy(street, action.group('PNAME'), action.group('BET'))
536 else:
537 hand.addBet(street, action.group('PNAME'), action.group('BET'))
538 elif action.group('ATYPE') == ' discards':
539 hand.addDiscard(street, action.group('PNAME'), action.group('BET'), action.group('DISCARDED'))
540 elif action.group('ATYPE') == ' stands pat':
541 hand.addStandsPat(street, action.group('PNAME'))
542 else:
543 log.fatal(
544 f"DEBUG:Unimplemented readAction: '{action.group('PNAME')}' '{action.group('ATYPE')}'"
545 )
546 print(f"Processed {acts}")
547 print("committed=", hand.pot.committed)
549 def readShowdownActions(self, hand):
550 for shows in self.re_ShowdownAction.finditer(hand.handText):
551 log.debug(f"add show actions {shows}")
552 cards = shows.group('CARDS')
553 cards = cards.split(' ')
554 print(f"DEBUG: addShownCards({cards}, {shows.group('PNAME')})")
555 hand.addShownCards(cards, shows.group('PNAME'))
557 def readCollectPot(self, hand):
558 hand.setUncalledBets(True)
559 for m in self.re_CollectPot.finditer(hand.handText):
560 hand.addCollectPot(player=m.group('PNAME'), pot=m.group('POT'))
562 def readShownCards(self, hand):
563 for m in self.re_ShownCards.finditer(hand.handText):
564 log.debug(f"Read shown cards: {m.group(0)}")
565 cards = m.group('CARDS')
566 cards = cards.split(' ') # needs to be a list, not a set--stud needs the order
567 (shown, mucked) = (False, False)
568 if m.group('CARDS') is not None:
569 shown = True
570 string = m.group('STRING')
571 print(m.group('PNAME'), cards, shown, mucked)
572 hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked, string=string)
574 @staticmethod
575 def getTableTitleRe(type, table_name=None, tournament=None, table_number=None):
577 log.info(
578 f"Winamax.getTableTitleRe: table_name='{table_name}' tournament='{tournament}' table_number='{table_number}'"
579 )
580 sysPlatform = platform.system() # Linux, Windows, Darwin
581 if sysPlatform[:5] == 'Linux':
582 regex = f"Winamax {table_name}"
583 else:
584 regex = f"Winamax {table_name} /"
585 print("regex get table cash title:", regex)
586 if tournament:
587 if (
588 table_number > 99
589 or (table_number >= 100 or table_number <= 9)
590 and table_number == 0
591 ):
593 regex = r"Winamax\s+([^\(]+)\(%s\)\(#%s\)" % (tournament, table_number)
594 elif table_number < 100 and table_number > 9:
595 regex = r"Winamax\s+([^\(]+)\(%s\)\(#0%s\)" % (tournament, table_number)
596 else:
597 regex = r"Winamax\s+([^\(]+)\(%s\)\(#00%s\)" % (tournament, table_number)
599 print("regex get mtt sng expresso cash title:", regex)
600 log.info(f"Winamax.getTableTitleRe: returns: '{regex}'")
601 return regex