Coverage for KingsClubToFpdb.py: 0%

328 statements  

« 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######################################################################## 

20 

21# import L10n 

22# _ = L10n.get_translation() 

23 

24# TODO: straighten out discards for draw games 

25 

26from HandHistoryConverter import HandHistoryConverter, FpdbParseError, FpdbHandPartial 

27from decimal import Decimal 

28import re 

29import logging 

30import datetime 

31 

32# KingsClub HH Format 

33log = logging.getLogger("parser") 

34 

35 

36class KingsClub(HandHistoryConverter): 

37 # Class Variables 

38 

39 sitename = "KingsClub" 

40 filetype = "text" 

41 codepage = ("utf8", "cp1252", "ISO-8859-1") 

42 siteId = 28 # 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 } 

60 

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 } 

122 

123 limits = {"No Limit": "nl", "Pot Limit": "pl", "Limit": "fl"} 

124 games = { # base, category 

125 "Holdem": ("hold", "holdem"), 

126 "Omaha": ("hold", "omahahi"), 

127 "Omaha Hi-Lo": ("hold", "omahahilo"), 

128 "Big O": ("hold", "5_omaha8"), 

129 "Omaha 5 Card": ("hold", "5_omahahi"), 

130 "Omaha 6 Card": ("hold", "6_omahahi"), 

131 "Short Deck Holdem": ("hold", "6_holdem"), 

132 "Razz": ("stud", "razz"), 

133 "Seven Card Stud": ("stud", "studhi"), 

134 "Seven Card Stud Hi-Lo": ("stud", "studhilo"), 

135 "Badugi": ("draw", "badugi"), 

136 "2-7 Triple Draw": ("draw", "27_3draw"), 

137 "2-7 Single Draw": ("draw", "27_1draw"), 

138 "5 Card Draw": ("draw", "fivedraw"), 

139 "A-5 Triple Draw": ("draw", "a5_3draw"), 

140 "A-5 Single Draw": ("draw", "a5_1draw"), 

141 "2-7 Razz": ("stud", "27_razz"), 

142 "Badacey": ("draw", "badacey"), 

143 "Badeucey": ("draw", "badeucey"), 

144 "2-7 Drawmaha": ("draw", "drawmaha"), 

145 "Captain": ("draw", "drawmaha"), 

146 } 

147 mixes = { 

148 "HORSE": "horse", 

149 "8-Game": "8game", 

150 "8-GAME": "8game", 

151 "HOSE": "hose", 

152 "Mixed PLH/PLO": "plh_plo", 

153 "Mixed NLH/PLO": "nlh_plo", 

154 "Mixed Omaha H/L": "plo_lo", 

155 "Mixed Hold'em": "mholdem", 

156 "Mixed Omaha": "momaha", 

157 "Triple Stud": "3stud", 

158 } # Legal mixed games 

159 currencies = {"€": "EUR", "$": "USD", "": "T$", "£": "GBP", "¥": "CNY", "₹": "INR"} 

160 

161 # Static regexes 

162 re_GameInfo = re.compile( 

163 """ 

164 \#(?P<HID>[0-9]+):\s+ 

165 (?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\s 

166 (?P<GAME>Holdem|Short\sDeck\sHoldem|Razz|Seven\sCard\sStud|Seven\sCard\sStud\sHi\-Lo|Omaha|Omaha\s(5|6)\sCard|Omaha\sHi\-Lo|Badugi|2\-7\sTriple\sDraw|2\-7\sSingle\sDraw|5\sCard\sDraw|Big\sO|2\-7\sRazz|Badacey|Badeucey|A\-5\sTriple\sDraw|A\-5\sSingle\sDraw|2\-7\sDrawmaha|Captain)\s 

167 \-\s(?P<SB>[,.0-9]+)/(?P<BB>[,.0-9]+) 

168 """ 

169 % substitutions, 

170 re.MULTILINE | re.VERBOSE, 

171 ) 

172 

173 re_PlayerInfo = re.compile( 

174 """ 

175 ^\s?Seat\s(?P<SEAT>[0-9]+):\s 

176 (?P<PNAME>.*)\s 

177 \((%(LS)s)?(?P<CASH>[,.0-9]+) 

178 \)""" 

179 % substitutions, 

180 re.MULTILINE | re.VERBOSE, 

181 ) 

182 

183 re_HandInfo = re.compile( 

184 """ 

185 ^\s?Table\s(ID\s)?\'(?P<TABLE>.+?)\'\s 

186 ((?P<MAX>\d+)-max\s)? 

187 (?P<PLAY>\(Play\sMoney\)\s)? 

188 (Seat\s\#(?P<BUTTON>\d+)\sis\sthe\sbutton)?""", 

189 re.MULTILINE | re.VERBOSE, 

190 ) 

191 

192 re_TourNo = re.compile("Table\s'T(?P<TOURNO>\d+)\s\[(?P<TABLENO>\d+)\]'") 

193 

194 re_Identify = re.compile("^\#\d+:") 

195 re_SplitHands = re.compile("(?:\s?\n){2,}") 

196 re_TailSplitHands = re.compile("(\n\n\n+)") 

197 re_Button = re.compile("Seat #(?P<BUTTON>\d+) is the button", re.MULTILINE) 

198 re_Board = re.compile(r"\[(?P<CARDS>.+)\]") 

199 re_Board2 = re.compile(r"\[(?P<C1>\S\S)\] \[(\S\S)?(?P<C2>\S\S) (?P<C3>\S\S)\]") 

200 re_DateTime = re.compile( 

201 """(?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]+)""", 

202 re.MULTILINE, 

203 ) 

204 # revised re including timezone (not currently used): 

205 # 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) 

206 

207 # These used to be compiled per player, but regression tests say 

208 # we don't have to, and it makes life faster. 

209 re_PostSB = re.compile(r"^%(PLYR)s: posts the small blind %(CUR)s(?P<SB>[,.0-9]+)" % substitutions, re.MULTILINE) 

210 re_PostBB = re.compile(r"^%(PLYR)s: posts the big blind %(CUR)s(?P<BB>[,.0-9]+)" % substitutions, re.MULTILINE) 

211 re_Antes = re.compile(r"^%(PLYR)s: posts ante %(CUR)s(?P<ANTE>[,.0-9]+)" % substitutions, re.MULTILINE) 

212 re_BringIn = re.compile( 

213 r"^%(PLYR)s brings[- ]in( low|) for %(CUR)s(?P<BRINGIN>[,.0-9]+)" % substitutions, re.MULTILINE 

214 ) 

215 re_PostBoth = re.compile(r"^%(PLYR)s: posts blind %(CUR)s(?P<SBBB>[,.0-9]+)" % substitutions, re.MULTILINE) 

216 re_PostStraddle = re.compile( 

217 r"^%(PLYR)s: (posts )?straddles? %(CUR)s(?P<STRADDLE>[,.0-9]+)" % substitutions, re.MULTILINE 

218 ) 

219 re_Action = re.compile( 

220 r"""^%(PLYR)s(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat|\sdraws)(\s%(CUR)s(?P<BET>[,.\d]+))?(\sto\s%(CUR)s(?P<BETTO>[,.\d]+))?\s*(,\sand\sis\sall.in)?(and\shas\sreached\sthe\s\[%(CUR)s\d\.,]+\scap)?(\son|\scards?)?(\s\(disconnect\))?(\s\[(?P<CARDS>.+?)\]\sdraws\s\[(?P<DRAWS1>.+?)\](\s\[(?P<DRAWS2>.+?)\])?)?\s*$""" 

221 % substitutions, 

222 re.MULTILINE | re.VERBOSE, 

223 ) 

224 re_ShowdownAction = re.compile(r"^%s shows \[(?P<CARDS>.*)\]" % substitutions["PLYR"], re.MULTILINE) 

225 re_sitsOut = re.compile("^%s sits out" % substitutions["PLYR"], re.MULTILINE) 

226 # 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) 

227 # bd43 wins (279.50) from pot 

228 re_CollectPot = re.compile( 

229 r"^%(PLYR)s wins (pot )?(side pot \d )?\((?P<POT>[,.\d]+)\)( from pot)?" % substitutions, re.MULTILINE 

230 ) 

231 re_CashedOut = re.compile(r"cashed\sout\sthe\shand") 

232 re_WinningRankOne = re.compile( 

233 "^%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[,\.0-9]+) - congratulations!$" % substitutions, 

234 re.MULTILINE, 

235 ) 

236 re_WinningRankOther = re.compile( 

237 "^%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place and received %(CUR)s(?P<AMT>[,.0-9]+)\.$" 

238 % substitutions, 

239 re.MULTILINE, 

240 ) 

241 re_RankOther = re.compile( 

242 "^%(PLYR)s finished the tournament in (?P<RANK>[0-9]+)(st|nd|rd|th) place$" % substitutions, re.MULTILINE 

243 ) 

244 re_Cancelled = re.compile("Hand\scancelled", re.MULTILINE) 

245 re_Uncalled = re.compile("Uncalled bet \(%(CUR)s(?P<BET>[,.\d]+)\) returned to" % substitutions, re.MULTILINE) 

246 # APTEM-89 wins the $0.27 bounty for eliminating Hero 

247 # ChazDazzle wins the 22000 bounty for eliminating berkovich609 

248 # JKuzja, vecenta split the $50 bounty for eliminating ODYSSES 

249 re_Bounty = re.compile( 

250 "^%(PLYR)s (?P<SPLIT>split|wins) the %(CUR)s(?P<AMT>[,\.0-9]+) bounty for eliminating (?P<ELIMINATED>.+?)$" 

251 % substitutions, 

252 re.MULTILINE, 

253 ) 

254 # Amsterdam71 wins $19.90 for eliminating MuKoJla and their own bounty increases by $19.89 to $155.32 

255 # Amsterdam71 wins $4.60 for splitting the elimination of Frimble11 and their own bounty increases by $4.59 to $41.32 

256 # Amsterdam71 wins the tournament and receives $230.36 - congratulations! 

257 re_Progressive = re.compile( 

258 """ 

259 ^%(PLYR)s\swins\s%(CUR)s(?P<AMT>[,\.0-9]+)\s 

260 for\s(splitting\sthe\selimination\sof|eliminating)\s(?P<ELIMINATED>.+?)\s 

261 and\stheir\sown\sbounty\sincreases\sby\s%(CUR)s(?P<INCREASE>[\.0-9]+)\sto\s%(CUR)s(?P<ENDAMT>[\.0-9]+)$""" 

262 % substitutions, 

263 re.MULTILINE | re.VERBOSE, 

264 ) 

265 

266 re_STP = re.compile( 

267 """ 

268 STP\sadded:\s%(CUR)s(?P<AMOUNT>[,\.0-9]+)""" 

269 % substitutions, 

270 re.MULTILINE | re.VERBOSE, 

271 ) 

272 

273 re_Rake = re.compile(r"^Rake\s(?P<RAKE>[,.0-9]+)$", re.MULTILINE) 

274 re_Split = re.compile(r"\*\*\* BOARD 1 - FLOP \*\*\*") 

275 re_Table = re.compile(r"^\s?Table\s(ID\s)?\'(?P<TABLE>.+?)\'\s", re.MULTILINE | re.VERBOSE) 

276 

277 def compilePlayerRegexs(self, hand): 

278 players = set([player[1] for player in hand.players]) 

279 if not players <= self.compiledPlayers: # x <= y means 'x is subset of y' 

280 self.compiledPlayers = players 

281 player_re = "(?P<PNAME>" + "|".join(map(re.escape, players)) + ")" 

282 subst = { 

283 "PLYR": player_re, 

284 "BRKTS": r"(\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) |\(button\) \(big blind\) )?", 

285 "CUR": "(\$|\xe2\x82\xac|\u20ac||\£|)", 

286 } 

287 self.re_HeroCards = re.compile( 

288 r"^Dealt to %(PLYR)s:(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % subst, re.MULTILINE 

289 ) 

290 

291 def readSupportedGames(self): 

292 return [ 

293 ["ring", "hold", "nl"], 

294 ["ring", "hold", "pl"], 

295 ["ring", "hold", "fl"], 

296 ["ring", "hold", "pn"], 

297 ["ring", "stud", "fl"], 

298 ["ring", "draw", "fl"], 

299 ["ring", "draw", "pl"], 

300 ["ring", "draw", "nl"], 

301 ["tour", "hold", "nl"], 

302 ["tour", "hold", "pl"], 

303 ["tour", "hold", "fl"], 

304 ["tour", "hold", "pn"], 

305 ["tour", "stud", "fl"], 

306 ["tour", "draw", "fl"], 

307 ["tour", "draw", "pl"], 

308 ["tour", "draw", "nl"], 

309 ] 

310 

311 def determineGameType(self, handText): 

312 info = {} 

313 m = self.re_GameInfo.search(handText) 

314 if not m: 

315 tmp = handText[0:200] 

316 log.error(("KingsClubToFpdb.determineGameType: '%s'") % tmp) 

317 raise FpdbParseError 

318 

319 mg = m.groupdict() 

320 if "LIMIT" in mg: 

321 info["limitType"] = self.limits[mg["LIMIT"]] 

322 if "GAME" in mg: 

323 (info["base"], info["category"]) = self.games[mg["GAME"]] 

324 if "SB" in mg and mg["SB"] is not None: 

325 info["sb"] = self.clearMoneyString(mg["SB"]) 

326 if "BB" in mg and mg["BB"] is not None: 

327 info["bb"] = self.clearMoneyString(mg["BB"]) 

328 

329 m1 = self.re_TourNo.search(handText) 

330 if m1: 

331 info["type"] = "tour" 

332 info["currency"] = "T$" 

333 else: 

334 info["type"] = "ring" 

335 info["currency"] = "USD" 

336 

337 m2 = self.re_Split.search(handText) 

338 if m2: 

339 info["split"] = True 

340 else: 

341 info["split"] = False 

342 

343 m3 = self.re_Table.search(handText) 

344 if m3 and "AOF" in m3.group("TABLE"): 

345 info["category"] = "aof_omaha" 

346 

347 if info["limitType"] == "fl" and info["bb"] is not None: 

348 if info["type"] == "ring": 

349 try: 

350 info["sb"] = self.Lim_Blinds[self.clearMoneyString(mg["BB"])][0] 

351 info["bb"] = self.Lim_Blinds[self.clearMoneyString(mg["BB"])][1] 

352 except KeyError: 

353 info["sb"] = str((Decimal(self.clearMoneyString(mg["SB"])) / 2).quantize(Decimal("0.01"))) 

354 info["bb"] = str(Decimal(self.clearMoneyString(mg["SB"])).quantize(Decimal("0.01"))) 

355 # tmp = handText[0:200] 

356 # log.error(_("KingsClubToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (mg['BB'], tmp)) 

357 # raise FpdbParseError 

358 else: 

359 info["sb"] = str((Decimal(self.clearMoneyString(mg["SB"])) / 2).quantize(Decimal("0.01"))) 

360 info["bb"] = str(Decimal(self.clearMoneyString(mg["SB"])).quantize(Decimal("0.01"))) 

361 

362 return info 

363 

364 def readHandInfo(self, hand): 

365 # First check if partial 

366 if hand.handText.count("*** SUMMARY *") != 1: 

367 raise FpdbHandPartial("Hand is not cleanly split into pre and post Summary") 

368 

369 info = {} 

370 m = self.re_HandInfo.search(hand.handText, re.DOTALL) 

371 m1 = self.re_DateTime.finditer(hand.handText, re.DOTALL) 

372 m2 = self.re_GameInfo.search(hand.handText) 

373 m3 = self.re_TourNo.search(hand.handText) 

374 if m is None or m1 is None or m2 is None: 

375 tmp = hand.handText[0:200] 

376 log.error(("KingsClubToFpdb.readHandInfo: '%s'") % tmp) 

377 raise FpdbParseError 

378 

379 info.update(m.groupdict()) 

380 info.update(m2.groupdict()) 

381 if m3 is not None: 

382 info.update(m3.groupdict()) 

383 # 2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET] # (both dates are parsed so ET date overrides the other) 

384 # 2008/08/17 - 01:14:43 (ET) 

385 # 2008/09/07 06:23:14 ET 

386 # 2021-01-02 15:39:12 

387 datetimestr = "2000/01/01 00:00:00" # default used if time not found 

388 for a in m1: 

389 datetimestr = "%s/%s/%s %s:%s:%s" % ( 

390 a.group("Y"), 

391 a.group("M"), 

392 a.group("D"), 

393 a.group("H"), 

394 a.group("MIN"), 

395 a.group("S"), 

396 ) 

397 # tz = a.group('TZ') # just assume ET?? 

398 # print " tz = ", tz, " datetime =", datetimestr 

399 hand.startTime = datetime.datetime.strptime( 

400 datetimestr, "%Y/%m/%d %H:%M:%S" 

401 ) # also timezone at end, e.g. " ET" 

402 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC") 

403 

404 # log.debug("readHandInfo: %s" % info) 

405 for key in info: 

406 if key == "HID": 

407 hand.handid = info[key] 

408 if key == "TOURNO": 

409 hand.tourNo = info[key] 

410 if key == "TABLE": 

411 if "TABLENO" in info: 

412 hand.tablename = info["TABLENO"] 

413 else: 

414 hand.tablename = info[key] 

415 if key == "BUTTON": 

416 hand.buttonpos = info[key] 

417 if self.re_Cancelled.search(hand.handText): 

418 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid) 

419 

420 def readButton(self, hand): 

421 m = self.re_Button.search(hand.handText) 

422 if m: 

423 hand.buttonpos = int(m.group("BUTTON")) 

424 else: 

425 log.info("readButton: " + ("not found")) 

426 

427 def readPlayerStacks(self, hand): 

428 pre, post = hand.handText.split("*** SUMMARY *") 

429 m = self.re_PlayerInfo.finditer(pre) 

430 for a in m: 

431 hand.addPlayer( 

432 int(a.group("SEAT")), 

433 a.group("PNAME"), 

434 str(Decimal(self.clearMoneyString(a.group("CASH"))) * 100) 

435 if hand.tourNo is not None 

436 else self.clearMoneyString(a.group("CASH")), 

437 ) 

438 

439 def markStreets(self, hand): 

440 # There is no marker between deal and draw in KingsClubPkr A5 single draw 

441 # this upsets the accounting, incorrectly sets handsPlayers.cardxx and 

442 # in consequence the mucked-display is incorrect. 

443 # Attempt to fix by inserting a DRAW marker into the hand text attribute 

444 

445 if hand.gametype["category"] == "a5_1draw": 

446 # isolate the first discard/stand pat line (thanks Carl for the regex) 

447 discard_split = re.split(r"(?:(.+(?: stands pat| discards| draws).+))", hand.handText, re.DOTALL) 

448 if len(hand.handText) == len(discard_split[0]): 

449 # handText was not split, no DRAW street occurred 

450 pass 

451 else: 

452 # DRAW street found, reassemble, with DRAW marker added 

453 discard_split[0] += "*** 1ST DRAW ***\r\n" 

454 hand.handText = "" 

455 for i in discard_split: 

456 hand.handText += i 

457 

458 # PREFLOP = ** Dealing down cards ** 

459 # This re fails if, say, river is missing; then we don't get the ** that starts the river. 

460 if hand.gametype["split"]: 

461 m = re.search( 

462 r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* BOARD 1 - FLOP \*\*\*)|.+)" 

463 r"(\*\*\* BOARD 1 - FLOP \*\*\* (?P<FLOP1>\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* BOARD 2 - FLOP \*\*\*)|.+))?" 

464 r"(\*\*\* BOARD 2 - FLOP \*\*\* (?P<FLOP2>\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* BOARD 1 - TURN \*\*\*)|.+))?" 

465 r"(\*\*\* BOARD 1 - TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN1>\[\S\S\].+(?=\*\*\* BOARD 2 - TURN \*\*\*)|.+))?" 

466 r"(\*\*\* BOARD 2 - TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN2>\[\S\S\].+(?=\*\*\* BOARD 1 - RIVER \*\*\*)|.+))?" 

467 r"(\*\*\* BOARD 1 - RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER1>\[\S\S\].+?(?=\*\*\* BOARD 2 - RIVER \*\*\*)|.+))?" 

468 r"(\*\*\* BOARD 2 - RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?", 

469 hand.handText, 

470 re.DOTALL, 

471 ) 

472 elif hand.gametype["category"] == "drawmaha": 

473 m = re.search( 

474 r"(?P<DEAL>.+(?=\*\*\* FLOP \*\*\*)|.+)" 

475 r"(\*\*\* FLOP \*\*\*(?P<DRAWONE> (\[\S\S\] )?\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?" 

476 r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<DRAWTWO>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?" 

477 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<DRAWTHREE>\[\S\S\].+))?", 

478 hand.handText, 

479 re.DOTALL, 

480 ) 

481 elif hand.gametype["base"] in ("hold"): 

482 arr = hand.handText.split("*** HOLE CARDS ***") 

483 if len(arr) > 1: 

484 pre, post = arr 

485 else: 

486 post = arr[0] 

487 m = re.search( 

488 r"(?P<PREFLOP>(.+(?P<FLOPET>\[\S\S\]))?.+(?=\*\*\* FLOP \*\*\*)|.+)" 

489 r"(\*\*\* FLOP \*\*\*(?P<FLOP> (\[\S\S\] )?\[(\S\S ?)?\S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?" 

490 r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?" 

491 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", 

492 post, 

493 re.DOTALL, 

494 ) 

495 

496 elif hand.gametype["base"] in ("stud"): 

497 arr = hand.handText.split("*** 3RD STREET ***") 

498 if self.re_BringIn.search(arr[0]): 

499 m = re.search( 

500 r"(?P<THIRD>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" 

501 r"(\*\*\* 3RD STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?" 

502 r"(\*\*\* 4TH STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 5TH STREET \*\*\*)|.+))?" 

503 r"(\*\*\* 5TH STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* 6TH STREET \*\*\*)|.+))?" 

504 r"(\*\*\* 6TH STREET \*\*\*(?P<SEVENTH>.+))?", 

505 hand.handText, 

506 re.DOTALL, 

507 ) 

508 else: 

509 m = re.search( 

510 r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" 

511 r"(\*\*\* 3RD STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4TH STREET \*\*\*)|.+))?" 

512 r"(\*\*\* 4TH STREET \*\*\*(?P<FOURTH>.+(?=\*\*\* 5TH STREET \*\*\*)|.+))?" 

513 r"(\*\*\* 5TH STREET \*\*\*(?P<FIFTH>.+(?=\*\*\* 6TH STREET \*\*\*)|.+))?" 

514 r"(\*\*\* 6TH STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* 7TH STREET \*\*\*)|.+))?" 

515 r"(\*\*\* 7TH STREET \*\*\*(?P<SEVENTH>.+))?", 

516 hand.handText, 

517 re.DOTALL, 

518 ) 

519 elif hand.gametype["base"] in ("draw"): 

520 if hand.gametype["category"] in ("27_1draw", "fivedraw"): 

521 m = re.search( 

522 r"(?P<PREDEAL>.+(?=\*\*\* 1ST BETTING ROUND \*\*\*)|.+)" 

523 r"(\*\*\* 1ST BETTING ROUND \*\*\*(?P<DEAL>.+(?=\*\*\* 1ST DRAW \*\*\*)|.+))?" 

524 r"(\*\*\* 1ST DRAW \*\*\*(?P<DRAWONE>.+))?", 

525 hand.handText, 

526 re.DOTALL, 

527 ) 

528 elif hand.gametype["category"] == "a5_1draw": 

529 m = re.search( 

530 r"(?P<DEAL>.+(?=\*\*\* 1ST DRAW \*\*\*)|.+)" r"(\*\*\* 1ST DRAW \*\*\*(?P<DRAWONE>.+))?", 

531 hand.handText, 

532 re.DOTALL, 

533 ) 

534 else: 

535 m = re.search( 

536 r"(?P<PREDEAL>.+(?=\*\*\* 1ST BETTING ROUND \*\*\*)|.+)" 

537 r"(\*\*\* 1ST BETTING ROUND \*\*\*(?P<DEAL>.+(?=\*\*\* 1ST DRAW \*\*\*)|.+))?" 

538 r"(\*\*\* 1ST DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* 2ND DRAW \*\*\*)|.+))?" 

539 r"(\*\*\* 2ND DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* 3RD DRAW \*\*\*)|.+))?" 

540 r"(\*\*\* 3RD DRAW \*\*\*(?P<DRAWTHREE>.+))?", 

541 hand.handText, 

542 re.DOTALL, 

543 ) 

544 hand.addStreets(m) 

545 if hand.gametype["base"] in ("hold") and not hand.gametype["split"]: 

546 m1 = re.search( 

547 r"(\*\*\* BOARD 1 - RIVER \*\*\* \[(?P<FLOP1>\S\S \S\S \S\S) (?P<TURN1>\S\S)] (?P<RIVER1>\[\S\S\].+(?=\*\*\* BOARD 2 - RIVER \*\*\*)|.+))" 

548 r"(\*\*\* BOARD 2 - RIVER \*\*\* \[(?P<FLOP2>(\S\S|\-) (\S\S|\-) (\S\S|\-)) (?P<TURN2>(\S\S|\-))] (?P<RIVER2>\[\S\S\].+))", 

549 post, 

550 re.DOTALL, 

551 ) 

552 if m1: 

553 if hand.streets.get("FLOP") is None: 

554 hand.streets.update({"FLOP1": m1.group("FLOP1"), "FLOP2": m1.group("FLOP2")}) 

555 if hand.streets.get("TURN") is None: 

556 hand.streets.update({"TURN1": m1.group("TURN1"), "TURN2": m1.group("TURN2")}) 

557 hand.streets.update({"RIVER1": m1.group("RIVER1"), "RIVER2": m1.group("RIVER2")}) 

558 else: 

559 m2 = re.search( 

560 r"(\*\*\* RIVER \*\*\* \[(?P<FLOP>\S\S \S\S \S\S) (?P<TURN>\S\S)] (?P<RIVER>\[\S\S\].+(?=\*\*\* SUMMARY \*\s?\*\*)|.+))", 

561 post, 

562 re.DOTALL, 

563 ) 

564 if m2: 

565 if hand.streets.get("FLOP") is None: 

566 hand.streets.update({"FLOP": m2.group("FLOP")}) 

567 if hand.streets.get("TURN") is None: 

568 hand.streets.update({"TURN": m2.group("TURN")}) 

569 hand.streets.update({"RIVER": m2.group("RIVER")}) 

570 

571 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand 

572 if ( 

573 street != "FLOPET" or hand.streets.get("FLOP") is None 

574 ): # a list of streets which get dealt community cards (i.e. all but PREFLOP) 

575 if street in ("FLOP1", "TURN1", "FLOP2", "TURN2") and not hand.gametype["split"]: 

576 hand.setCommunityCards(street, hand.streets[street].split(" ")) 

577 else: 

578 m = self.re_Board.search(hand.streets[street]) 

579 if m: 

580 hand.setCommunityCards(street, m.group("CARDS").split(" ")) 

581 elif street in ("FLOP", "TURN"): 

582 hand.setCommunityCards(street, hand.streets[street].split(" ")) 

583 if street in ("FLOP1", "TURN1", "RIVER1", "FLOP2", "TURN2", "RIVER2"): 

584 hand.runItTimes = 2 

585 

586 def readSTP(self, hand): 

587 log.debug(("read Splash the Pot")) 

588 m = self.re_STP.search(hand.handText) 

589 if m: 

590 hand.addSTP(m.group("AMOUNT")) 

591 

592 def readAntes(self, hand): 

593 log.debug("reading antes") 

594 m = self.re_Antes.finditer(hand.handText) 

595 pnames = set([]) 

596 for player in m: 

597 # ~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE'))) 

598 if player.group("PNAME") in pnames and hand.gametype["category"] == "6_holdem": 

599 hand.addBlind( 

600 player.group("PNAME"), 

601 "button blind", 

602 str(Decimal(self.clearMoneyString(player.group("ANTE"))) * 100) 

603 if hand.tourNo is not None 

604 else self.clearMoneyString(player.group("ANTE")), 

605 ) 

606 else: 

607 hand.addAnte( 

608 player.group("PNAME"), 

609 str(Decimal(self.clearMoneyString(player.group("ANTE"))) * 100) 

610 if hand.tourNo is not None 

611 else self.clearMoneyString(player.group("ANTE")), 

612 ) 

613 pnames.add(player.group("PNAME")) 

614 

615 def readBringIn(self, hand): 

616 m = self.re_BringIn.search(hand.handText, re.DOTALL) 

617 if m: 

618 # ~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN'))) 

619 hand.addBringIn( 

620 m.group("PNAME"), 

621 str(Decimal(self.clearMoneyString(m.group("BRINGIN"))) * 100) 

622 if hand.tourNo is not None 

623 else self.clearMoneyString(m.group("BRINGIN")), 

624 ) 

625 

626 def readBlinds(self, hand): 

627 for a in self.re_PostSB.finditer(hand.handText): 

628 hand.addBlind( 

629 a.group("PNAME"), 

630 "small blind", 

631 str(Decimal(self.clearMoneyString(a.group("SB"))) * 100) 

632 if hand.tourNo is not None 

633 else self.clearMoneyString(a.group("SB")), 

634 ) 

635 for a in self.re_PostBB.finditer(hand.handText): 

636 hand.addBlind( 

637 a.group("PNAME"), 

638 "big blind", 

639 str(Decimal(self.clearMoneyString(a.group("BB"))) * 100) 

640 if hand.tourNo is not None 

641 else self.clearMoneyString(a.group("BB")), 

642 ) 

643 for a in self.re_PostBoth.finditer(hand.handText): 

644 hand.addBlind( 

645 a.group("PNAME"), 

646 "big blind", 

647 str(Decimal(self.clearMoneyString(a.group("SBBB"))) * 100) 

648 if hand.tourNo is not None 

649 else self.clearMoneyString(a.group("SBBB")), 

650 ) 

651 for a in self.re_PostStraddle.finditer(hand.handText): 

652 hand.addBlind( 

653 a.group("PNAME"), 

654 "straddle", 

655 str(Decimal(self.clearMoneyString(a.group("STRADDLE"))) * 100) 

656 if hand.tourNo is not None 

657 else self.clearMoneyString(a.group("STRADDLE")), 

658 ) 

659 

660 def readHoleCards(self, hand): 

661 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause 

662 # we need to grab hero's cards 

663 for street in ("PREFLOP", "DEAL"): 

664 if street in hand.streets.keys(): 

665 m = self.re_HeroCards.finditer(hand.streets[street]) 

666 for found in m: 

667 # if m == None: 

668 # hand.involved = False 

669 # else: 

670 newcards = [x for x in found.group("NEWCARDS").split(" ") if x != "X"] 

671 if len(newcards) > 0: 

672 hand.hero = found.group("PNAME") 

673 _street = "FLOP" if hand.gametype["category"] == "aof_omaha" else street 

674 hand.addHoleCards(_street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True) 

675 

676 for street, text in list(hand.streets.items()): 

677 if not text or street in ("PREFLOP", "DEAL"): 

678 continue # already done these 

679 m = self.re_HeroCards.finditer(hand.streets[street]) 

680 for found in m: 

681 player = found.group("PNAME") 

682 if found.group("NEWCARDS") is None: 

683 newcards = [] 

684 else: 

685 newcards = [x for x in found.group("NEWCARDS").split(" ") if x != "X"] 

686 if found.group("OLDCARDS") is None: 

687 oldcards = [] 

688 else: 

689 oldcards = [x for x in found.group("OLDCARDS").split(" ") if x != "X"] 

690 

691 if street == "THIRD" and len(newcards) == 3: # hero in stud game 

692 hand.hero = player 

693 hand.dealt.add(player) # need this for stud?? 

694 hand.addHoleCards( 

695 street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False 

696 ) 

697 else: 

698 hand.addHoleCards( 

699 street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False 

700 ) 

701 

702 def readAction(self, hand, street): 

703 if hand.gametype["split"] and street in hand.communityStreets: 

704 s = street + "2" 

705 else: 

706 s = street 

707 if not hand.streets[s]: 

708 return 

709 m = self.re_Action.finditer(hand.streets[s]) 

710 for action in m: 

711 action.groupdict() 

712 if ( 

713 action.group("ATYPE") in (" discards", " stands pat", " draws") 

714 and hand.gametype["category"] == "drawmaha" 

715 ): 

716 street = "DRAWTWO" 

717 # log.error("DEBUG: %s acts: %s" % (street, acts)) 

718 if action.group("ATYPE") == " folds": 

719 hand.addFold(street, action.group("PNAME")) 

720 elif action.group("ATYPE") == " checks": 

721 hand.addCheck(street, action.group("PNAME")) 

722 elif action.group("ATYPE") == " calls": 

723 hand.addCallTo( 

724 street, 

725 action.group("PNAME"), 

726 str(Decimal(self.clearMoneyString(action.group("BET"))) * 100) 

727 if hand.tourNo is not None 

728 else self.clearMoneyString(action.group("BET")), 

729 ) 

730 elif action.group("ATYPE") == " raises": 

731 if action.group("BETTO") is not None: 

732 hand.addRaiseTo( 

733 street, 

734 action.group("PNAME"), 

735 str(Decimal(self.clearMoneyString(action.group("BETTO"))) * 100) 

736 if hand.tourNo is not None 

737 else self.clearMoneyString(action.group("BETTO")), 

738 ) 

739 elif action.group("BET") is not None: 

740 hand.addCallandRaise( 

741 street, 

742 action.group("PNAME"), 

743 str(Decimal(self.clearMoneyString(action.group("BET"))) * 100) 

744 if hand.tourNo is not None 

745 else self.clearMoneyString(action.group("BET")), 

746 ) 

747 elif action.group("ATYPE") == " bets": 

748 if street in ("PREFLOP", "THIRD", "DEAL"): 

749 hand.addRaiseTo( 

750 street, 

751 action.group("PNAME"), 

752 str(Decimal(self.clearMoneyString(action.group("BET"))) * 100) 

753 if hand.tourNo is not None 

754 else self.clearMoneyString(action.group("BET")), 

755 ) 

756 else: 

757 hand.addBet( 

758 street, 

759 action.group("PNAME"), 

760 str(Decimal(self.clearMoneyString(action.group("BET"))) * 100) 

761 if hand.tourNo is not None 

762 else self.clearMoneyString(action.group("BET")), 

763 ) 

764 elif action.group("ATYPE") == " discards": 

765 hand.addDiscard( 

766 street, action.group("PNAME"), len(action.group("CARDS").split(" ")), action.group("CARDS") 

767 ) 

768 if action.group("DRAWS1") is not None: 

769 player = action.group("PNAME") 

770 newcards = [x for x in action.group("DRAWS1").split(" ") if x != "X"] 

771 discards = action.group("CARDS").split(" ") 

772 laststreet = hand.allStreets[hand.allStreets.index(street) - 1] 

773 oldcards = [x for x in hand.join_holecards(player, True, laststreet) if x not in discards] 

774 hand.addHoleCards( 

775 street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False 

776 ) 

777 elif action.group("ATYPE") == " draws": 

778 hand.addDiscard(street, action.group("PNAME"), self.clearMoneyString(action.group("BET"))) 

779 elif action.group("ATYPE") == " stands pat": 

780 hand.addStandsPat(street, action.group("PNAME"), action.group("CARDS")) 

781 else: 

782 log.debug( 

783 ("DEBUG:") 

784 + " " 

785 + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group("PNAME"), action.group("ATYPE")) 

786 ) 

787 

788 def readShowdownActions(self, hand): 

789 pass 

790 

791 def readCollectPot(self, hand): 

792 if (hand.gametype["category"] == "27_1draw" and hand.gametype["limitType"] == "nl") or hand.gametype[ 

793 "base" 

794 ] == "stud": 

795 hand.adjustCollected = False 

796 else: 

797 hand.adjustCollected = True 

798 for m in self.re_CollectPot.finditer(hand.handText): 

799 pot = ( 

800 str(Decimal(self.clearMoneyString(m.group("POT"))) * 100) 

801 if hand.tourNo is not None 

802 else self.clearMoneyString(m.group("POT")) 

803 ) 

804 hand.addCollectPot(player=m.group("PNAME"), pot=pot) 

805 for m in self.re_Rake.finditer(hand.handText): 

806 if hand.rakes.get("rake"): 

807 hand.rakes["rake"] += Decimal(self.clearMoneyString(m.group("RAKE"))) 

808 else: 

809 hand.rakes["rake"] = Decimal(self.clearMoneyString(m.group("RAKE"))) 

810 

811 def readShownCards(self, hand): 

812 runIt = False 

813 for shows in self.re_ShowdownAction.finditer(hand.handText): 

814 player = shows.group("PNAME").replace("Run 1: ", "") 

815 if "Run 2: " in shows.group("PNAME"): 

816 runIt = True 

817 else: 

818 cards = [x for x in shows.group("CARDS").split(" ") if x != "X"] 

819 hand.addShownCards(cards, player, shown=True, mucked=False) 

820 if runIt: 

821 hand.streetList += ["DRAWTWO"] 

822 hand.allStreets += ["DRAWTWO"] 

823 hand.holeStreets += ["DRAWTWO"] 

824 hand.actionStreets += ["DRAWTWO"] 

825 hand.streets["DRAWTWO"] = "" 

826 hand.actions["DRAWTWO"] = [] 

827 hand.holecards["DRAWTWO"] = {} 

828 for shows in self.re_ShowdownAction.finditer(hand.handText): 

829 if "Run 2: " in shows.group("PNAME"): 

830 cards = [x for x in shows.group("CARDS").split(" ") if x != "X"] 

831 hand.addShownCards(cards, shows.group("PNAME").replace("Run 2: ", ""), shown=True, mucked=False)