Coverage for WinamaxToFpdb.py: 0%

325 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 18:50 +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######################################################################## 

20import sys 

21import Exceptions 

22# import L10n 

23# _ = L10n.get_translation() 

24from HandHistoryConverter import * 

25from decimal_wrapper import Decimal 

26import platform 

27 

28 

29# Winamax HH Format 

30 

31 

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 

39 

40 my_f.__name = f.__name__ 

41 my_f.__doc__ = f.__doc__ 

42 return my_f 

43 

44 filter = "Winamax" 

45 siteName = "Winamax" 

46 filetype = "text" 

47 codepage = ("utf8", "cp1252") 

48 siteId = 15 # Needs to match id entry in Sites database 

49 

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 } 

57 

58 limits = {'no limit': 'nl', 'pot limit': 'pl', 'fixed limit': 'fl'} 

59 

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 } 

81 

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

86 

87 

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) 

111 

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) 

127 

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) 

134 

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) 

165 

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>.+)\]') 

171 

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) 

177 

178 def readSupportedGames(self): 

179 return [ 

180 ["ring", "hold", "fl"], 

181 ["ring", "hold", "nl"], 

182 ["ring", "hold", "pl"], 

183 

184 ["ring", "stud", "fl"], 

185 

186 ["ring", "draw", "fl"], 

187 ["ring", "draw", "pl"], 

188 ["ring", "draw", "nl"], 

189 

190 ["tour", "hold", "fl"], 

191 ["tour", "hold", "nl"], 

192 ["tour", "hold", "pl"], 

193 

194 ["tour", "stud", "fl"], 

195 

196 ["tour", "draw", "fl"], 

197 ["tour", "draw", "pl"], 

198 ["tour", "draw", "nl"], 

199 ] 

200 

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 = {} 

205 

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 

211 

212 mg = m.groupdict() 

213 

214 if mg.get('TOUR'): 

215 info['type'] = 'tour' 

216 info['currency'] = 'T$' 

217 elif mg.get('RING'): 

218 info['type'] = 'ring' 

219 

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

237 

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

241 

242 return info 

243 

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 

251 

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

269 

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

282 

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

291 

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" 

309 

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 

326 

327 info['BIRAKE'] = info['BIRAKE'].strip(u'$€') 

328 

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" 

340 

341 if key == 'LEVEL': 

342 hand.level = info[key] 

343 

344 hand.mixed = None 

345 

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 = {} 

358 

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

364 

365 if len(plist.keys()) < 2: 

366 raise FpdbHandPartial(f"Less than 2 players in hand! {hand.handid}.") 

367 

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) 

388 

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

395 

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 

399 

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

406 

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

413 

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

420 

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) 

430 

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

448 

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

455 

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

460 

461 def readSTP(self, hand): 

462 if m := self.re_HUTP.search(hand.handText): 

463 hand.addSTP(m.group('AMOUNT')) 

464 

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

476 

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

480 

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

494 

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) 

510 

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) 

548 

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

556 

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

561 

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) 

573 

574 @staticmethod 

575 def getTableTitleRe(type, table_name=None, tournament=None, table_number=None): 

576 

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

592 

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) 

598 

599 print("regex get mtt sng expresso cash title:", regex) 

600 log.info(f"Winamax.getTableTitleRe: returns: '{regex}'") 

601 return regex