Coverage for KingsClubToFpdb.py: 0%

324 statements  

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

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 

26import sys 

27from HandHistoryConverter import * 

28from decimal_wrapper import Decimal 

29 

30# KingsClub HH Format 

31 

32class KingsClub(HandHistoryConverter): 

33 

34 # Class Variables 

35 

36 sitename = "KingsClub" 

37 filetype = "text" 

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

39 siteId = 28 # Needs to match id entry in Sites database 

40 sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "\xe2\x82\xac", "GBP": "\£", "play": "", "INR": "\₹", "CNY": "\¥"} # ADD Euro, Sterling, etc HERE 

41 substitutions = { 

42 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP|SC|INR|CNY", # legal ISO currency codes 

43 'LS' : u"\$|\xe2\x82\xac|\u20ac|\£|\u20b9|\¥|", # legal currency symbols - Euro(cp1252, utf-8) 

44 'PLYR': r'\s?(?P<PNAME>.+?)', 

45 'CUR': u"(\$|\xe2\x82\xac|\u20ac||\£|\u20b9|\¥|)", 

46 'BRKTS': r'(\(button\) |\(small blind\) |\(big blind\) |\(button blind\) |\(button\) \(small blind\) |\(small blind/button\) |\(button\) \(big blind\) )?', 

47 } 

48 

49 # translations from captured groups to fpdb info strings 

50 Lim_Blinds = { '0.04': ('0.01', '0.02'), '0.08': ('0.02', '0.04'), 

51 '0.10': ('0.02', '0.05'), '0.20': ('0.05', '0.10'), 

52 '0.40': ('0.10', '0.20'), '0.50': ('0.10', '0.25'), 

53 '1.00': ('0.25', '0.50'), '1': ('0.25', '0.50'), 

54 '2.00': ('0.50', '1.00'), '2': ('0.50', '1.00'), 

55 '4.00': ('1.00', '2.00'), '4': ('1.00', '2.00'), 

56 '6.00': ('1.00', '3.00'), '6': ('1.00', '3.00'), 

57 '8.00': ('2.00', '4.00'), '8': ('2.00', '4.00'), 

58 '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'), 

59 '16.00': ('4.00', '8.00'), '16': ('4.00', '8.00'), 

60 '20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'), 

61 '30.00': ('10.00', '15.00'), '30': ('10.00', '15.00'), 

62 '40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'), 

63 '50.00': ('10.00', '25.00'), '50': ('10.00', '25.00'), 

64 '60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'), 

65 '80.00': ('20.00', '40.00'), '80': ('20.00', '40.00'), 

66 '100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'), 

67 '150.00': ('50.00', '75.00'), '150': ('50.00', '75.00'), 

68 '200.00': ('50.00', '100.00'), '200': ('50.00', '100.00'), 

69 '400.00': ('100.00', '200.00'), '400': ('100.00', '200.00'), 

70 '500.00': ('100.00', '250.00'), '500': ('100.00', '250.00'), 

71 '600.00': ('150.00', '300.00'), '600': ('150.00', '300.00'), 

72 '800.00': ('200.00', '400.00'), '800': ('200.00', '400.00'), 

73 '1000.00': ('250.00', '500.00'), '1000': ('250.00', '500.00'), 

74 '2000.00': ('500.00', '1000.00'), '2000': ('500.00', '1000.00'), 

75 '4000.00': ('1000.00', '2000.00'), '4000': ('1000.00', '2000.00'), 

76 '10000.00': ('2500.00', '5000.00'), '10000': ('2500.00', '5000.00'), 

77 '20000.00': ('5000.00', '10000.00'),'20000': ('5000.00', '10000.00'), 

78 '40000.00': ('10000.00', '20000.00'),'40000': ('10000.00', '20000.00'), 

79 } 

80 

81 limits = { 

82 'No Limit':'nl', 

83 'Pot Limit':'pl', 

84 'Limit':'fl' 

85 } 

86 games = { # base, category 

87 'Holdem': ('hold', 'holdem'), 

88 'Omaha': ('hold', 'omahahi'), 

89 'Omaha Hi-Lo': ('hold', 'omahahilo'), 

90 'Big O': ('hold', '5_omaha8'), 

91 'Omaha 5 Card': ('hold', '5_omahahi'), 

92 'Omaha 6 Card': ('hold', '6_omahahi'), 

93 'Short Deck Holdem': ('hold', '6_holdem'), 

94 'Razz': ('stud', 'razz'), 

95 'Seven Card Stud': ('stud', 'studhi'), 

96 'Seven Card Stud Hi-Lo': ('stud', 'studhilo'), 

97 'Badugi': ('draw', 'badugi'), 

98 '2-7 Triple Draw': ('draw', '27_3draw'), 

99 '2-7 Single Draw': ('draw', '27_1draw'), 

100 '5 Card Draw': ('draw', 'fivedraw'), 

101 'A-5 Triple Draw': ('draw', 'a5_3draw'), 

102 'A-5 Single Draw': ('draw', 'a5_1draw'), 

103 '2-7 Razz': ('stud', '27_razz'), 

104 'Badacey': ('draw', 'badacey'), 

105 'Badeucey': ('draw', 'badeucey'), 

106 '2-7 Drawmaha': ('draw', 'drawmaha'), 

107 'Captain': ('draw', 'drawmaha') 

108 } 

109 mixes = { 

110 'HORSE': 'horse', 

111 '8-Game': '8game', 

112 '8-GAME': '8game', 

113 'HOSE': 'hose', 

114 'Mixed PLH/PLO': 'plh_plo', 

115 'Mixed NLH/PLO': 'nlh_plo', 

116 'Mixed Omaha H/L': 'plo_lo', 

117 'Mixed Hold\'em': 'mholdem', 

118 'Mixed Omaha': 'momaha', 

119 'Triple Stud': '3stud' 

120 } # Legal mixed games 

121 currencies = { u'€':'EUR', '$':'USD', '':'T$', u'£':'GBP', u'¥':'CNY', u'₹':'INR'} 

122 

123 # Static regexes 

124 re_GameInfo = re.compile(u""" 

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

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

127 (?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 

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

129 """ % substitutions, re.MULTILINE|re.VERBOSE) 

130 

131 re_PlayerInfo = re.compile(u""" 

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

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

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

135 \)""" % substitutions, 

136 re.MULTILINE|re.VERBOSE) 

137 

138 re_HandInfo = re.compile(""" 

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

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

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

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

143 re.MULTILINE|re.VERBOSE) 

144 

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

146 

147 re_Identify = re.compile(u'^\#\d+:') 

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

149 re_TailSplitHands = re.compile('(\n\n\n+)') 

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

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

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

153 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]+)""", re.MULTILINE) 

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

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

156 

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

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

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

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

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

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

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

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

165 re_Action = re.compile(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*$""" % substitutions, re.MULTILINE|re.VERBOSE) 

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

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

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

169 #bd43 wins (279.50) from pot 

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

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

172 re_WinningRankOne = re.compile(u"^%(PLYR)s wins the tournament and receives %(CUR)s(?P<AMT>[,\.0-9]+) - congratulations!$" % substitutions, re.MULTILINE) 

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

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

175 re_Cancelled = re.compile('Hand\scancelled', re.MULTILINE) 

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

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

178 #ChazDazzle wins the 22000 bounty for eliminating berkovich609 

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

180 re_Bounty = re.compile(u"^%(PLYR)s (?P<SPLIT>split|wins) the %(CUR)s(?P<AMT>[,\.0-9]+) bounty for eliminating (?P<ELIMINATED>.+?)$" % substitutions, re.MULTILINE) 

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

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

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

184 re_Progressive = re.compile(u""" 

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

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

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

188 % substitutions, re.MULTILINE|re.VERBOSE) 

189 

190 re_STP = re.compile(u""" 

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

192 % substitutions, re.MULTILINE|re.VERBOSE) 

193 

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

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

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

197 

198 def compilePlayerRegexs(self, hand): 

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

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

201 self.compiledPlayers = players 

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

203 subst = { 

204 'PLYR': player_re, 

205 'BRKTS': r'(\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) |\(button\) \(big blind\) )?', 

206 'CUR': u"(\$|\xe2\x82\xac|\u20ac||\£|)" 

207 } 

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

209 

210 def readSupportedGames(self): 

211 return [["ring", "hold", "nl"], 

212 ["ring", "hold", "pl"], 

213 ["ring", "hold", "fl"], 

214 ["ring", "hold", "pn"], 

215 

216 ["ring", "stud", "fl"], 

217 

218 ["ring", "draw", "fl"], 

219 ["ring", "draw", "pl"], 

220 ["ring", "draw", "nl"], 

221 

222 ["tour", "hold", "nl"], 

223 ["tour", "hold", "pl"], 

224 ["tour", "hold", "fl"], 

225 ["tour", "hold", "pn"], 

226 

227 ["tour", "stud", "fl"], 

228 

229 ["tour", "draw", "fl"], 

230 ["tour", "draw", "pl"], 

231 ["tour", "draw", "nl"], 

232 ] 

233 

234 def determineGameType(self, handText): 

235 info = {} 

236 m = self.re_GameInfo.search(handText) 

237 if not m: 

238 tmp = handText[0:200] 

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

240 raise FpdbParseError 

241 

242 mg = m.groupdict() 

243 if 'LIMIT' in mg: 

244 info['limitType'] = self.limits[mg['LIMIT']] 

245 if 'GAME' in mg: 

246 (info['base'], info['category']) = self.games[mg['GAME']] 

247 if 'SB' in mg and mg['SB'] is not None: 

248 info['sb'] = self.clearMoneyString(mg['SB']) 

249 if 'BB' in mg and mg['BB'] is not None: 

250 info['bb'] = self.clearMoneyString(mg['BB']) 

251 

252 m1 = self.re_TourNo.search(handText) 

253 if m1: 

254 info['type'] = 'tour' 

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

256 else: 

257 info['type'] = 'ring' 

258 info['currency'] = 'USD' 

259 

260 m2 = self.re_Split.search(handText) 

261 if m2: 

262 info['split'] = True 

263 else: 

264 info['split'] = False 

265 

266 m3 = self.re_Table.search(handText) 

267 if m3 and 'AOF' in m3.group('TABLE'): 

268 info['category'] = 'aof_omaha' 

269 

270 if info['limitType'] == 'fl' and info['bb'] is not None: 

271 if info['type'] == 'ring': 

272 try: 

273 info['sb'] = self.Lim_Blinds[self.clearMoneyString(mg['BB'])][0] 

274 info['bb'] = self.Lim_Blinds[self.clearMoneyString(mg['BB'])][1] 

275 except KeyError: 

276 info['sb'] = str((old_div(Decimal(self.clearMoneyString(mg['SB'])),2)).quantize(Decimal("0.01"))) 

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

278 #tmp = handText[0:200] 

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

280 #raise FpdbParseError 

281 else: 

282 info['sb'] = str((old_div(Decimal(self.clearMoneyString(mg['SB'])),2)).quantize(Decimal("0.01"))) 

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

284 

285 return info 

286 

287 def readHandInfo(self, hand): 

288 #First check if partial 

289 if hand.handText.count('*** SUMMARY *') != 1: 

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

291 

292 info = {} 

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

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

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

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

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

298 tmp = hand.handText[0:200] 

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

300 raise FpdbParseError 

301 

302 info.update(m.groupdict()) 

303 info.update(m2.groupdict()) 

304 if m3 is not None: 

305 info.update(m3.groupdict()) 

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

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

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

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

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

311 for a in m1: 

312 datetimestr = "%s/%s/%s %s:%s:%s" % (a.group('Y'), a.group('M'),a.group('D'),a.group('H'),a.group('MIN'),a.group('S')) 

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

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

315 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET" 

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

317 

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

319 for key in info: 

320 if key == 'HID': 

321 hand.handid = info[key] 

322 if key == 'TOURNO': 

323 hand.tourNo = info[key] 

324 if key == 'TABLE': 

325 if 'TABLENO' in info: 

326 hand.tablename = info['TABLENO'] 

327 else: 

328 hand.tablename = info[key] 

329 if key == 'BUTTON': 

330 hand.buttonpos = info[key] 

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

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

333 

334 def readButton(self, hand): 

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

336 if m: 

337 hand.buttonpos = int(m.group('BUTTON')) 

338 else: 

339 log.info('readButton: ' + ('not found')) 

340 

341 def readPlayerStacks(self, hand): 

342 pre, post = hand.handText.split('*** SUMMARY *') 

343 m = self.re_PlayerInfo.finditer(pre) 

344 for a in m: 

345 hand.addPlayer( 

346 int(a.group('SEAT')), 

347 a.group('PNAME'), 

348 str(Decimal(self.clearMoneyString(a.group('CASH')))*100) if hand.tourNo is not None else self.clearMoneyString(a.group('CASH')) 

349 ) 

350 

351 def markStreets(self, hand): 

352 

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

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

355 # in consequence the mucked-display is incorrect. 

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

357 

358 if hand.gametype['category'] == 'a5_1draw': 

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

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

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

362 # handText was not split, no DRAW street occurred 

363 pass 

364 else: 

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

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

367 hand.handText = "" 

368 for i in discard_split: 

369 hand.handText += i 

370 

371 # PREFLOP = ** Dealing down cards ** 

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

373 if hand.gametype['split']: 

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

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

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

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

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

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

380 r"(\*\*\* BOARD 2 - RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER2>\[\S\S\].+))?", hand.handText,re.DOTALL) 

381 elif hand.gametype['category'] == 'drawmaha': 

382 m = re.search(r"(?P<DEAL>.+(?=\*\*\* FLOP \*\*\*)|.+)" 

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

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

385 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<DRAWTHREE>\[\S\S\].+))?", hand.handText,re.DOTALL) 

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

387 arr = hand.handText.split('*** HOLE CARDS ***') 

388 if len(arr) > 1: 

389 pre, post = arr 

390 else: 

391 post = arr[0] 

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

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

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

395 r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", post,re.DOTALL) 

396 

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

398 arr = hand.handText.split('*** 3RD STREET ***') 

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

400 m = re.search(r"(?P<THIRD>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" 

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

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

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

404 r"(\*\*\* 6TH STREET \*\*\*(?P<SEVENTH>.+))?", hand.handText, re.DOTALL) 

405 else: 

406 m = re.search(r"(?P<ANTES>.+(?=\*\*\* 3RD STREET \*\*\*)|.+)" 

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

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

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

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

411 r"(\*\*\* 7TH STREET \*\*\*(?P<SEVENTH>.+))?", hand.handText, re.DOTALL) 

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

413 if hand.gametype['category'] in ('27_1draw', 'fivedraw'): 

414 m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* 1ST BETTING ROUND \*\*\*)|.+)" 

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

416 r"(\*\*\* 1ST DRAW \*\*\*(?P<DRAWONE>.+))?", hand.handText,re.DOTALL) 

417 elif hand.gametype['category'] == 'a5_1draw': 

418 m = re.search(r"(?P<DEAL>.+(?=\*\*\* 1ST DRAW \*\*\*)|.+)" 

419 r"(\*\*\* 1ST DRAW \*\*\*(?P<DRAWONE>.+))?", hand.handText,re.DOTALL) 

420 else: 

421 m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* 1ST BETTING ROUND \*\*\*)|.+)" 

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

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

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

425 r"(\*\*\* 3RD DRAW \*\*\*(?P<DRAWTHREE>.+))?", hand.handText,re.DOTALL) 

426 hand.addStreets(m) 

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

428 m1 = re.search( 

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

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

431 if m1: 

432 if hand.streets.get('FLOP') is None: 

433 hand.streets.update({'FLOP1': m1.group('FLOP1'),'FLOP2': m1.group('FLOP2')}) 

434 if hand.streets.get('TURN') is None: 

435 hand.streets.update({'TURN1': m1.group('TURN1'),'TURN2': m1.group('TURN2')}) 

436 hand.streets.update({'RIVER1': m1.group('RIVER1'), 'RIVER2': m1.group('RIVER2')}) 

437 else: 

438 m2 = re.search( 

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

440 if m2: 

441 if hand.streets.get('FLOP') is None: 

442 hand.streets.update({'FLOP': m2.group('FLOP')}) 

443 if hand.streets.get('TURN') is None: 

444 hand.streets.update({'TURN': m2.group('TURN')}) 

445 hand.streets.update({'RIVER': m2.group('RIVER')}) 

446 

447 

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

449 if street!='FLOPET' or hand.streets.get('FLOP')==None: # a list of streets which get dealt community cards (i.e. all but PREFLOP) 

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

451 hand.setCommunityCards(street, hand.streets[street].split(' ')) 

452 else: 

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

454 if m: 

455 hand.setCommunityCards(street, m.group('CARDS').split(' ')) 

456 elif street in ('FLOP', 'TURN'): 

457 hand.setCommunityCards(street, hand.streets[street].split(' ')) 

458 if street in ('FLOP1', 'TURN1', 'RIVER1', 'FLOP2', 'TURN2', 'RIVER2'): 

459 hand.runItTimes = 2 

460 

461 def readSTP(self, hand): 

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

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

464 if m: 

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

466 

467 def readAntes(self, hand): 

468 log.debug("reading antes") 

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

470 pnames = set([]) 

471 for player in m: 

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

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

474 hand.addBlind( 

475 player.group('PNAME'), 

476 'button blind', 

477 str(Decimal(self.clearMoneyString(player.group('ANTE')))*100) if hand.tourNo is not None else self.clearMoneyString(player.group('ANTE')) 

478 ) 

479 else: 

480 hand.addAnte( 

481 player.group('PNAME'), 

482 str(Decimal(self.clearMoneyString(player.group('ANTE')))*100) if hand.tourNo is not None else self.clearMoneyString(player.group('ANTE')) 

483 ) 

484 pnames.add(player.group('PNAME')) 

485 

486 def readBringIn(self, hand): 

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

488 if m: 

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

490 hand.addBringIn( 

491 m.group('PNAME'), 

492 str(Decimal(self.clearMoneyString(m.group('BRINGIN')))*100) if hand.tourNo is not None else self.clearMoneyString(m.group('BRINGIN')) 

493 ) 

494 

495 def readBlinds(self, hand): 

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

497 hand.addBlind( 

498 a.group('PNAME'), 

499 'small blind', 

500 str(Decimal(self.clearMoneyString(a.group('SB')))*100) if hand.tourNo is not None else self.clearMoneyString(a.group('SB')) 

501 ) 

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

503 hand.addBlind( 

504 a.group('PNAME'), 

505 'big blind', 

506 str(Decimal(self.clearMoneyString(a.group('BB')))*100) if hand.tourNo is not None else self.clearMoneyString(a.group('BB')) 

507 ) 

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

509 hand.addBlind( 

510 a.group('PNAME'), 

511 'big blind', 

512 str(Decimal(self.clearMoneyString(a.group('SBBB')))*100) if hand.tourNo is not None else self.clearMoneyString(a.group('SBBB')) 

513 ) 

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

515 hand.addBlind( 

516 a.group('PNAME'), 

517 'straddle', 

518 str(Decimal(self.clearMoneyString(a.group('STRADDLE')))*100) if hand.tourNo is not None else self.clearMoneyString(a.group('STRADDLE')) 

519 ) 

520 

521 def readHoleCards(self, hand): 

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

523# we need to grab hero's cards 

524 for street in ('PREFLOP', 'DEAL'): 

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

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

527 for found in m: 

528# if m == None: 

529# hand.involved = False 

530# else: 

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

532 if len(newcards)>0: 

533 hand.hero = found.group('PNAME') 

534 _street = 'FLOP' if hand.gametype['category'] == 'aof_omaha' else street 

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

536 

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

538 if not text or street in ('PREFLOP', 'DEAL'): continue # already done these 

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

540 for found in m: 

541 player = found.group('PNAME') 

542 if found.group('NEWCARDS') is None: 

543 newcards = [] 

544 else: 

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

546 if found.group('OLDCARDS') is None: 

547 oldcards = [] 

548 else: 

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

550 

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

552 hand.hero = player 

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

554 hand.addHoleCards(street, player, closed=newcards[0:2], open=[newcards[2]], shown=False, mucked=False, dealt=False) 

555 else: 

556 hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False) 

557 

558 

559 def readAction(self, hand, street): 

560 if hand.gametype['split'] and street in hand.communityStreets: 

561 s = street + '2' 

562 else: 

563 s = street 

564 if not hand.streets[s]: 

565 return 

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

567 for action in m: 

568 acts = action.groupdict() 

569 if action.group('ATYPE') in (' discards', ' stands pat', ' draws') and hand.gametype['category'] == 'drawmaha': 

570 street = 'DRAWTWO' 

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

572 if action.group('ATYPE') == ' folds': 

573 hand.addFold( street, action.group('PNAME')) 

574 elif action.group('ATYPE') == ' checks': 

575 hand.addCheck( street, action.group('PNAME')) 

576 elif action.group('ATYPE') == ' calls': 

577 hand.addCallTo( 

578 street, 

579 action.group('PNAME'), 

580 str(Decimal(self.clearMoneyString(action.group('BET')))*100) if hand.tourNo is not None else self.clearMoneyString(action.group('BET')) 

581 ) 

582 elif action.group('ATYPE') == ' raises': 

583 if action.group('BETTO') is not None: 

584 hand.addRaiseTo( 

585 street, 

586 action.group('PNAME'), 

587 str(Decimal(self.clearMoneyString(action.group('BETTO')))*100) if hand.tourNo is not None else self.clearMoneyString(action.group('BETTO')) 

588 ) 

589 elif action.group('BET') is not None: 

590 hand.addCallandRaise( 

591 street, 

592 action.group('PNAME'), 

593 str(Decimal(self.clearMoneyString(action.group('BET')))*100) if hand.tourNo is not None else self.clearMoneyString(action.group('BET')) 

594 ) 

595 elif action.group('ATYPE') == ' bets': 

596 if street in ('PREFLOP', 'THIRD', 'DEAL'): 

597 hand.addRaiseTo( 

598 street, 

599 action.group('PNAME'), 

600 str(Decimal(self.clearMoneyString(action.group('BET')))*100) if hand.tourNo is not None else self.clearMoneyString(action.group('BET')) 

601 ) 

602 else: 

603 hand.addBet( 

604 street, 

605 action.group('PNAME'), 

606 str(Decimal(self.clearMoneyString(action.group('BET')))*100) if hand.tourNo is not None else self.clearMoneyString(action.group('BET')) 

607 ) 

608 elif action.group('ATYPE') == ' discards': 

609 hand.addDiscard(street, action.group('PNAME'), len(action.group('CARDS').split(" ")), action.group('CARDS')) 

610 if action.group('DRAWS1') is not None: 

611 player = action.group('PNAME') 

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

613 discards = action.group('CARDS').split(' ') 

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

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

616 hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False) 

617 elif action.group('ATYPE') == ' draws': 

618 hand.addDiscard(street, action.group('PNAME'), self.clearMoneyString(action.group('BET'))) 

619 elif action.group('ATYPE') == ' stands pat': 

620 hand.addStandsPat( street, action.group('PNAME'), action.group('CARDS')) 

621 else: 

622 log.debug(("DEBUG:") + " " + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group('PNAME'), action.group('ATYPE'))) 

623 

624 

625 def readShowdownActions(self, hand): 

626 pass 

627 

628 def readCollectPot(self,hand): 

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

630 hand.gametype['base'] == 'stud'): 

631 hand.adjustCollected = False 

632 else: 

633 hand.adjustCollected = True 

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

635 pot = str(Decimal(self.clearMoneyString(m.group('POT')))*100) if hand.tourNo is not None else self.clearMoneyString(m.group('POT')) 

636 hand.addCollectPot(player=m.group('PNAME'),pot=pot) 

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

638 if hand.rakes.get('rake'): 

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

640 else: 

641 hand.rakes['rake'] = Decimal(self.clearMoneyString(m.group('RAKE'))) 

642 

643 def readShownCards(self,hand): 

644 runIt = False 

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

646 player = shows.group('PNAME').replace('Run 1: ', '') 

647 if 'Run 2: ' in shows.group('PNAME'): 

648 runIt = True 

649 else: 

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

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

652 if runIt: 

653 hand.streetList += ['DRAWTWO'] 

654 hand.allStreets += ['DRAWTWO'] 

655 hand.holeStreets += ['DRAWTWO'] 

656 hand.actionStreets += ['DRAWTWO'] 

657 hand.streets['DRAWTWO'] = "" 

658 hand.actions['DRAWTWO'] = [] 

659 hand.holecards['DRAWTWO'] = {} 

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

661 if 'Run 2: ' in shows.group('PNAME'): 

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

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

664