Coverage for BetOnlineToFpdb.py: 0%

367 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, Chaz Littlejohn 

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 

21from __future__ import division 

22 

23from past.utils import old_div 

24#import L10n 

25#_ = L10n.get_translation() 

26 

27# TODO: straighten out discards for draw games 

28 

29import sys 

30from HandHistoryConverter import * 

31from decimal_wrapper import Decimal 

32 

33# BetOnline HH Format 

34 

35class BetOnline(HandHistoryConverter): 

36 

37 # Class Variables 

38 

39 sitename = "BetOnline" 

40 skin = "BetOnline" 

41 filetype = "text" 

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

43 siteId = 19 # Needs to match id entry in Sites database 

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

45 substitutions = { 

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

47 'PLYR': r'(?P<PNAME>.+?)', 

48 'NUM' :u".,\d", 

49 } 

50 

51 # translations from captured groups to fpdb info strings 

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

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

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

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

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

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

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

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

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

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

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

63 '40.00': ('10.00', '20.00'), '40': ('10.00', '20.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 '200.00': ('50.00', '100.00'), '200': ('50.00', '100.00'), 

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

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

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

71 } 

72 

73 limits = { 'No Limit':'nl', 'Pot Limit':'pl', 'Limit':'fl', 'LIMIT':'fl' } 

74 games = { # base, category 

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

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

77 'Omaha Hi/Lo' : ('hold','omahahilo'), 

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

79 '7 Card Stud' : ('stud','studhi'), 

80 '7 Card Stud Hi/Lo' : ('stud','studhilo'), 

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

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

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

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

85 } 

86 mixes = { 

87 'HORSE': 'horse', 

88 '8-Game': '8game', 

89 'HOSE': 'hose', 

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

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

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

93 'Triple Stud': '3stud' 

94 } # Legal mixed games 

95 currencies = { u'€':'EUR', '$':'USD', '':'T$' } 

96 

97 skins = { 

98 'BetOnline Poker': 'BetOnline', 

99 'PayNoRake': 'PayNoRake', 

100 'ActionPoker.com': 'ActionPoker', 

101 'Gear Poker': 'GearPoker', 

102 'SportsBetting.ag Poker': 'SportsBetting.ag', 

103 'Tiger Gaming': 'Tiger Gaming' 

104 } # Legal mixed games 

105 

106 # Static regexes 

107 re_GameInfo = re.compile(u""" 

108 (?P<SKIN>BetOnline\sPoker|PayNoRake|ActionPoker\.com|Gear\sPoker|SportsBetting\.ag\sPoker|Tiger\sGaming)\sGame\s\#(?P<HID>[0-9]+):\s+ 

109 (\{.*\}\s+)?(Tournament\s\# # open paren of tournament info 

110 (?P<TOURNO>\d+):\s? 

111 # here's how I plan to use LS 

112 (?P<BUYIN>(?P<BIAMT>(%(LS)s)[%(NUM)s]+)?\+?(?P<BIRAKE>(%(LS)s)[%(NUM)s]+)?\+?(?P<BOUNTY>(%(LS)s)[%(NUM)s]+)?\s?|Freeroll|)\s+)? 

113 # close paren of tournament info 

114 (?P<GAME>Hold\'em|Razz|7\sCard\sStud|7\sCard\sStud\sHi/Lo|Omaha|Omaha\sHi/Lo|Badugi|Triple\sDraw\s2\-7\sLowball|Single\sDraw\s2\-7\sLowball|5\sCard\sDraw)\s 

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

116 ( 

117 \(? # open paren of the stakes 

118 (?P<CURRENCY>%(LS)s|)? 

119 (?P<SB>[%(NUM)s]+)/(%(LS)s)? 

120 (?P<BB>[%(NUM)s]+) 

121 \)? # close paren of the stakes 

122 )? 

123 \s?-\s 

124 (?P<DATETIME>.*$) 

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

126 

127 re_PlayerInfo = re.compile(u""" 

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

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

130 \((%(LS)s)?(?P<CASH>[%(NUM)s]+)\sin\s[cC]hips\)""" % substitutions, 

131 re.MULTILINE|re.VERBOSE) 

132 

133 re_HandInfo1 = re.compile(""" 

134 ^Table\s\'(?P<TABLE>[\/,\.\-\ &%\$\#a-zA-Z\d\'\(\)]+)\'\s 

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

136 (?P<MONEY>\((Play\sMoney|Real\sMoney)\)\s)? 

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

138 re.MULTILINE|re.VERBOSE) 

139 

140 re_HandInfo2 = re.compile(""" 

141 ^Table\s(?P<TABLE>[\/,\.\-\ &%\$\#a-zA-Z\d\']+)\s 

142 (?P<MONEY>\((Play\sMoney|Real\sMoney)\)\s)? 

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

144 re.MULTILINE|re.VERBOSE) 

145 

146 re_Identify = re.compile(u'(BetOnline\sPoker|PayNoRake|ActionPoker\.com|Gear\sPoker|SportsBetting\.ag\sPoker|Tiger\sGaming)\sGame\s\#\d+') 

147 re_SplitHands = re.compile('\n\n\n+') 

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

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

150 re_Board1 = re.compile(r"Board \[(?P<FLOP>\S\S\S? \S\S\S? \S\S\S?)?\s?(?P<TURN>\S\S\S?)?\s?(?P<RIVER>\S\S\S?)?\]") 

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

152 re_Hole = re.compile(r"\*\*\*\sHOLE\sCARDS\s\*\*\*") 

153 

154 

155 re_DateTime1 = 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]+))?\s(?P<TZ>.*$)""", re.MULTILINE) 

156 re_DateTime2 = 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) 

157 

158 re_PostSB = re.compile(r"^%(PLYR)s: [Pp]osts small blind (%(LS)s)?(?P<SB>[%(NUM)s]+)" % substitutions, re.MULTILINE) 

159 re_PostBB = re.compile(r"^%(PLYR)s: ([Pp]osts big blind|[Pp]osts? [Nn]ow)( (%(LS)s)?(?P<BB>[%(NUM)s]+))?" % substitutions, re.MULTILINE) 

160 re_Antes = re.compile(r"^%(PLYR)s: ante processed (%(LS)s)?(?P<ANTE>[%(NUM)s]+)" % substitutions, re.MULTILINE) 

161 re_BringIn = re.compile(r"^%(PLYR)s: brings[- ]in( low|) for (%(LS)s)?(?P<BRINGIN>[%(NUM)s]+)" % substitutions, re.MULTILINE) 

162 re_PostBoth = re.compile(r"^%(PLYR)s: [Pp]ost dead (%(LS)s)?(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE) 

163 re_HeroCards = re.compile(r"^Dealt [Tt]o %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % substitutions, re.MULTILINE) 

164 re_Action = re.compile(r""" 

165 ^%(PLYR)s:?(?P<ATYPE>\shas\sleft\sthe\stable|\s[Bb]ets|\s[Cc]hecks|\s[Rr]aises|\s[Cc]alls|\s[Ff]olds|\s[Dd]iscards|\s[Ss]tands\spat|\sReraises) 

166 (\s(%(LS)s)?(?P<BET>[%(NUM)s]+))?(\sto\s(%(LS)s)?(?P<BETTO>[%(NUM)s]+))? # the number discarded goes in <BET> 

167 \s*(and\sis\s[Aa]ll.[Ii]n)? 

168 (\son|\scards?)? 

169 (\s\[(?P<CARDS>.+?)\])?\.?\s*$""" 

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

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

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

173 re_JoinsTable = re.compile("^.+ joins the table at seat #\d+", re.MULTILINE) 

174 re_TotalPot = re.compile(r"^Total pot (?P<POT>[%(NUM)s]+)( \| Rake (?P<RAKE>[%(NUM)s]+))?", re.MULTILINE) 

175 re_ShownCards = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s (\(.+?\) )?(?P<SHOWED>showed|mucked) \[(?P<CARDS>.*)\]( and won \([%(NUM)s]+\))?" % substitutions, re.MULTILINE) 

176 re_CollectPot = re.compile(r"Seat (?P<SEAT>[0-9]+): %(PLYR)s (\(.+?\) )?(collected|showed \[.*\] and won) \((%(LS)s)?(?P<POT>[%(NUM)s]+)\)" % substitutions, re.MULTILINE) 

177 

178 def compilePlayerRegexs(self, hand): 

179 pass 

180 

181 def readSupportedGames(self): 

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

183 ["ring", "hold", "pl"], 

184 ["ring", "hold", "fl"], 

185 

186 #["ring", "stud", "fl"], 

187 

188 #["ring", "draw", "fl"], 

189 #["ring", "draw", "pl"], 

190 #["ring", "draw", "nl"], 

191 

192 ["tour", "hold", "nl"], 

193 ["tour", "hold", "pl"], 

194 ["tour", "hold", "fl"], 

195 

196 #["tour", "stud", "fl"], 

197 

198 #["tour", "draw", "fl"], 

199 #["tour", "draw", "pl"], 

200 #["tour", "draw", "nl"], 

201 ] 

202 

203 def determineGameType(self, handText): 

204 info = {} 

205 m = self.re_GameInfo.search(handText) 

206 if not m: 

207 # BetOnline starts writing the hh the moment you sit down. 

208 # Test if the hh contains the join line, and throw a partial if so. 

209 m2 = self.re_JoinsTable.search(handText) 

210 if not m2: 

211 tmp = handText[0:200] 

212 log.error(("BetOnlineToFpdb.determineGameType: '%s'") % tmp) 

213 raise FpdbParseError 

214 else: 

215 raise FpdbHandPartial("BetOnlineToFpdb.determineGameType: " + ("Partial hand history: 'Player joining table'")) 

216 

217 mg = m.groupdict() 

218 if mg['LIMIT']: 

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

220 if info['limitType']=='pl': 

221 m = self.re_HeroCards.search(handText) 

222 if m and len(m.group('NEWCARDS').split(' '))==4: 

223 (info['base'], info['category']) = self.games['Omaha'] 

224 else: 

225 info['limitType'] = self.limits['No Limit'] 

226 if 'SKIN' in mg: 

227 self.skin = self.skins[mg['SKIN']] 

228 if 'GAME' in mg and not info.get('base'): 

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

230 if 'SB' in mg: 

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

232 if 'BB' in mg: 

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

234 if 'CURRENCY' in mg and mg['CURRENCY'] is not None: 

235 info['currency'] = self.currencies[mg['CURRENCY']] 

236 else: 

237 info['currency'] = 'USD' 

238 if 'MIXED' in mg: 

239 if mg['MIXED'] is not None: info['mix'] = self.mixes[mg['MIXED']] 

240 

241 if 'TOURNO' in mg and mg['TOURNO'] is None: 

242 info['type'] = 'ring' 

243 else: 

244 info['type'] = 'tour' 

245 

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

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

248 try: 

249 info['sb'] = self.Lim_Blinds[info['BB']][0] 

250 info['bb'] = self.Lim_Blinds[info['BB']][1] 

251 except KeyError: 

252 tmp = handText[0:200] 

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

254 raise FpdbParseError 

255 else: 

256 info['sb'] = str((old_div(Decimal(info['SB']),2)).quantize(Decimal("0.01"))) 

257 info['bb'] = str(Decimal(info['SB']).quantize(Decimal("0.01"))) 

258 

259 return info 

260 

261 def readHandInfo(self, hand): 

262 info = {} 

263 if self.skin in ('ActionPoker', 'GearPoker'): 

264 m = self.re_HandInfo2.search(hand.handText,re.DOTALL) 

265 else: 

266 m = self.re_HandInfo1.search(hand.handText,re.DOTALL) 

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

268 if m is None or m2 is None: 

269 tmp = hand.handText[0:200] 

270 log.error(("BetOnlineToFpdb.readHandInfo: '%s'") % tmp) 

271 raise FpdbParseError 

272 

273 info.update(m.groupdict()) 

274 info.update(m2.groupdict()) 

275 

276 #print 'DEBUG:', info 

277 for key in info: 

278 if key == 'DATETIME': 

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

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

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

282 

283 datetimestr, time_zone = "2000/01/01 00:00:00", 'ET' # default used if time not found 

284 if self.skin not in ('ActionPoker', 'GearPoker'): 

285 m1 = self.re_DateTime1.finditer(info[key]) 

286 for a in m1: 

287 seconds = '00' 

288 if a.group('S'): 

289 seconds = a.group('S') 

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

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

292 if tz == 'GMT Standard Time': 

293 time_zone = 'GMT' 

294 elif tz in ('Pacific Daylight Time', 'Pacific Standard Time'): 

295 time_zone = 'PT' 

296 else: 

297 time_zone = 'ET' 

298 else: 

299 m2 = self.re_DateTime2.finditer(info[key]) 

300 for a in m2: 

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

302 time_zone = 'ET' 

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

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

305 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, time_zone, "UTC") 

306 if key == 'HID': 

307 hand.handid = info[key] 

308 if key == 'MONEY': 

309 if info[key]=='(Play Money) ': 

310 hand.gametype['currency'] = 'play' 

311 if key == 'TOURNO': 

312 hand.tourNo = info[key] 

313 if key == 'BUYIN': 

314 if hand.tourNo!=None: 

315 #print "DEBUG: info['BUYIN']: %s" % info['BUYIN'] 

316 #print "DEBUG: info['BIAMT']: %s" % info['BIAMT'] 

317 #print "DEBUG: info['BIRAKE']: %s" % info['BIRAKE'] 

318 #print "DEBUG: info['BOUNTY']: %s" % info['BOUNTY'] 

319 if not info[key] or info[key] == 'Freeroll': 

320 hand.buyin = 0 

321 hand.fee = 0 

322 hand.buyinCurrency = "FREE" 

323 else: 

324 if info[key].find("$")!=-1: 

325 hand.buyinCurrency="USD" 

326 elif info[key].find(u"€")!=-1: 

327 hand.buyinCurrency="EUR" 

328 elif re.match("^[0-9+]*$", info[key]): 

329 hand.buyinCurrency="play" 

330 else: 

331 #FIXME: handle other currencies, play money 

332 raise FpdbParseError(("BetOnlineToFpdb.readHandInfo: Failed to detect currency.") + " " + ("Hand ID") + ": %s: '%s'" % (hand.handid, info[key])) 

333 

334 info['BIAMT'] = info['BIAMT'].strip(u'$€') 

335 if info['BOUNTY'] != None: 

336 # There is a bounty, Which means we need to switch BOUNTY and BIRAKE values 

337 tmp = info['BOUNTY'] 

338 info['BOUNTY'] = info['BIRAKE'] 

339 info['BIRAKE'] = tmp 

340 info['BOUNTY'] = info['BOUNTY'].strip(u'$€') # Strip here where it isn't 'None' 

341 hand.koBounty = int(100*Decimal(info['BOUNTY'])) 

342 hand.isKO = True 

343 else: 

344 hand.isKO = False 

345 

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

347 

348 hand.buyin = int(100*Decimal(info['BIAMT'])) 

349 hand.fee = int(100*Decimal(info['BIRAKE'])) 

350 if key == 'LEVEL': 

351 hand.level = info[key] 

352 

353 if key == 'TABLE': 

354 if hand.tourNo != None: 

355 hand.tablename = re.split("-", info[key])[1] 

356 else: 

357 hand.tablename = info[key] 

358 if key == 'BUTTON': 

359 hand.buttonpos = info[key] 

360 if key == 'MAX' and info[key] != None: 

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

362 if not self.re_Board1.search(hand.handText) and self.skin not in ('ActionPoker', 'GearPoker'): 

363 raise FpdbHandPartial("readHandInfo: " + ("Partial hand history") + ": '%s'" % hand.handid) 

364 

365 def readButton(self, hand): 

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

367 if m: 

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

369 else: 

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

371 

372 def readPlayerStacks(self, hand): 

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

374 for a in m: 

375 pname = self.unknownPlayer(hand, a.group('PNAME')) 

376 hand.addPlayer(int(a.group('SEAT')), pname, self.clearMoneyString(a.group('CASH'))) 

377 

378 def markStreets(self, hand): 

379 

380 # There is no marker between deal and draw in Stars single draw games 

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

382 # in consequence the mucked-display is incorrect. 

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

384 

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

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

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

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

389 # handText was not split, no DRAW street occurred 

390 pass 

391 else: 

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

393 discard_split[0] += "*** DRAW ***\r\n" 

394 hand.handText = "" 

395 for i in discard_split: 

396 hand.handText += i 

397 

398 # PREFLOP = ** Dealing down cards ** 

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

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

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

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

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

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

405 m2 = self.re_Board1.search(hand.handText) 

406 if m and m2: 

407 if m2.group('FLOP') and not m.group('FLOP'): 

408 m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=Board )|.+)" 

409 r"(Board \[(?P<FLOP>\S\S\S? \S\S\S? \S\S\S?)?\s?(?P<TURN>\S\S\S?)?\s?(?P<RIVER>\S\S\S?)?\])?", hand.handText,re.DOTALL) 

410 elif m2.group('TURN') and not m.group('TURN'): 

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

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

413 r"(Board \[(?P<BFLOP>\S\S\S? \S\S\S? \S\S\S?)?\s?(?P<TURN>\S\S\S?)?\s?(?P<RIVER>\S\S\S?)?\])?", hand.handText,re.DOTALL) 

414 elif m2.group('RIVER') and not m.group('RIVER'): 

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

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

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

418 r"(Board \[(?P<BFLOP>\S\S\S? \S\S\S? \S\S\S?)?\s?(?P<BTURN>\S\S\S?)?\s?(?P<RIVER>\S\S\S?)?\])?", hand.handText,re.DOTALL) 

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

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

421 r"(\*\*\* 3rd STREET \*\*\*(?P<THIRD>.+(?=\*\*\* 4th STREET \*\*\*)|.+))?" 

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

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

424 r"(\*\*\* 6th STREET \*\*\*(?P<SIXTH>.+(?=\*\*\* RIVER \*\*\*)|.+))?" 

425 r"(\*\*\* RIVER \*\*\*(?P<SEVENTH>.+))?", hand.handText,re.DOTALL) 

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

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

428 m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)" 

429 r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* DRAW \*\*\*)|.+))?" 

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

431 else: 

432 m = re.search(r"(?P<PREDEAL>.+(?=\*\*\* DEALING HANDS \*\*\*)|.+)" 

433 r"(\*\*\* DEALING HANDS \*\*\*(?P<DEAL>.+(?=\*\*\* FIRST DRAW \*\*\*)|.+))?" 

434 r"(\*\*\* FIRST DRAW \*\*\*(?P<DRAWONE>.+(?=\*\*\* SECOND DRAW \*\*\*)|.+))?" 

435 r"(\*\*\* SECOND DRAW \*\*\*(?P<DRAWTWO>.+(?=\*\*\* THIRD DRAW \*\*\*)|.+))?" 

436 r"(\*\*\* THIRD DRAW \*\*\*(?P<DRAWTHREE>.+))?", hand.handText,re.DOTALL) 

437 hand.addStreets(m) 

438 #if m3 and m2: 

439 # if m2.group('RIVER') and not m3.group('RIVER'): 

440 # print hand.streets 

441 

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

443 if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP) 

444 if self.skin not in ('ActionPoker', 'GearPoker'): 

445 m = self.re_Board1.search(hand.handText) 

446 if m and m.group(street): 

447 cards = m.group(street).split(' ') 

448 cards = [c.replace("10", "T") for c in cards] 

449 hand.setCommunityCards(street, cards) 

450 else: 

451 m = self.re_Board2.search(hand.streets[street]) 

452 cards = m.group('CARDS').split(' ') 

453 cards = [c[:-1].replace('10', 'T') + c[-1].lower() for c in cards] 

454 hand.setCommunityCards(street, cards) 

455 

456 def readAntes(self, hand): 

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

458 for player in m: 

459 if player.group('ANTE')!='0.00': 

460 pname = self.unknownPlayer(hand, player.group('PNAME')) 

461 hand.addAnte(pname, self.clearMoneyString(player.group('ANTE'))) 

462 

463 def readBringIn(self, hand): 

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

465 if m: 

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

467 hand.addBringIn(m.group('PNAME'), self.clearMoneyString(m.group('BRINGIN'))) 

468 

469 def readBlinds(self, hand): 

470 liveBlind = True 

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

472 pname = self.unknownPlayer(hand, a.group('PNAME')) 

473 sb = self.clearMoneyString(a.group('SB')) 

474 if liveBlind: 

475 hand.addBlind(pname, 'small blind', sb) 

476 liveBlind = False 

477 else: 

478 # Post dead blinds as ante 

479 hand.addBlind(pname, 'secondsb', sb) 

480 if not hand.gametype['sb'] and self.skin in ('ActionPoker', 'GearPoker'): 

481 hand.gametype['sb'] = sb 

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

483 pname = self.unknownPlayer(hand, a.group('PNAME')) 

484 if a.group('BB') is not None: 

485 bb = self.clearMoneyString(a.group('BB')) 

486 elif hand.gametype['bb']: 

487 bb = hand.gametype['bb'] 

488 else: 

489 raise FpdbHandPartial("BetOnlineToFpdb.readBlinds: " + ("Partial hand history: 'No blind info'")) 

490 hand.addBlind(pname, 'big blind', bb) 

491 if not hand.gametype['bb'] and self.skin in ('ActionPoker', 'GearPoker'): 

492 hand.gametype['bb'] = bb 

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

494 if a.group('SBBB')!='0.00': 

495 pname = self.unknownPlayer(hand, a.group('PNAME')) 

496 sbbb = self.clearMoneyString(a.group('SBBB')) 

497 amount = str(Decimal(sbbb) + old_div(Decimal(sbbb),2)) 

498 hand.addBlind(pname, 'both', amount) 

499 else: 

500 pname = self.unknownPlayer(hand, a.group('PNAME')) 

501 hand.addBlind(pname, 'secondsb', hand.gametype['sb']) 

502 self.fixBlinds(hand) 

503 

504 def fixBlinds(self, hand): 

505 # FIXME 

506 # The following should only trigger when a small blind is missing in ActionPoker hands, or the sb/bb is ALL_IN 

507 if self.skin in ('ActionPoker', 'GearPoker'): 

508 if hand.gametype['sb'] == None and hand.gametype['bb'] != None: 

509 BB = str(Decimal(hand.gametype['bb']) * 2) 

510 if self.Lim_Blinds.get(BB) != None: 

511 hand.gametype['sb'] = self.Lim_Blinds.get(BB)[0] 

512 elif hand.gametype['bb'] == None and hand.gametype['sb'] != None: 

513 for k, v in list(self.Lim_Blinds.items()): 

514 if hand.gametype['sb'] == v[0]: 

515 hand.gametype['bb'] = v[1] 

516 if hand.gametype['sb'] == None or hand.gametype['bb'] == None: 

517 log.error(("BetOnline.fixBlinds: Failed to fix blinds") + " Hand ID: %s" % (hand.handid, )) 

518 raise FpdbParseError 

519 hand.sb = hand.gametype['sb'] 

520 hand.bb = hand.gametype['bb'] 

521 

522 def unknownPlayer(self, hand, pname): 

523 if pname == 'Unknown player' or not pname: 

524 if not pname: pname = 'Dead' 

525 if pname not in (p[1] for p in hand.players): 

526 hand.addPlayer(-1, pname, '0') 

527 return pname 

528 

529 def readHoleCards(self, hand): 

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

531# we need to grab hero's cards 

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

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

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

535 for found in m: 

536# if m == None: 

537# hand.involved = False 

538# else: 

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

540 newcards = found.group('NEWCARDS').split(' ') 

541 newcards = [c[:-1].replace('10', 'T') + c[-1].lower() for c in newcards] 

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

543 

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

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

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

547 for found in m: 

548 player = found.group('PNAME') 

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

550 newcards = [] 

551 else: 

552 newcards = found.group('NEWCARDS').split(' ') 

553 newcards = [c[:-1].replace('10', 'T') + c[-1].lower() for c in newcards] 

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

555 oldcards = [] 

556 else: 

557 oldcards = found.group('OLDCARDS').split(' ') 

558 oldcards = [c[:-1].replace('10', 'T') + c[-1].lower() for c in oldcards] 

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

560 hand.hero = player 

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

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

563 else: 

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

565 

566 

567 def readAction(self, hand, street): 

568 if street=='PREFLOP': 

569 m0 = self.re_Action.finditer(self.re_Hole.split(hand.handText)[0]) 

570 for action in m0: 

571 pname = self.unknownPlayer(hand, action.group('PNAME')) 

572 if action.group('ATYPE') == ' has left the table': 

573 if pname in (p[1] for p in hand.players): 

574 hand.addFold(street, pname) 

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

576 for action in m: 

577 acts = action.groupdict() 

578 #print "DEBUG: street: %s acts: %s" % (street, acts) 

579 pname = self.unknownPlayer(hand, action.group('PNAME')) 

580 if action.group('ATYPE') in (' folds', ' Folds', ' has left the table'): 

581 if pname in (p[1] for p in hand.players): 

582 hand.addFold(street, pname) 

583 elif action.group('ATYPE') in (' checks', ' Checks'): 

584 hand.addCheck( street, pname) 

585 elif action.group('ATYPE') in (' calls', ' Calls'): 

586 hand.addCall( street, pname, self.clearMoneyString(action.group('BET')) ) 

587 elif action.group('ATYPE') in (' raises', ' Raises', ' Reraises'): 

588 hand.addCallandRaise( street, pname, self.clearMoneyString(action.group('BET')) ) 

589 elif action.group('ATYPE') in (' bets', ' Bets'): 

590 hand.addBet( street, pname, self.clearMoneyString(action.group('BET')) ) 

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

592 hand.addDiscard(street, pname, action.group('BET'), action.group('CARDS')) 

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

594 hand.addStandsPat( street, pname, action.group('CARDS')) 

595 else: 

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

597 

598 

599 def readShowdownActions(self, hand): 

600# TODO: pick up mucks also?? 

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

602 cards = shows.group('CARDS').split(' ') 

603 cards = [c[:-1].replace('10', 'T') + c[-1].lower() for c in cards] 

604 hand.addShownCards(cards, shows.group('PNAME')) 

605 

606 def readCollectPot(self,hand): 

607 hand.adjustCollected = True 

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

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

610 for m in self.re_TotalPot.finditer(hand.handText): 

611 if hand.rakes.get('pot'): 

612 hand.rakes['pot'] += Decimal(self.clearMoneyString(m.group('POT'))) 

613 else: 

614 hand.rakes['pot'] = Decimal(self.clearMoneyString(m.group('POT'))) 

615 

616 def readShownCards(self,hand): 

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

618 if m.group('CARDS') is not None: 

619 pname = self.unknownPlayer(hand, m.group('PNAME')) 

620 cards = m.group('CARDS') 

621 cards = cards.split(' ') # needs to be a list, not a set--stud needs the order 

622 cards = [c[:-1].replace('10', 'T') + c[-1].lower() for c in cards if len(c)>0] 

623 (shown, mucked) = (False, False) 

624 if m.group('SHOWED') == "showed": shown = True 

625 elif m.group('SHOWED') == "mucked": mucked = True 

626 if hand.gametype['category']=='holdem' and len(cards)>2: continue 

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

628 hand.addShownCards(cards=cards, player=pname, shown=shown, mucked=mucked, string=None) 

629 

630 @staticmethod 

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

632 """Returns string to search in windows titles""" 

633 if type == 'tour': 

634 return r'\(' + re.escape(str(tournament)) + r'\-' + re.escape(str(table_number)) + r'\)' 

635 else: 

636 return re.escape(table_name)