Coverage for PacificPokerToFpdb.py: 0%

313 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-14 11:07 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3# 

4# Copyright 2008-2010, 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 

22# import L10n 

23# _ = L10n.get_translation() 

24 

25from HandHistoryConverter import HandHistoryConverter, FpdbParseError 

26from decimal import Decimal 

27import re 

28import logging 

29import datetime 

30 

31 

32# PacificPoker(888) HH Format 

33log = logging.getLogger("parser") 

34 

35 

36class PacificPoker(HandHistoryConverter): 

37 # Class Variables 

38 

39 sitename = "PacificPoker" 

40 filetype = "text" 

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

42 siteId = 10 # Needs to match id entry in Sites database 

43 

44 mixes = {"HORSE": "horse", "8-Game": "8game", "HOSE": "hose"} # Legal mixed games 

45 sym = {"USD": "\$", "CAD": "\$", "T$": "", "EUR": "€", "GBP": "\xa3", "play": ""} # ADD Euro, Sterling, etc HERE 

46 substitutions = { 

47 "LEGAL_ISO": "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes 

48 "PLYR": r"(?P<PNAME>.+?)", 

49 "LS": "\$|€|", # legal currency symbols - Euro(cp1252, utf-8) 

50 "NUM": "\s.,\d\xa0", 

51 "CUR": "(\$|€|)", 

52 } 

53 

54 # translations from captured groups to fpdb info strings 

55 # not needed for PacificPoker 

56 # Lim_Blinds = { '0.01': ('0.01', '0.02'), 

57 # '0.02': ('0.02', '0.04'), 

58 # '0.03': ('0.03', '0.06'), 

59 # '0.05': ('0.05', '0.10'), 

60 # '0.12': ('0.12', '0.25'), 

61 # '0.25': ('0.25', '0.50'), 

62 # '0.50': ('0.50', '1.00'), 

63 # '1.00': ('1.00', '2.00'), '1': ('1.00', '2.00'), 

64 # '2.00': ('2.00', '4.00'), '2': ('2.00', '4.00'), 

65 # '3.00': ('3.00', '6.00'), '3': ('3.00', '6.00'), 

66 # '5.00': ('5.00', '10.00'), '5': ('5.00', '10.00'), 

67 # '10.00': ('10.00', '20.00'), '10': ('10.00', '20.00'), 

68 # '15.00': ('15.00', '30.00'), '15': ('15.00', '30.00'), 

69 # '30.00': ('30.00', '60.00'), '30': ('30.00', '60.00'), 

70 # '50.00': ('50.00', '100.00'), '50': ('50.00', '100.00'), 

71 # '75.00': ('75.00', '150.00'), '75': ('75.00', '150.00'), 

72 # '100.00': ('100.00', '200.00'), '100': ('100.00', '200.00'), 

73 # '200.00': ('200.00', '400.00'), '200': ('200.00', '400.00'), 

74 # '250.00': ('250.00', '500.00'), '250': ('250.00', '500.00') 

75 # } 

76 

77 limits = {"No Limit": "nl", "Pot Limit": "pl", "Limit": "fl", "LIMIT": "fl", "Fix Limit": "fl"} 

78 

79 games = { # base, category 

80 "Hold'em": ("hold", "holdem"), 

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

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

83 "Omaha Hi/Lo": ("hold", "omahahilo"), 

84 "OmahaHL": ("hold", "omahahilo"), 

85 } 

86 

87 currencies = {"€": "EUR", "$": "USD", "": "T$"} 

88 

89 # Static regexes 

90 re_GameInfo = re.compile( 

91 """ 

92 (\#Game\sNo\s:\s[0-9]+\\n)? 

93 \*\*\*\*\*?\s(Cassava|888poker|888)(\.[a-z]{2})?(\-[a-z]{2})?\s(?P<FAST>Snap\sPoker\s)?(BLAST\s)?Hand\sHistory\sfor\sGame\s(?P<HID>[0-9]+)\s\*\*\*\*\*\\n 

94 (?P<CURRENCY1>%(LS)s)?\s?(?P<SB>[%(NUM)s]+)\s?(?P<CURRENCY2>%(LS)s)?/(%(LS)s)?\s?(?P<BB>[%(NUM)s]+)\s?(%(LS)s)?\sBlinds\s 

95 (?P<LIMIT>No\sLimit|Fix\sLimit|Pot\sLimit)\s 

96 (?P<GAME>Holdem|Omaha|OmahaHL|Hold\'em|Omaha\sHi/Lo|OmahaHL) 

97 (\sJackpot\stable)? 

98 \s-\s\*\*\*\s 

99 (?P<DATETIME>.*$)\s 

100 (Tournament\s\#(?P<TOURNO>\d+))? 

101 """ 

102 % substitutions, 

103 re.MULTILINE | re.VERBOSE, 

104 ) 

105 

106 re_PlayerInfo = re.compile( 

107 """ 

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

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

110 \(\s(%(LS)s)?\s?(?P<CASH>[%(NUM)s]+)\s?(%(LS)s)?\s\)""" 

111 % substitutions, 

112 re.MULTILINE | re.VERBOSE, 

113 ) 

114 

115 re_HandInfo = re.compile( 

116 """ 

117 ^( 

118 (Table\s(?P<TABLE>[-\ \#a-zA-Z\d]+?)\s) 

119 | 

120 (Tournament\s\#(?P<TOURNO>\d+)\s 

121 ( 

122 (?P<BUYIN>( 

123 ((?P<BIAMT>(%(LS)s)?\s?[%(NUM)s]+\s?(%(LS)s)?)(\s\+\s?(?P<BIRAKE>(%(LS)s)?\s?[%(NUM)s]+\s?(%(LS)s)?))?) 

124 | 

125 (Free) 

126 | 

127 (.+?) 

128 )) 

129 ) 

130 (\s-\sTable\s\#(?P<TABLEID>\d+))?\s 

131 ) 

132 ) 

133 ((?P<MAX>\d+)\sMax\s)? 

134 (\(Real\sMoney\))? 

135 (?P<PLAY>\((Practice\sPlay|Play\sMoney)\))? 

136 \\n 

137 Seat\s(?P<BUTTON>[0-9]+)\sis\sthe\sbutton 

138 """ 

139 % substitutions, 

140 re.MULTILINE | re.VERBOSE, 

141 ) 

142 

143 re_Identify = re.compile( 

144 "\*{4,5}\s(Cassava|888poker|888)(\.[a-z]{2})?(\-[a-z]{2})?\s(Snap\sPoker\s|BLAST\s)?Hand\sHistory\sfor\sGame\s\d+\s" 

145 ) 

146 re_SplitHands = re.compile("\n\n+") 

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

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

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

150 re_Spanish_10 = re.compile("D([tpeo])") 

151 

152 re_DateTime = re.compile( 

153 """(?P<D>[0-9]{2})\s(?P<M>[0-9]{2})\s(?P<Y>[0-9]{4})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)""", 

154 re.MULTILINE, 

155 ) 

156 

157 short_subst = {"PLYR": r"(?P<PNAME>.+?)", "CUR": "\$?", "NUM": ".,\d\xa0"} 

158 re_PostSB = re.compile( 

159 r"^%(PLYR)s posts small blind \[(%(CUR)s)?\s?(?P<SB>[%(NUM)s]+)\s?(%(CUR)s)?\]" % substitutions, re.MULTILINE 

160 ) 

161 re_PostBB = re.compile( 

162 r"^%(PLYR)s posts big blind \[(%(CUR)s)?\s?(?P<BB>[%(NUM)s]+)\s?(%(CUR)s)?\]" % substitutions, re.MULTILINE 

163 ) 

164 re_Antes = re.compile( 

165 r"^%(PLYR)s posts (the\s)?ante \[(%(CUR)s)?\s?(?P<ANTE>[%(NUM)s]+)\s?(%(CUR)s)?\]" % substitutions, re.MULTILINE 

166 ) 

167 # TODO: unknown in available hand histories for pacificpoker: 

168 re_BringIn = re.compile( 

169 r"^%(PLYR)s: brings[- ]in( low|) for (%(CUR)s)?\s?(?P<BRINGIN>[%(NUM)s]+)\s?(%(CUR)s)?" % substitutions, 

170 re.MULTILINE, 

171 ) 

172 re_PostBoth = re.compile( 

173 r"^%(PLYR)s posts dead blind \[(%(CUR)s)?\s?(?P<SB>[%(NUM)s]+)\s?(%(CUR)s)?\s\+\s(%(CUR)s)?\s?(?P<BB>[%(NUM)s]+)\s?(%(CUR)s)?\]" 

174 % substitutions, 

175 re.MULTILINE, 

176 ) 

177 re_HeroCards = re.compile(r"^Dealt to %(PLYR)s( \[\s(?P<NEWCARDS>.+?)\s\])" % substitutions, re.MULTILINE) 

178 re_Action = re.compile( 

179 r""" 

180 ^%(PLYR)s(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sdiscards|\sstands\spat) 

181 (\s\[(%(CUR)s)?\s?(?P<BET>[%(NUM)s]+)\s?(%(CUR)s)?\])? 

182 (\s*and\sis\sall.in)? 

183 (\s*and\shas\sreached\sthe\s[%(CUR)s\s?\d\.]+\scap)? 

184 (\s*cards?(\s\[(?P<DISCARDED>.+?)\])?)?\s*$""" 

185 % substitutions, 

186 re.MULTILINE | re.VERBOSE, 

187 ) 

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

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

190 re_ShownCards = re.compile( 

191 "^%s ?(?P<SHOWED>shows|mucks) \[ (?P<CARDS>.*) \]$" % substitutions["PLYR"], re.MULTILINE 

192 ) 

193 re_CollectPot = re.compile( 

194 r"^%(PLYR)s collected \[ (%(CUR)s)?\s?(?P<POT>[%(NUM)s]+)\s?(%(CUR)s)? \]$" % substitutions, re.MULTILINE 

195 ) 

196 

197 def compilePlayerRegexs(self, hand): 

198 pass 

199 

200 def readSupportedGames(self): 

201 return [ 

202 ["ring", "hold", "nl"], 

203 ["ring", "hold", "pl"], 

204 ["ring", "hold", "fl"], 

205 ["tour", "hold", "nl"], 

206 ["tour", "hold", "pl"], 

207 ["tour", "hold", "fl"], 

208 ] 

209 

210 def determineGameType(self, handText): 

211 info = {} 

212 m = self.re_GameInfo.search(handText) 

213 if not m: 

214 tmp = handText[0:200] 

215 log.error(("PacificPokerToFpdb.determineGameType: '%s'") % tmp) 

216 raise FpdbParseError 

217 

218 mg = m.groupdict() 

219 # print "DEBUG: mg: ", mg 

220 if "LIMIT" in mg: 

221 # print "DEBUG: re_GameInfo[LIMIT] \'", mg['LIMIT'], "\'" 

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

223 if "GAME" in mg: 

224 # print "DEBUG: re_GameInfo[GAME] \'", mg['GAME'], "\'" 

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

226 if "SB" in mg: 

227 # print "DEBUG: re_GameInfo[SB] \'", mg['SB'], "\'" 

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

229 if "BB" in mg: 

230 # print "DEBUG: re_GameInfo[BB] \'", mg['BB'], "\'" 

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

232 if "CURRENCY1" in mg: 

233 # print "DEBUG: re_GameInfo[CURRENCY] \'", mg['CURRENCY'], "\'" 

234 info["currency"] = self.currencies[mg["CURRENCY1"]] 

235 if "CURRENCY2" in mg and mg["CURRENCY2"]: 

236 # print "DEBUG: re_GameInfo[CURRENCY] \'", mg['CURRENCY'], "\'" 

237 info["currency"] = self.currencies[mg["CURRENCY2"]] 

238 if "FAST" in mg and mg["FAST"] is not None: 

239 info["fast"] = True 

240 

241 if "TOURNO" in mg and mg["TOURNO"] is not None: 

242 info["type"] = "tour" 

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

244 else: 

245 info["type"] = "ring" 

246 

247 # Pacific Poker includes the blind levels in the gametype, the following is not needed. 

248 # if info['limitType'] == 'fl' and info['bb'] is not None and info['type'] == 'ring' and info['base'] != 'stud': 

249 # try: 

250 # info['sb'] = self.Lim_Blinds[mg['BB']][0] 

251 # info['bb'] = self.Lim_Blinds[mg['BB']][1] 

252 # except KeyError: 

253 # log.error(("determineGameType: Lim_Blinds has no lookup for '%s'" % mg['BB'])) 

254 # log.error(("determineGameType: Raising FpdbParseError")) 

255 # raise FpdbParseError(("Lim_Blinds has no lookup for '%s'") % mg['BB']) 

256 

257 return info 

258 

259 def readHandInfo(self, hand): 

260 info = {} 

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

262 if m is None: 

263 log.error("re_HandInfo could not be parsed") 

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

265 if m2 is None: 

266 log.error("re_GameInfo could not be parsed") 

267 if m is None or m2 is None: 

268 tmp = hand.handText[0:200] 

269 log.error(("PacificPokerToFpdb.readHandInfo: '%s'") % tmp) 

270 raise FpdbParseError 

271 

272 info.update(m.groupdict()) 

273 info.update(m2.groupdict()) 

274 

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

276 for key in info: 

277 if key == "DATETIME": 

278 # 28 11 2011 19:05:11 

279 m1 = self.re_DateTime.finditer(info[key]) 

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

281 for a in m1: 

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

283 a.group("Y"), 

284 a.group("M"), 

285 a.group("D"), 

286 a.group("H"), 

287 a.group("MIN"), 

288 a.group("S"), 

289 ) 

290 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") 

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

292 hand.newFormat = datetime.datetime.strptime("20220908000000", "%Y%m%d%H%M%S") # this is a guess 

293 hand.newFormat = HandHistoryConverter.changeTimezone(hand.newFormat, "ET", "UTC") 

294 if key == "HID": 

295 hand.handid = info[key] 

296 if key == "TOURNO" and info["TOURNO"] is not None: 

297 hand.tourNo = info[key] 

298 hand.isKO = False 

299 if key == "BUYIN" and info["BUYIN"] is not None: 

300 if info[key] == "Free" or info["BIAMT"] is None: 

301 hand.buyin = 0 

302 hand.fee = 0 

303 hand.buyinCurrency = "FREE" 

304 else: 

305 if info["BUYIN"].find("$") != -1: 

306 hand.buyinCurrency = "USD" 

307 elif info["BUYIN"].find("€") != -1: 

308 hand.buyinCurrency = "EUR" 

309 elif "PLAY" in info and info["PLAY"] != "Practice Play" and info["PLAY"] != "Play Money": 

310 hand.buyinCurrency = "FREE" 

311 else: 

312 # FIXME: handle other currencies, FPP, play money 

313 log.error( 

314 ("PacificPokerToFpdb.readHandInfo: Failed to detect currency.") 

315 + " Hand ID: %s: '%s'" % (hand.handid, info[key]) 

316 ) 

317 raise FpdbParseError 

318 

319 info["BIAMT"] = self.clearMoneyString(info["BIAMT"].strip("$€")) 

320 hand.buyin = int(100 * Decimal(info["BIAMT"])) 

321 

322 if info["BIRAKE"] is None: 

323 hand.fee = 0 

324 else: 

325 info["BIRAKE"] = self.clearMoneyString(info["BIRAKE"].strip("$€")) 

326 hand.fee = int(100 * Decimal(info["BIRAKE"])) 

327 

328 if key == "TABLE" and info["TABLE"] is not None: 

329 hand.tablename = info[key] 

330 if key == "TABLEID" and info["TABLEID"] is not None: 

331 hand.tablename = info[key] 

332 if key == "BUTTON": 

333 hand.buttonpos = info[key] 

334 if key == "MAX" and info["MAX"] is not None: 

335 hand.maxseats = int(info[key]) 

336 

337 if key == "PLAY" and info["PLAY"] is not None: 

338 # hand.currency = 'play' # overrides previously set value 

339 hand.gametype["currency"] = "play" 

340 

341 if "TOURNO" in info and info["TOURNO"] is not None and hand.tablename == "": 

342 hand.tablename = 1 

343 

344 def readButton(self, hand): 

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

346 if m: 

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

348 else: 

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

350 

351 def readPlayerStacks(self, hand): 

352 m = self.re_PlayerInfo.finditer(hand.handText) 

353 for a in m: 

354 if len(a.group("PNAME")) == 0: 

355 log.error("PacificPokerToFpdb.readPlayerStacks: Player name empty %s" % hand.handid) 

356 raise FpdbParseError 

357 hand.addPlayer(int(a.group("SEAT")), a.group("PNAME"), self.clearMoneyString(a.group("CASH"))) 

358 

359 def markStreets(self, hand): 

360 # PREFLOP = ** Dealing down cards ** (observed hands don't have this line) 

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

362 if hand.gametype["base"] in ("hold"): 

363 m = re.search( 

364 r"(?P<PREFLOP>.+(?=\*\* Dealing flop \*\*)|.+)" 

365 r"(\*\* Dealing flop \*\* (?P<FLOP>\[ \S\S, \S\S, \S\S \].+(?=\*\* Dealing turn \*\*)|.+))?" 

366 r"(\*\* Dealing turn \*\* (?P<TURN>\[ \S\S \].+(?=\*\* Dealing river \*\*)|.+))?" 

367 r"(\*\* Dealing river \*\* (?P<RIVER>\[ \S\S \].+?(?=\*\* Summary \*\*)|.+))?", 

368 hand.handText, 

369 re.DOTALL, 

370 ) 

371 if m is None: 

372 log.error(("PacificPokerToFpdb.markStreets: Unable to recognise streets %s" % hand.handid)) 

373 raise FpdbParseError 

374 else: 

375 # print "DEBUG: Matched markStreets" 

376 m.groupdict() 

377 # if 'PREFLOP' in mg: 

378 # print "DEBUG: PREFLOP: ", [mg['PREFLOP']] 

379 # if 'FLOP' in mg: 

380 # print "DEBUG: FLOP: ", [mg['FLOP']] 

381 # if 'TURN' in mg: 

382 # print "DEBUG: TURN: ", [mg['TURN']] 

383 # if 'RIVER' in mg: 

384 # print "DEBUG: RIVER: ", [mg['RIVER']] 

385 

386 hand.addStreets(m) 

387 

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

389 if street in ( 

390 "FLOP", 

391 "TURN", 

392 "RIVER", 

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

394 # print "DEBUG readCommunityCards:", street, hand.streets.group(street) 

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

396 cards = self.splitCards(m.group("CARDS")) 

397 hand.setCommunityCards(street, cards) 

398 

399 def readAntes(self, hand): 

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

401 for player in m: 

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

403 hand.addAnte(player.group("PNAME"), self.clearMoneyString(player.group("ANTE"))) 

404 self.allInBlind(hand, "PREFLOP", player, "ante") 

405 

406 def readBringIn(self, hand): 

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

408 if m: 

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

410 hand.addBringIn(m.group("PNAME"), m.group("BRINGIN")) 

411 

412 def readBlinds(self, hand): 

413 if hand.startTime < hand.newFormat: 

414 hand.setUncalledBets(True) 

415 liveBlind, hand.allInBlind = True, False 

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

417 if a.group("PNAME") in hand.stacks: 

418 if liveBlind: 

419 hand.addBlind(a.group("PNAME"), "small blind", self.clearMoneyString(a.group("SB"))) 

420 liveBlind = False 

421 else: 

422 # Post dead blinds as ante 

423 hand.addBlind(a.group("PNAME"), "secondsb", self.clearMoneyString(a.group("SB"))) 

424 self.allInBlind(hand, "PREFLOP", a, "secondsb") 

425 else: 

426 log.error( 

427 "PacificPokerToFpdb.readBlinds (SB): '%s', '%s' not in hand.stacks" 

428 % (hand.handid, a.group("PNAME")) 

429 ) 

430 raise FpdbParseError 

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

432 if a.group("PNAME") in hand.stacks: 

433 hand.addBlind(a.group("PNAME"), "big blind", self.clearMoneyString(a.group("BB"))) 

434 self.allInBlind(hand, "PREFLOP", a, "big blind") 

435 else: 

436 log.error( 

437 "PacificPokerToFpdb.readBlinds (BB): '%s', '%s' not in hand.stacks" 

438 % (hand.handid, a.group("PNAME")) 

439 ) 

440 raise FpdbParseError 

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

442 if a.group("PNAME") in hand.stacks: 

443 if Decimal(self.clearMoneyString(a.group("BB"))) > 0: 

444 bb = self.clearMoneyString(a.group("BB")) 

445 sb = self.clearMoneyString(a.group("SB")) 

446 both = str(Decimal(bb) + Decimal(sb)) 

447 hand.addBlind(a.group("PNAME"), "both", both) 

448 else: 

449 hand.addBlind(a.group("PNAME"), "secondsb", self.clearMoneyString(a.group("SB"))) 

450 self.allInBlind(hand, "PREFLOP", a, "both") 

451 else: 

452 log.error( 

453 "PacificPokerToFpdb.readBlinds (Both): '%s', '%s' not in hand.stacks" 

454 % (hand.handid, a.group("PNAME")) 

455 ) 

456 raise FpdbParseError 

457 

458 def readHoleCards(self, hand): 

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

460 # we need to grab hero's cards 

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

462 if street in list(hand.streets.keys()): 

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

464 for found in m: 

465 # if m == None: 

466 # hand.involved = False 

467 # else: 

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

469 newcards = self.splitCards(found.group("NEWCARDS")) 

470 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True) 

471 

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

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

474 continue # already done these 

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

476 for found in m: 

477 player = found.group("PNAME") 

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

479 newcards = [] 

480 else: 

481 newcards = self.splitCards(found.group("NEWCARDS")) 

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

483 oldcards = [] 

484 else: 

485 oldcards = self.splitCards(found.group("OLDCARDS")) 

486 

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

488 hand.hero = player 

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

490 hand.addHoleCards( 

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

492 ) 

493 else: 

494 hand.addHoleCards( 

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

496 ) 

497 

498 def readAction(self, hand, street): 

499 m = self.re_Action.finditer(hand.streets[street]) 

500 for action in m: 

501 action.groupdict() 

502 if street not in ("PREFLOP", "DEAL"): 

503 hand.setUncalledBets(False) 

504 # print "DEBUG: acts: %s" %acts 

505 bet = self.clearMoneyString(action.group("BET")) if action.group("BET") else None 

506 if action.group("PNAME") in hand.stacks: 

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

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

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

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

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

512 hand.addCall(street, action.group("PNAME"), bet) 

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

514 hand.addCallandRaise(street, action.group("PNAME"), bet) 

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

516 hand.addBet(street, action.group("PNAME"), bet) 

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

518 hand.addDiscard(street, action.group("PNAME"), bet, action.group("DISCARDED")) 

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

520 hand.addStandsPat(street, action.group("PNAME")) 

521 else: 

522 log.debug(("DEBUG:") + " " + "Unimplemented %s: '%s' '%s'") % ( 

523 "readAction", 

524 action.group("PNAME"), 

525 action.group("ATYPE"), 

526 ) 

527 

528 if action.group("ATYPE") not in (" checks", " folds"): 

529 if not hand.allInBlind: 

530 if not (hand.stacks[action.group("PNAME")] == 0 and action.group("ATYPE") == " calls"): 

531 hand.setUncalledBets(False) 

532 if hand.stacks[action.group("PNAME")] == 0 and action.group("ATYPE") == " raises": 

533 hand.checkForUncalled = True 

534 else: 

535 log.error( 

536 "PacificPokerToFpdb.readAction: '%s', '%s' not in hand.stacks" 

537 % (hand.handid, action.group("PNAME")) 

538 ) 

539 raise FpdbParseError 

540 

541 def allInBlind(self, hand, street, action, actiontype): 

542 if street in ("PREFLOP", "DEAL"): 

543 if hand.stacks[action.group("PNAME")] == 0: 

544 if actiontype == "ante": 

545 if action.group("PNAME") in [p for (p, b) in hand.posted]: 

546 hand.setUncalledBets(False) 

547 hand.checkForUncalled = True 

548 hand.allInBlind = True 

549 elif actiontype in ("secondsb", "big blind", "both") and not self.re_Antes.search(hand.handText): 

550 hand.setUncalledBets(False) 

551 hand.checkForUncalled = True 

552 hand.allInBlind = True 

553 

554 def readShowdownActions(self, hand): 

555 # TODO: pick up mucks also?? 

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

557 cards = shows.group("CARDS").split(", ") 

558 hand.addShownCards(cards, shows.group("PNAME")) 

559 

560 def readCollectPot(self, hand): 

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

562 # print "DEBUG: hand.addCollectPot(player=", m.group('PNAME'), ", pot=", m.group('POT'), ")" 

563 hand.addCollectPot(player=m.group("PNAME"), pot=self.clearMoneyString(m.group("POT"))) 

564 

565 def readShownCards(self, hand): 

566 for m in self.re_ShownCards.finditer(hand.handText): 

567 if m.group("CARDS") is not None: 

568 cards = m.group("CARDS") 

569 cards = self.splitCards(cards) 

570 

571 (shown, mucked) = (False, False) 

572 if m.group("SHOWED") == "shows": 

573 shown = True 

574 elif m.group("SHOWED") == "mucks": 

575 mucked = True 

576 

577 # print "DEBUG: hand.addShownCards(%s, %s, %s, %s)" %(cards, m.group('PNAME'), shown, mucked) 

578 hand.addShownCards(cards=cards, player=m.group("PNAME"), shown=shown, mucked=mucked) 

579 

580 def splitCards(self, cards): 

581 # Polish 

582 cards = cards.replace("Kreuz", "c") 

583 cards = cards.replace("Karo", "d") 

584 cards = cards.replace("Pik", "s") 

585 cards = cards.replace("Herz", "h") 

586 cards = cards.replace("10", "T") 

587 # Russian 

588 cards = cards.replace("\xd2", "Q") 

589 cards = cards.replace("\xc2", "A") 

590 cards = cards.replace("\xc4", "J") 

591 # Spanish 

592 cards = self.re_Spanish_10.sub("T\g<1>", cards) 

593 cards = cards.replace("t", "h") 

594 cards = cards.replace("p", "s") 

595 cards = cards.replace("e", "d") 

596 cards = cards.replace("o", "h") 

597 # Dutch 

598 cards = cards.replace("B", "J") 

599 cards = cards.replace("V", "Q") 

600 cards = cards.replace("H", "K") 

601 # Swedish 

602 cards = cards.replace("Kn", "J") 

603 cards = cards.replace("D", "Q") 

604 cards = cards.replace("E", "A") 

605 cards = cards.split(", ") 

606 return cards 

607 

608 @staticmethod 

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

610 # Tournament tables look like: 

611 # Tour NLH 50+5 Brouhaha ID #28353026 Table #7 Blinds: 200/400 

612 log.info( 

613 "Pacific.getTableTitleRe: table_name='%s' tournament='%s' table_number='%s'" 

614 % (table_name, tournament, table_number) 

615 ) 

616 regex = "%s" % (table_name) 

617 if tournament: 

618 regex = "%s Table #%s" % (tournament, table_number) 

619 

620 log.info("Pacific.getTableTitleRe: returns: '%s'" % (regex)) 

621 return regex 

622 

623 def readSummaryInfo(self, summaryInfoList): 

624 self.status = True 

625 return self.status