Coverage for KingsClubToFpdb.py: 0%
324 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-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########################################################################
21#import L10n
22#_ = L10n.get_translation()
24# TODO: straighten out discards for draw games
26import sys
27from HandHistoryConverter import *
28from decimal_wrapper import Decimal
30# KingsClub HH Format
32class KingsClub(HandHistoryConverter):
34 # Class Variables
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 }
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 }
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'}
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)
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)
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)
145 re_TourNo = re.compile(u"Table\s\'T(?P<TOURNO>\d+)\s\[(?P<TABLENO>\d+)\]\'")
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)
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)
190 re_STP = re.compile(u"""
191 STP\sadded:\s%(CUR)s(?P<AMOUNT>[,\.0-9]+)"""
192 % substitutions, re.MULTILINE|re.VERBOSE)
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)
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)
210 def readSupportedGames(self):
211 return [["ring", "hold", "nl"],
212 ["ring", "hold", "pl"],
213 ["ring", "hold", "fl"],
214 ["ring", "hold", "pn"],
216 ["ring", "stud", "fl"],
218 ["ring", "draw", "fl"],
219 ["ring", "draw", "pl"],
220 ["ring", "draw", "nl"],
222 ["tour", "hold", "nl"],
223 ["tour", "hold", "pl"],
224 ["tour", "hold", "fl"],
225 ["tour", "hold", "pn"],
227 ["tour", "stud", "fl"],
229 ["tour", "draw", "fl"],
230 ["tour", "draw", "pl"],
231 ["tour", "draw", "nl"],
232 ]
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
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'])
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'
260 m2 = self.re_Split.search(handText)
261 if m2:
262 info['split'] = True
263 else:
264 info['split'] = False
266 m3 = self.re_Table.search(handText)
267 if m3 and 'AOF' in m3.group('TABLE'):
268 info['category'] = 'aof_omaha'
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")))
285 return info
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")
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
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")
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)
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'))
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 )
351 def markStreets(self, hand):
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
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
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)
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')})
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
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'))
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'))
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 )
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 )
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)
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']
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)
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')))
625 def readShowdownActions(self, hand):
626 pass
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')))
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)