Coverage for PokerTrackerToFpdb.py: 0%
428 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
« 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-2012, 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########################################################################
21from __future__ import division
23from past.utils import old_div
24#import L10n
25#_ = L10n.get_translation()
27# TODO: straighten out discards for draw games
29import sys
30from HandHistoryConverter import *
31import MergeStructures
32from decimal_wrapper import Decimal
34# PokerTracker HH Format
36class PokerTracker(HandHistoryConverter):
38 # Class Variables
39 Structures = None
40 filetype = "text"
41 codepage = ("utf8", "cp1252")
42 sym = {'USD': "\$", 'CAD': "\$", 'T$': "", "EUR": "€", "GBP": "\£", "play": ""} # ADD Euro, Sterling, etc HERE
43 substitutions = {
44 'LEGAL_ISO' : "USD|EUR|GBP|CAD|FPP", # legal ISO currency codes
45 'LS' : u"\$|€|\£|", # legal currency symbols - Euro(cp1252, utf-8)
46 'PLYR': r'(?P<PNAME>.+?)',
47 'NUM' : u".,\d",
48 'CUR': u"(\$|€||\£|)",
49 'BRKTS': r'(\(button\) |\(small blind\) |\(big blind\) |\(button\) \(small blind\) |\(button\) \(big blind\) )?',
50 }
52 # translations from captured groups to fpdb info strings
53 Lim_Blinds = { '0.04': ('0.01', '0.02'), '0.08': ('0.02', '0.04'),
54 '0.10': ('0.02', '0.05'), '0.20': ('0.05', '0.10'),
55 '0.40': ('0.10', '0.20'), '0.50': ('0.10', '0.25'),
56 '1.00': ('0.25', '0.50'), '1': ('0.25', '0.50'),
57 '2.00': ('0.50', '1.00'), '2': ('0.50', '1.00'),
58 '4.00': ('1.00', '2.00'), '4': ('1.00', '2.00'),
59 '6.00': ('1.00', '3.00'), '6': ('1.00', '3.00'),
60 '8.00': ('2.00', '4.00'), '8': ('2.00', '4.00'),
61 '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'),
62 '20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'),
63 '30.00': ('10.00', '15.00'), '30': ('10.00', '15.00'),
64 '40.00': ('10.00', '20.00'), '40': ('10.00', '20.00'),
65 '60.00': ('15.00', '30.00'), '60': ('15.00', '30.00'),
66 '80.00': ('20.00', '40.00'), '80': ('20.00', '40.00'),
67 '100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'),
68 '150.00': ('50.00', '75.00'), '150': ('50.00', '75.00'),
69 '200.00': ('50.00', '100.00'), '200': ('50.00', '100.00'),
70 '400.00': ('100.00', '200.00'), '400': ('100.00', '200.00'),
71 '800.00': ('200.00', '400.00'), '800': ('200.00', '400.00'),
72 '1000.00': ('250.00', '500.00'), '1000': ('250.00', '500.00'),
73 '2000.00': ('500.00', '1000.00'), '2000': ('500.00', '1000.00'),
74 }
76 limits = { 'NL':'nl', 'No Limit':'nl', 'Pot Limit':'pl', 'PL': 'pl', 'FL': 'fl', 'Limit':'fl', 'LIMIT':'fl' }
77 games = { # base, category
78 "Hold'em" : ('hold','holdem'),
79 "Texas Hold'em" : ('hold','holdem'),
80 "Holdem" : ('hold','holdem'),
81 'Omaha' : ('hold','omahahi'),
82 'Omaha Hi' : ('hold','omahahi'),
83 'Omaha Hi/Lo' : ('hold','omahahilo')
84 }
85 sites = { 'EverestPoker Game #' : ('Everest', 16),
86 'GAME #' : ('iPoker', 14),
87 'MERGE_GAME #' : ('Merge', 12),
88 'Merge Game #' : ('Merge', 12),
89 '** Game ID ' : ('Microgaming', 20),
90 '** Hand # ' : ('Microgaming', 20)
92 }
93 currencies = { u'€':'EUR', '$':'USD', '':'T$', u'£':'GBP' }
96 re_Site = re.compile(u'(?P<SITE>EverestPoker\sGame\s\#|GAME\s\#|MERGE_GAME\s\#|Merge\sGame\s\#|\*{2}\s(Game\sID|Hand\s\#)\s)\d+')
97 # Static regexes
98 re_GameInfo1 = re.compile(u"""
99 (?P<SITE>GAME\s\#|MERGE_GAME\s\#|Merge\sGame\s\#)(?P<HID>[0-9\-]+)(\sVersion:[\d\.]+\s(?P<UNCALLED>Uncalled:Y))?(:?\s+|\s\|\s)
100 (?P<GAME>Holdem|Texas\sHold\'em|Omaha|Omaha\sHi|Omaha\sHi/Lo)\s\s?
101 ((?P<LIMIT>PL|NL|FL|No\sLimit|Limit|LIMIT|Pot\sLimit)\s\s?)?
102 (?P<TOUR>Tournament)?
103 (\(? # open paren of the stakes
104 (?P<CURRENCY>%(LS)s|)?
105 (?P<SB>[%(NUM)s]+)/(%(LS)s)?
106 (?P<BB>[%(NUM)s]+)
107 (?P<BLAH>\s-\s[%(LS)s\d\.]+\sCap\s-\s)? # Optional Cap part
108 \s?(?P<ISO>%(LEGAL_ISO)s)?
109 \)?
110 )?(\s|\s\|\s) # close paren of the stakes
111 (?P<DATETIME>.*$)
112 """ % substitutions, re.MULTILINE|re.VERBOSE)
114 re_GameInfo2 = re.compile(u"""
115 EverestPoker\sGame\s\#(?P<HID>[0-9]+):\s+
116 (?P<TOUR>Tourney\sID:\s(?P<TOURNO>\d+),\s)?
117 Table\s(?P<TABLE>.+)?
118 \s-\s
119 ((?P<CURRENCY>%(LS)s|)?
120 (?P<SB>[%(NUM)s]+)/(%(LS)s)?
121 (?P<BB>[%(NUM)s]+))?
122 \s-\s
123 (?P<LIMIT>No\sLimit|Limit|Pot\sLimit)\s
124 (?P<GAME>Hold\'em|Omaha|Omaha\sHi|Omaha\sHi/Lo)\s
125 (-\s)?
126 (?P<DATETIME>.*$)
127 """ % substitutions, re.MULTILINE|re.VERBOSE)
129 re_GameInfo3 = re.compile(u"""
130 (?P<HID>[0-9]+)(\sVersion:\d)?\sstarting\s\-\s(?P<DATETIME>.*$)\s
131 \*\*(?P<TOUR>.+(?P<SPEED>(Turbo|Hyper))?\((?P<TOURNO>\d+)\):Table)?\s(?P<TABLE>.+)\s
132 \[((Multi|Single)\sTable\s)?(?P<GAME>Hold\'em|Omaha|Omaha\sHi|Omaha\sHi/Lo)\]\s
133 \((?P<SB>[%(NUM)s]+)\|(?P<BB>[%(NUM)s]+)\s(?P<LIMIT>NL|FL|PL)\s\-\s(MTT|SNG|STT|(?P<CURRENCY>%(LS)s|)\s?Cash\sGame)(\sseats:(?P<MAX>\d+))?.*\)\s
134 (?P<PLAY>Real|Play)\sMoney
135 """ % substitutions, re.MULTILINE|re.VERBOSE)
137 re_PlayerInfo1 = re.compile(u"""
138 ^Seat\s(?P<SEAT>[0-9]+):\s
139 (?P<PNAME>.*)\s
140 \((?P<CURRENCY>%(LS)s)?(?P<CASH>[%(NUM)s]+)(\sin\schips)?\)
141 (?P<BUTTON>\sDEALER)?""" % substitutions,
142 re.MULTILINE|re.VERBOSE)
144 re_PlayerInfo2 = re.compile(u"""
145 ^(\-\s)?(?P<PNAME>.*)\s
146 sitting\sin\sseat\s(?P<SEAT>[0-9]+)\swith\s
147 (%(LS)s)?(?P<CASH>[%(NUM)s]+)
148 (?P<BUTTON>\s?\[Dealer\])?""" % substitutions,
149 re.MULTILINE|re.VERBOSE)
151 re_HandInfo_Tour = re.compile("""
152 ^Table\s(?P<TABLE>.*),\s(?P<TOURNO>\d+)(,\s\d+)?\s
153 (?P<TOUR>\(Tournament:\s(.+)?\sBuy\-In:\s(?P<BUYIN>(?P<BIAMT>[%(LS)s\d\.]+)\s?\+?\s?(?P<BIRAKE>[%(LS)s\d\.]+))\))
154 """ % substitutions
155 , re.MULTILINE|re.VERBOSE)
157 re_HandInfo_Cash = re.compile("""
158 ^Table\s(?P<TABLE>[^,]+?)(,\sSeats\s(?P<MAX>\d+))?$""" % substitutions
159 , re.MULTILINE|re.VERBOSE)
161 re_Identify = re.compile(u'(EverestPoker\sGame\s\#|GAME\s\#|MERGE_GAME\s\#|Merge\sGame\s\#|\*{2}\s(Game\sID|Hand\s\#)\s)\d+')
162 re_SplitHands = re.compile('\n\n\n+?')
163 re_TailSplitHands = re.compile('(\n\n\n+)')
164 re_Button = re.compile('The button is in seat #(?P<BUTTON>\d+)', re.MULTILINE)
165 re_Board1 = re.compile(r"\[(?P<CARDS>.+)\]")
166 re_Board2 = re.compile(r":\s(?P<CARDS>.+)\n")
167 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]+)""", re.MULTILINE)
168 re_DateTime2 = re.compile("""(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})\/(?P<Y>[0-9]{4})[\- ]+(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)""", re.MULTILINE)
169 re_DateTime3 = re.compile("""(?P<H>[0-9]+):(?P<MIN>[0-9]+):(?P<S>[0-9]+)[\- ]+(?P<Y>[0-9]{4})\/(?P<M>[0-9]{2})\/(?P<D>[0-9]{2})""", re.MULTILINE)
170 # revised re including timezone (not currently used):
171 #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)
173 # These used to be compiled per player, but regression tests say
174 # we don't have to, and it makes life faster.
175 re_PostSB = re.compile(r"^%(PLYR)s:? ((posts|posted) the small blind( of)?|(Post )?SB) (\- )?%(CUR)s(?P<SB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
176 re_PostBB = re.compile(r"^%(PLYR)s:? ((posts|posted) the big blind( of)?|posts the dead blind of|(Post )?BB) (\- )?%(CUR)s(?P<BB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
177 re_Antes = re.compile(r"^%(PLYR)s:? ((posts|posted) (the )?ante( of)?|(Post )?Ante) (\- )?%(CUR)s(?P<ANTE>[%(NUM)s]+)" % substitutions, re.MULTILINE)
178 re_PostBoth1 = re.compile(r"^%(PLYR)s:? (posts|Post|(Post )?DB) %(CUR)s(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
179 re_PostBoth2 = re.compile(r"^%(PLYR)s:? posted to play \- %(CUR)s(?P<SBBB>[%(NUM)s]+)" % substitutions, re.MULTILINE)
180 re_HeroCards1 = re.compile(r"^Dealt to %(PLYR)s(?: \[(?P<OLDCARDS>.+?)\])?( \[(?P<NEWCARDS>.+?)\])" % substitutions, re.MULTILINE)
181 re_HeroCards2 = re.compile(r"rd(s)? to %(PLYR)s: (?P<OLDCARDS>NONE)?(?P<NEWCARDS>.+)\n" % substitutions, re.MULTILINE)
182 re_Action1 = re.compile(r"""^%(PLYR)s:?(?P<ATYPE>\sbets|\schecks|\sraises|\scalls|\sfolds|\sBet|\sCheck|\sRaise(\sto)?|\sCall|\sFold|\sAllin)(?P<RAISETO>\s\(NF\))?(\sto)?(\s%(CUR)s(?P<BET>[%(NUM)s]+))?\s*(and\sis\sall.in)?(and\shas\sreached\sthe\s\[%(CUR)s\d\.,]+\scap)?(\son|\scards?)?(\s\[(?P<CARDS>.+?)\])?\s*$"""% substitutions, re.MULTILINE|re.VERBOSE)
183 re_Action2 = re.compile(r"""
184 ^%(PLYR)s(?P<ATYPE>\sbet|\schecked|\sraised(\sto)?|\scalled|\sfolded|\swent\sall\-in)
185 (\s(\-\s)?%(CUR)s(?P<BET>[%(NUM)s]+))?\s*$"""
186 % substitutions, re.MULTILINE|re.VERBOSE)
187 re_ShownCards1 = re.compile("^%(PLYR)s:? (?P<SHOWED>shows|Shows|mucked) \[(?P<CARDS>.*)\]" % substitutions, re.MULTILINE)
188 re_ShownCards2 = re.compile("^%(PLYR)s (?P<SHOWED>shows|mucks): (?P<CARDS>.+)\n" % substitutions, re.MULTILINE)
189 re_CollectPot1 = re.compile(r"^%(PLYR)s:? (collects|wins) %(CUR)s(?P<POT>[%(NUM)s]+)" % substitutions, re.MULTILINE)
190 re_CollectPot2 = re.compile(r"^%(PLYR)s wins %(CUR)s(?P<POT>[%(NUM)s]+)" % substitutions, re.MULTILINE)
191 re_Cancelled = re.compile('Hand\scancelled', re.MULTILINE)
192 re_Tournament = re.compile('\(Tournament:')
193 re_Hole = re.compile(r"\*\*\sDealing\scard")
194 re_Currency = re.compile(r"\s\-\s(?P<CURRENCY>%(CUR)s)[%(NUM)s]+\s(Max|Min)" % substitutions)
195 re_Max = re.compile(r"(\s(?P<MAX>(HU|\d+\sSeat))\s)")
196 re_FastFold = re.compile(r"^%(PLYR)s\sQuick\sFolded" % substitutions, re.MULTILINE)
198 def compilePlayerRegexs(self, hand):
199 pass
201 def readSupportedGames(self):
202 return [["ring", "hold", "nl"],
203 ["ring", "hold", "pl"],
204 ["ring", "hold", "fl"],
206 ["tour", "hold", "nl"],
207 ["tour", "hold", "pl"],
208 ["tour", "hold", "fl"],
209 ]
211 def determineGameType(self, handText):
212 m = self.re_Site.search(handText)
213 if not m:
214 tmp = handText[0:200]
215 log.error(("PokerTrackerToFpdb.determineGameType: '%s'") % tmp)
216 raise FpdbParseError
218 self.sitename = self.sites[m.group('SITE')][0]
219 self.siteId = self.sites[m.group('SITE')][1] # Needs to match id entry in Sites database
221 info = {}
222 if self.sitename in ('iPoker', 'Merge'):
223 m = self.re_GameInfo1.search(handText)
224 elif self.sitename=='Everest':
225 m = self.re_GameInfo2.search(handText)
226 elif self.sitename == 'Microgaming':
227 m = self.re_GameInfo3.search(handText)
228 if not m:
229 tmp = handText[0:200]
230 log.error(("PokerTrackerToFpdb.determineGameType: '%s'") % tmp)
231 raise FpdbParseError
233 mg = m.groupdict()
234 #print 'DEBUG determineGameType', '%r' % mg
235 if 'LIMIT' in mg and mg['LIMIT'] != None:
236 info['limitType'] = self.limits[mg['LIMIT']]
237 if 'GAME' in mg:
238 (info['base'], info['category']) = self.games[mg['GAME']]
239 if mg['LIMIT'] == None:
240 if info['category']=='omahahi':
241 info['limitType'] = 'pl'
242 elif info['category']=='holdem':
243 info['limitType'] = 'nl'
244 if 'SB' in mg:
245 info['sb'] = self.clearMoneyString(mg['SB'])
246 if 'BB' in mg:
247 info['bb'] = self.clearMoneyString(mg['BB'])
248 if 'CURRENCY' in mg and mg['CURRENCY'] is not None:
249 if self.sitename == 'Microgaming' and not mg['CURRENCY']:
250 m1 = self.re_Currency.search(mg['TABLE'])
251 if m1: mg['CURRENCY'] = m1.group('CURRENCY')
252 if self.sitename == 'iPoker' and not mg['CURRENCY']:
253 m1 = self.re_PlayerInfo1.search(handText)
254 if m1: mg['CURRENCY'] = m1.group('CURRENCY')
255 info['currency'] = self.currencies[mg['CURRENCY']]
256 if 'MIXED' in mg:
257 if mg['MIXED'] is not None: info['mix'] = self.mixes[mg['MIXED']]
259 if 'TOUR' in mg and mg['TOUR'] is not None:
260 info['type'] = 'tour'
261 info['currency'] = 'T$'
262 else:
263 info['type'] = 'ring'
266 if info['limitType'] == 'fl' and info['bb'] is not None:
267 if info['type'] == 'ring':
268 try:
269 bb = self.clearMoneyString(mg['BB'])
270 info['sb'] = self.Lim_Blinds[bb][0]
271 info['bb'] = self.Lim_Blinds[bb][1]
272 except KeyError:
273 tmp = handText[0:200]
274 log.error(("PokerTrackerToFpdb.determineGameType: Lim_Blinds has no lookup for '%s' - '%s'") % (mg['BB'], tmp))
275 raise FpdbParseError
276 else:
277 sb = self.clearMoneyString(mg['SB'])
278 info['sb'] = str((old_div(Decimal(sb),2)).quantize(Decimal("0.01")))
279 info['bb'] = str(Decimal(sb).quantize(Decimal("0.01")))
281 return info
283 def readHandInfo(self, hand):
284 info, m = {}, None
285 if self.sitename in ('iPoker', 'Merge'):
286 m3 = self.re_Tournament.search(hand.handText,re.DOTALL)
287 if m3:
288 m = self.re_HandInfo_Tour.search(hand.handText,re.DOTALL)
289 else:
290 m = self.re_HandInfo_Cash.search(hand.handText,re.DOTALL)
291 m2 = self.re_GameInfo1.search(hand.handText)
292 elif self.sitename=='Everest':
293 m2 = self.re_GameInfo2.search(hand.handText)
294 elif self.sitename=='Microgaming':
295 m2 = self.re_GameInfo3.search(hand.handText)
296 if (m is None and self.sitename not in ('Everest', 'Microgaming')) or m2 is None:
297 tmp = hand.handText[0:200]
298 log.error(("PokerTrackerToFpdb.readHandInfo: '%s'") % tmp)
299 raise FpdbParseError
301 if self.sitename not in ('Everest', 'Microgaming'):
302 info.update(m.groupdict())
303 info.update(m2.groupdict())
305 if self.sitename != 'Everest' and info.get('UNCALLED') is None:
306 hand.setUncalledBets(True)
308 #print 'readHandInfo', info
309 for key in info:
310 if key == 'DATETIME':
311 #2008/11/12 10:00:48 CET [2008/11/12 4:00:48 ET] # (both dates are parsed so ET date overrides the other)
312 #2008/08/17 - 01:14:43 (ET)
313 #2008/09/07 06:23:14 ET
314 if self.sitename in ('iPoker', 'Microgaming'):
315 m1 = self.re_DateTime1.finditer(info[key])
316 elif self.sitename == 'Merge':
317 m1 = self.re_DateTime2.finditer(info[key])
318 elif self.sitename == 'Everest':
319 m1 = self.re_DateTime3.finditer(info[key])
320 datetimestr = "2000/01/01 00:00:00" # default used if time not found
321 for a in m1:
322 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'))
323 #tz = a.group('TZ') # just assume ET??
324 #print " tz = ", tz, " datetime =", datetimestr
325 hand.startTime = datetime.datetime.strptime(datetimestr, "%Y/%m/%d %H:%M:%S") # also timezone at end, e.g. " ET"
326 hand.startTime = HandHistoryConverter.changeTimezone(hand.startTime, "ET", "UTC")
327 if key == 'HID':
328 if self.sitename == 'Merge':
329 hand.handid = info[key][:8] + info[key][9:]
330 else:
331 hand.handid = info[key]
332 if key == 'TOURNO':
333 hand.tourNo = info[key]
334 if key == 'BUYIN':
335 if hand.tourNo!=None:
336 tourneyname = ''
337 if self.sitename == 'Merge':
338 if self.Structures is None:
339 self.Structures = MergeStructures.MergeStructures()
340 tourneyname = re.split(",", m.group('TABLE'))[0].strip()
341 structure = self.Structures.lookupSnG(tourneyname, hand.startTime)
342 if structure!=None:
343 hand.buyin = int(100*structure['buyIn'])
344 hand.fee = int(100*structure['fee'])
345 hand.buyinCurrency=structure['currency']
346 hand.maxseats = structure['seats']
347 hand.isSng = True
348 else:
349 #print 'DEBUG', 'no match for tourney %s tourNo %s' % (tourneyname, hand.tourNo)
350 hand.buyin = 0
351 hand.fee = 0
352 hand.buyinCurrency="NA"
353 hand.maxseats = None
354 if self.sitename != 'Merge' or hand.buyin==0:
355 if info[key] == 'Freeroll' or 'Free' in tourneyname:
356 hand.buyin = 0
357 hand.fee = 0
358 hand.buyinCurrency = "FREE"
359 else:
360 if info[key].find("$")!=-1:
361 hand.buyinCurrency="USD"
362 elif info[key].find(u"£")!=-1:
363 hand.buyinCurrency="GBP"
364 elif info[key].find(u"€")!=-1:
365 hand.buyinCurrency="EUR"
366 elif re.match("^[0-9+]*$", info[key]):
367 hand.buyinCurrency="play"
368 else:
369 #FIXME: handle other currencies, play money
370 log.error(("PokerTrackerToFpdb.readHandInfo: Failed to detect currency.") + " Hand ID: %s: '%s'" % (hand.handid, info[key]))
371 raise FpdbParseError
373 info['BIAMT'] = info['BIAMT'].strip(u'$€£')
374 info['BIRAKE'] = info['BIRAKE'].strip(u'$€£')
376 hand.buyin = int(100*Decimal(info['BIAMT']))
377 hand.fee = int(100*Decimal(info['BIRAKE']))
378 if key == 'TABLE':
379 if hand.gametype['type'] == 'tour':
380 hand.tablename = '0'
381 elif hand.gametype['type'] == 'tour' and self.sitename == 'Microgaming':
382 hand.tablename = info[key]
383 else:
384 hand.tablename = re.split(",", info[key])[0]
385 hand.tablename = hand.tablename.strip()
386 if 'Blaze' in hand.tablename:
387 hand.gametype['fast'] = True
388 if self.sitename == 'Microgaming':
389 m3 = self.re_Max.search(hand.tablename)
390 if m3 and m3.group('MAX'):
391 if m3.group('MAX')=='HU':
392 hand.maxseats = 2
393 elif len(m3.group('MAX').split(' '))==2:
394 hand.maxseats = int(m3.group('MAX').split(' ')[0])
395 if key == 'BUTTON':
396 hand.buttonpos = info[key]
397 if key == 'MAX' and info[key] != None:
398 seats = int(info[key])
399 if seats <=10:
400 hand.maxseats = int(info[key])
402 if key == 'PLAY' and info['PLAY'] is not None and info['PLAY']=='Play':
403# hand.currency = 'play' # overrides previously set value
404 hand.gametype['currency'] = 'play'
406 if self.re_FastFold.search(hand.handText):
407 hand.fastFold = True
409 if self.re_Cancelled.search(hand.handText):
410 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid)
412 def readButton(self, hand):
413 m = self.re_Button.search(hand.handText)
414 if m:
415 hand.buttonpos = int(m.group('BUTTON'))
416 else:
417 log.info('readButton: ' + ('not found'))
419 def readPlayerStacks(self, hand):
420 if self.sitename != 'Microgaming':
421 m = self.re_PlayerInfo1.finditer(hand.handText)
422 else:
423 m = self.re_PlayerInfo2.finditer(hand.handText)
424 for a in m:
425 #print a.group('SEAT'), a.group('PNAME'), a.group('CASH')
426 hand.addPlayer(int(a.group('SEAT')), a.group('PNAME'), a.group('CASH'))
427 if a.group('BUTTON')!=None:
428 hand.buttonpos = int(a.group('SEAT'))
429 if len(hand.players)==1:
430 raise FpdbHandPartial(("Hand '%s' was cancelled.") % hand.handid)
432 def markStreets(self, hand):
434 # PREFLOP = ** Dealing down cards **
435 # This re fails if, say, river is missing; then we don't get the ** that starts the river.
436 if self.sitename == 'Microgaming':
437 m = re.search(r"\*\* Dealing ca(?P<PREFLOP>.+(?=\*\* Dealing the flop)|.+)"
438 r"(\*\* Dealing the flop(?P<FLOP>:\s.+(?=\*\* Dealing the turn)|.+))?"
439 r"(\*\* Dealing the turn(?P<TURN>:\s.+(?=\*\* Dealing the river)|.+))?"
440 r"(\*\* Dealing the river(?P<RIVER>:\s.+))?", hand.handText,re.DOTALL)
441 else:
442 m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
443 r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S\S? \S\S\S? \S\S\S?\].+(?=\*\*\* TURN \*\*\*)|.+))?"
444 r"(\*\*\* TURN \*\*\* (?P<TURN>\[\S\S\S?\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
445 r"(\*\*\* RIVER \*\*\* (?P<RIVER>\[\S\S\S?\].+))?", hand.handText,re.DOTALL)
446 hand.addStreets(m)
448 def readCommunityCards(self, hand, street): # street has been matched by markStreets, so exists in this hand
449 if street in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
450 #print "DEBUG readCommunityCards:", street, hand.streets.group(street)
451 if self.sitename == 'Microgaming':
452 m = self.re_Board2.search(hand.streets[street])
453 cards = [c.replace('10', 'T').strip() for c in m.group('CARDS').replace(' of ', '').split(', ')]
454 else:
455 m = self.re_Board1.search(hand.streets[street])
456 if self.sitename=='iPoker':
457 cards = [c[1:].replace('10', 'T') + c[0].lower() for c in m.group('CARDS').split(' ')]
458 else:
459 cards = [c.replace('10', 'T').strip() for c in m.group('CARDS').split(' ')]
460 hand.setCommunityCards(street, cards)
462 def readAntes(self, hand):
463 log.debug(("reading antes"))
464 m = self.re_Antes.finditer(hand.handText)
465 for player in m:
466 #~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
467 self.adjustMergeTourneyStack(hand, player.group('PNAME'), player.group('ANTE'))
468 hand.addAnte(player.group('PNAME'), player.group('ANTE'))
470 def readBlinds(self, hand):
471 liveBlind, bb, sb = True, None, None
472 for a in self.re_PostSB.finditer(hand.handText):
473 sb = self.clearMoneyString(a.group('SB'))
474 if liveBlind:
475 self.adjustMergeTourneyStack(hand, a.group('PNAME'), a.group('SB'))
476 hand.addBlind(a.group('PNAME'), 'small blind', sb)
477 if not hand.gametype['sb']:
478 hand.gametype['sb'] = sb
479 liveBlind = False
480 elif hand.gametype['type'] == 'tour':
481 self.adjustMergeTourneyStack(hand, a.group('PNAME'), a.group('SB'))
482 if not hand.gametype['bb']:
483 hand.gametype['bb'] = sb
484 hand.addBlind(a.group('PNAME'), 'big blind', sb)
485 else:
486 # Post dead blinds as ante
487 self.adjustMergeTourneyStack(hand, a.group('PNAME'), a.group('SB'))
488 hand.addBlind(a.group('PNAME'), 'secondsb', sb)
489 for a in self.re_PostBB.finditer(hand.handText):
490 bb = self.clearMoneyString(a.group('BB'))
491 self.adjustMergeTourneyStack(hand, a.group('PNAME'), a.group('BB'))
492 if not hand.gametype['bb']:
493 hand.gametype['bb'] = bb
494 hand.addBlind(a.group('PNAME'), 'big blind', bb)
495 else:
496 both = Decimal(hand.gametype['bb']) + old_div(Decimal(hand.gametype['bb']),2)
497 if both == Decimal(a.group('BB')):
498 hand.addBlind(a.group('PNAME'), 'both', bb)
499 else:
500 hand.addBlind(a.group('PNAME'), 'big blind', bb)
502 if self.sitename == 'Microgaming':
503 for a in self.re_PostBoth2.finditer(hand.handText):
504 if self.clearMoneyString(a.group('SBBB')) == hand.gametype['sb']:
505 hand.addBlind(a.group('PNAME'), 'secondsb', self.clearMoneyString(a.group('SBBB')))
506 else:
507 bet = self.clearMoneyString(a.group('SBBB'))
508 amount = str(Decimal(bet) + old_div(Decimal(bet),2))
509 hand.addBlind(a.group('PNAME'), 'both', amount)
510 for a in self.re_Action2.finditer(self.re_Hole.split(hand.handText)[0]):
511 if a.group('ATYPE') == ' went all-in':
512 amount = Decimal(self.clearMoneyString(a.group('BET')))
513 player = a.group('PNAME')
514 if bb is None:
515 hand.addBlind(player, 'big blind', self.clearMoneyString(a.group('BET')))
516 self.allInBlind(hand, 'PREFLOP', a, 'big blind')
517 elif sb is None:
518 hand.addBlind(player, 'small blind', self.clearMoneyString(a.group('BET')))
519 self.allInBlind(hand, 'PREFLOP', a, 'small blind')
520 else:
521 for a in self.re_PostBoth1.finditer(hand.handText):
522 self.adjustMergeTourneyStack(hand, a.group('PNAME'), a.group('SBBB'))
523 if (Decimal(str(hand.sb)) == Decimal(self.clearMoneyString(a.group('SBBB')))):
524 hand.addBlind(a.group('PNAME'), 'small blind', self.clearMoneyString(a.group('SBBB')))
525 else:
526 hand.addBlind(a.group('PNAME'), 'both', self.clearMoneyString(a.group('SBBB')))
528 # FIXME
529 # The following should only trigger when a small blind is missing in a tournament, or the sb/bb is ALL_IN
530 # see http://sourceforge.net/apps/mantisbt/fpdb/view.php?id=115
531 if hand.gametype['type'] == 'tour' and self.sitename in ('Merge', 'iPoker'):
532 if hand.gametype['sb'] == None and hand.gametype['bb'] == None:
533 hand.gametype['sb'] = "1"
534 hand.gametype['bb'] = "2"
535 elif hand.gametype['sb'] == None:
536 hand.gametype['sb'] = str(old_div(int(Decimal(hand.gametype['bb'])),2))
537 elif hand.gametype['bb'] == None:
538 hand.gametype['bb'] = str(int(Decimal(hand.gametype['sb']))*2)
539 if old_div(int(Decimal(hand.gametype['bb'])),2) != int(Decimal(hand.gametype['sb'])):
540 if old_div(int(Decimal(hand.gametype['bb'])),2) < int(Decimal(hand.gametype['sb'])):
541 hand.gametype['bb'] = str(int(Decimal(hand.gametype['sb']))*2)
542 else:
543 hand.gametype['sb'] = str(old_div(int(Decimal(hand.gametype['bb'])),2))
545 def readHoleCards(self, hand):
546# streets PREFLOP, PREDRAW, and THIRD are special cases beacause
547# we need to grab hero's cards
548 if self.sitename != 'Microgaming':
549 re_HeroCards = self.re_HeroCards1
550 else:
551 re_HeroCards = self.re_HeroCards2
552 for street in ('PREFLOP', 'DEAL'):
553 if street in list(hand.streets.keys()):
554 m = re_HeroCards.finditer(hand.streets[street])
555 for found in m:
556# if m == None:
557# hand.involved = False
558# else:
559 hand.hero = found.group('PNAME')
560 if self.sitename=='iPoker':
561 newcards = [c[1:].replace('10', 'T') + c[0].lower() for c in found.group('NEWCARDS').split(' ')]
562 elif self.sitename=='Microgaming':
563 newcards = [c.replace('10', 'T').strip() for c in found.group('NEWCARDS').replace(' of ', '').split(', ')]
564 else:
565 newcards = [c.replace('10', 'T').strip() for c in found.group('NEWCARDS').split(' ')]
566 hand.addHoleCards(street, hand.hero, closed=newcards, shown=False, mucked=False, dealt=True)
568 for street, text in list(hand.streets.items()):
569 if not text or street in ('PREFLOP', 'DEAL'): continue # already done these
570 m = re_HeroCards.finditer(hand.streets[street])
571 for found in m:
572 player = found.group('PNAME')
573 if found.group('NEWCARDS') is None:
574 newcards = []
575 else:
576 if self.sitename=='iPoker':
577 newcards = [c[1:].replace('10', 'T') + c[0].lower() for c in found.group('NEWCARDS').split(' ')]
578 elif self.sitename=='Microgaming':
579 newcards = [c.replace('10', 'T').strip() for c in found.group('NEWCARDS').replace(' of ', '').split(', ')]
580 else:
581 newcards = [c.replace('10', 'T').strip() for c in found.group('NEWCARDS').split(' ')]
582 if found.group('OLDCARDS') is None:
583 oldcards = []
584 else:
585 if self.sitename=='iPoker':
586 oldcards = [c[1:].replace('10', 'T') + c[0].lower() for c in found.group('OLDCARDS').split(' ')]
587 else:
588 oldcards = [c.replace('10', 'T').strip() for c in found.group('OLDCARDS').split(' ')]
590 hand.addHoleCards(street, player, open=newcards, closed=oldcards, shown=False, mucked=False, dealt=False)
593 def readAction(self, hand, street):
594 if self.sitename != 'Microgaming':
595 m = self.re_Action1.finditer(hand.streets[street])
596 else:
597 m = self.re_Action2.finditer(hand.streets[street])
598 curr_pot = Decimal('0')
599 for action in m:
600 acts = action.groupdict()
601 #print "DEBUG: acts: %s" %acts
602 if action.group('ATYPE') in (' folds', ' Fold', ' folded'):
603 hand.addFold( street, action.group('PNAME'))
604 elif action.group('ATYPE') in (' checks', ' Check', ' checked'):
605 hand.addCheck( street, action.group('PNAME'))
606 elif action.group('ATYPE') in (' calls', ' Call', ' called'):
607 hand.addCall( street, action.group('PNAME'), action.group('BET') )
608 elif action.group('ATYPE') in (' raises', ' Raise', ' raised', ' raised to', ' Raise to'):
609 amount = Decimal(self.clearMoneyString(action.group('BET')))
610 if self.sitename == 'Merge':
611 hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
612 elif self.sitename == 'Microgaming' or action.group('ATYPE')==' Raise to':
613 hand.addCallandRaise(street, action.group('PNAME'), action.group('BET') )
614 else:
615 if curr_pot > amount:
616 hand.addCall( street, action.group('PNAME'), action.group('BET') )
617 #elif not action.group('RAISETO') and action.group('ATYPE')==' Raise':
618 # hand.addRaiseBy( street, action.group('PNAME'), action.group('BET') )
619 else:
620 hand.addRaiseTo( street, action.group('PNAME'), action.group('BET') )
621 curr_pot = amount
622 elif action.group('ATYPE') in (' bets', ' Bet', ' bet'):
623 if self.sitename == 'Microgaming' and street in ('PREFLOP', 'THIRD', 'DEAL'):
624 hand.addCallandRaise(street, action.group('PNAME'), action.group('BET') )
625 else:
626 hand.addBet( street, action.group('PNAME'), action.group('BET') )
627 curr_pot = Decimal(self.clearMoneyString(action.group('BET')))
628 elif action.group('ATYPE') in (' Allin', ' went all-in'):
629 amount = Decimal(self.clearMoneyString(action.group('BET')))
630 hand.addAllIn(street, action.group('PNAME'), action.group('BET'))
631 if curr_pot > amount and curr_pot > Decimal('0') and self.sitename == 'Microgaming':
632 hand.setUncalledBets(False)
633 curr_pot = amount
634 else:
635 log.debug(("DEBUG:") + " " + ("Unimplemented %s: '%s' '%s'") % ("readAction", action.group('PNAME'), action.group('ATYPE')))
637 def allInBlind(self, hand, street, action, actiontype):
638 if street in ('PREFLOP', 'DEAL'):
639 player = action.group('PNAME')
640 if hand.stacks[player]==0:
641 hand.setUncalledBets(True)
643 def adjustMergeTourneyStack(self, hand, player, amount):
644 if self.sitename == 'Merge':
645 amount = Decimal(self.clearMoneyString(amount))
646 if hand.gametype['type'] == 'tour':
647 for p in hand.players:
648 if p[1]==player:
649 stack = Decimal(p[2])
650 stack += amount
651 p[2] = str(stack)
652 hand.stacks[player] += amount
654 def readCollectPot(self,hand):
655 if self.sitename == 'Microgaming':
656 for m in self.re_CollectPot2.finditer(hand.handText):
657 hand.addCollectPot(player=m.group('PNAME'),pot=re.sub(u',',u'',m.group('POT')))
658 else:
659 for m in self.re_CollectPot1.finditer(hand.handText):
660 hand.addCollectPot(player=m.group('PNAME'),pot=re.sub(u',',u'',m.group('POT')))
662 def readShowdownActions(self, hand):
663 pass
665 def readShownCards(self,hand):
666 found = []
667 if self.sitename=='Microgaming':
668 re_ShownCards = self.re_ShownCards2
669 else:
670 re_ShownCards = self.re_ShownCards1
671 for m in re_ShownCards.finditer(hand.handText):
672 if m.group('CARDS') is not None and m.group('PNAME') not in found:
673 if self.sitename=='iPoker':
674 cards = [c[1:].replace('10', 'T') + c[0].lower() for c in m.group('CARDS').split(' ')]
675 elif self.sitename=='Microgaming':
676 cards = [c.replace('10', 'T').strip() for c in m.group('CARDS').replace(' of ', '').split(', ')]
677 else:
678 cards = [c.replace('10', 'T').strip() for c in m.group('CARDS').split(' ')]
680 (shown, mucked) = (False, False)
681 if m.group('SHOWED') in ('shows', 'Shows'): shown = True
682 elif m.group('SHOWED') in ('mucked', 'mucks'): mucked = True
683 found.append(m.group('PNAME'))
685 #print "DEBUG: hand.addShownCards(%s, %s, %s, %s)" %(cards, m.group('PNAME'), shown, mucked)
686 hand.addShownCards(cards=cards, player=m.group('PNAME'), shown=shown, mucked=mucked)