2 # -*- coding: utf-8 -*-
4 # Copyright 2009-2011, Grigorij Indigirkin
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.
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.
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 ########################################################################
22 _
= L10n
.get_translation()
25 from collections
import defaultdict
27 from Configuration
import LOCALE_ENCODING
28 from Exceptions
import FpdbParseError
29 from HandHistoryConverter
import *
31 # PartyPoker HH Format
33 class PartyPoker(HandHistoryConverter
):
34 sitename
= "PartyPoker"
38 sym
= {'USD': "\$", 'EUR': u
"\u20ac", 'T$': ""}
39 currencies
= {"\$": "USD", "$": "USD", u
"\xe2\x82\xac": "EUR", u
"\u20ac": "EUR", '': "T$"}
41 'LEGAL_ISO' : "USD|EUR", # legal ISO currency codes
42 'LS' : u
"\$|\u20ac|\xe2\x82\xac|", # Currency symbols - Euro(cp1252, utf-8)
45 limits
= { 'NL':'nl', 'PL':'pl', '':'fl' }
46 games
= { # base, category
47 "Texas Hold'em" : ('hold','holdem'),
48 'Omaha' : ('hold','omahahi'),
49 "7 Card Stud Hi-Lo" : ('stud','studhilo'),
50 "7 Card Stud" : ('stud','studhi'),
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.30': ('0.07', '0.15'), '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 '10.00': ('2.00', '5.00'), '10': ('2.00', '5.00'),
61 '20.00': ('5.00', '10.00'), '20': ('5.00', '10.00'),
62 # Commented lines need verification
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 #'100.00': ('25.00', '50.00'), '100': ('25.00', '50.00'),
67 #'200.00': ('50.00', '100.00'), '200': ('50.00', '100.00'),
68 #'500.00': ('??', '250.00'), '500': ('??', '250.00'),
70 NLim_Blinds_20bb
= { '0.80': ('0.01', '0.02'),
71 '1.60': ('0.02', '0.04'),
72 '4': ('0.05', '0.10'),
73 '10': ('0.10', '0.25'),
74 '20': ('0.25', '0.50'),
75 '40': ('0.50', '1.00'),
76 #'10': ('0.10', '0.25'),
77 #'10': ('0.10', '0.25'),
78 #'10': ('0.10', '0.25'),
79 #'10': ('0.10', '0.25'),
80 #'10': ('0.10', '0.25'),
83 months
= { 'January':1, 'Jan':1, 'February':2, 'Feb':2, 'March':3, 'Mar':3,
84 'April':4, 'Apr':4, 'May':5, 'May':5, 'June':6, 'Jun':6,
85 'July':7, 'Jul':7, 'August':8, 'Aug':8, 'September':9, 'Sep':9,
86 'October':10, 'Oct':10, 'November':11, 'Nov':11, 'December':12, 'Dec':12}
89 re_GameInfo
= re
.compile(u
"""
90 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\d+)\s\*{5}\s+
91 ((?P<CURRENCY>[%(LS)s]))?\s*
93 ([%(LS)s]?(?P<SB>[%(NUM)s]+)/[%(LS)s]?(?P<BB>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s)?\s*)|
94 ((?P<CASHBI>[%(NUM)s]+)\s(?:%(LEGAL_ISO)s)?\s*)(?P<LIMIT2>(NL|PL|))?\s*
97 (?P<GAME>(Texas\sHold\'em|Omaha|7\sCard\sStud\sHi-Lo|7\sCard\sStud))\s*
100 (\((?P<LIMIT>(NL|PL|))\)\s*)?
101 (\(STT\sTournament\s\#(?P<TOURNO>\d+)\)\s*)?
105 """ % substitutions
, re
.VERBOSE | re
.UNICODE
)
107 re_HandInfo
= re
.compile("""
108 ^Table\s+(?P<TTYPE>[$a-zA-Z0-9 ]+)?\s+
109 (?: \#|\(|)(?P<TABLE>\d+)\)?\s+
110 (?:[a-zA-Z0-9 ]+\s+\#(?P<MTTTABLE>\d+).+)?
112 \((?P<PLAY>Real|Play)\s+Money\)\s+(--\s*)? # FIXME: check if play money is correct
113 Seat\s+(?P<BUTTON>\d+)\sis\sthe\sbutton
114 \s+Total\s+number\s+of\s+players\s+\:\s+(?P<PLYRS>\d+)/?(?P<MAX>\d+)?
115 """, re
.VERBOSE|re
.MULTILINE|re
.DOTALL
)
117 re_GameInfoTrny
= re
.compile("""
118 \*{5}\sHand\sHistory\s(F|f)or\sGame\s(?P<HID>\d+)\s\*{5}\s+
119 (?P<LIMIT>(NL|PL|))\s*
120 (?P<GAME>(Texas\ Hold\'em|Omaha))\s+
121 (?:(?P<BUYIN>[%(LS)s]?[%(NUM)s]+)\s*(?P<BUYIN_CURRENCY>%(LEGAL_ISO)s)?\s*Buy-in\s+)?
122 Trny:\s?(?P<TOURNO>\d+)\s+
123 Level:\s*(?P<LEVEL>\d+)\s+
124 ((Blinds|Stakes)(?:-Antes)?)\(
125 (?P<SB>[%(NUM)s ]+)\s*
127 (?:\s*-\s*(?P<ANTE>[%(NUM)s ]+)\$?)?
131 """ % substitutions
, re
.VERBOSE | re
.UNICODE
)
133 re_PlayerInfo
= re
.compile(u
"""
134 Seat\s(?P<SEAT>\d+):\s
136 \(\s*[%(LS)s]?(?P<CASH>[%(NUM)s]+)\s*(?:%(LEGAL_ISO)s|)\s*\)
137 """ % substitutions
, re
.VERBOSE| re
.UNICODE
)
140 re_CountedSeats
= re
.compile("^Total\s+number\s+of\s+players\s*:\s*(?P<COUNTED_SEATS>\d+)", re
.MULTILINE
)
141 re_SplitHands
= re
.compile('\x00+')
142 re_TailSplitHands
= re
.compile('(\x00+)')
144 re_Button
= re
.compile('Seat (?P<BUTTON>\d+) is the button', re
.MULTILINE
)
145 re_Board
= re
.compile(r
"\[(?P<CARDS>.+)\]")
146 re_NoSmallBlind
= re
.compile(
147 '^There is no Small Blind in this hand as the Big Blind '
148 'of the previous hand left the table', re
.MULTILINE
)
149 re_20BBmin
= re
.compile(r
"Table 20BB Min")
151 def allHandsAsList(self
):
152 list = HandHistoryConverter
.allHandsAsList(self
)
155 return filter(lambda text
: len(text
.strip()), list)
157 def guessMaxSeats(self
, hand
):
158 """Return a guess at max_seats when not specified in HH."""
159 mo
= self
.maxOccSeat(hand
)
160 if mo
== 10: return mo
163 # there are 9-max tables for cash and 10-max for tournaments
164 return 9 if hand
.gametype
['type']=='ring' else 10
166 def compilePlayerRegexs(self
, hand
):
167 players
= set([player
[1] for player
in hand
.players
])
168 if not players
<= self
.compiledPlayers
: # x <= y means 'x is subset of y'
170 self
.compiledPlayers
= players
171 player_re
= "(?P<PNAME>" + "|".join(map(re
.escape
, players
)) + ")"
172 subst
= {'PLYR': player_re
, 'CUR_SYM': self
.sym
[hand
.gametype
['currency']],
173 'CUR': hand
.gametype
['currency'] if hand
.gametype
['currency']!='T$' else '',
176 self
.re_PostSB
= re
.compile(
177 r
"^%(PLYR)s posts small blind [%(BRAX)s]?%(CUR_SYM)s(?P<SB>[.,0-9]+) ?%(CUR)s[%(BRAX)s]?\."
178 % subst
, re
.MULTILINE
)
179 self
.re_PostBB
= re
.compile(
180 u
"%(PLYR)s posts big blind [%(BRAX)s]?%(CUR_SYM)s(?P<BB>[.,0-9]+) ?%(CUR)s[%(BRAX)s]?\."
181 % subst
, re
.MULTILINE
)
182 self
.re_PostDead
= re
.compile(
183 r
"^%(PLYR)s posts big blind + dead [%(BRAX)s]?(?P<BBNDEAD>[.,0-9]+) ?%(CUR_SYM)s[%(BRAX)s]?\." % subst
,
185 self
.re_Antes
= re
.compile(
186 r
"^%(PLYR)s posts ante [%(BRAX)s]?%(CUR_SYM)s(?P<ANTE>[.,0-9]+) ?%(CUR)s[%(BRAX)s]?" % subst
,
188 self
.re_HeroCards
= re
.compile(
189 r
"^Dealt to %(PLYR)s \[\s*(?P<NEWCARDS>.+)\s*\]" % subst
,
191 self
.re_Action
= re
.compile(u
"""
192 ^%(PLYR)s\s+(?P<ATYPE>bets|checks|raises|calls|folds|is\sall-In)
193 (?:\s+[%(BRAX)s]?%(CUR_SYM)s?(?P<BET>[.,\d]+)\s*(%(CUR)s)?[%(BRAX)s]?)?
194 """ % subst
, re
.MULTILINE|re
.VERBOSE
)
195 self
.re_ShownCards
= re
.compile(
196 r
"^%s (?P<SHOWED>(?:doesn\'t )?shows?) " % player_re
+
197 r
"\[ *(?P<CARDS>.+) *\](?P<COMBINATION>.+)\.",
199 self
.re_CollectPot
= re
.compile(
200 r
"""^%(PLYR)s \s+ wins \s+
201 %(CUR_SYM)s(?P<POT>[.,\d]+)\s*%(CUR)s""" % subst
,
202 re
.MULTILINE|re
.VERBOSE
)
204 def readSupportedGames(self
):
205 return [["ring", "hold", "nl"],
206 ["ring", "hold", "pl"],
207 ["ring", "hold", "fl"],
209 ["ring", "stud", "fl"],
211 ["tour", "hold", "nl"],
212 ["tour", "hold", "pl"],
213 ["tour", "hold", "fl"],
216 def determineGameType(self
, handText
):
218 m
= self
.re_GameInfo
.search(handText
)
220 m
= self
.re_GameInfoTrny
.search(handText
)
222 tmp
= handText
[0:150]
223 log
.error(_("Unable to recognise gametype from: '%s'") % tmp
)
224 log
.error("determineGameType: " + _("Raising FpdbParseError"))
225 raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp
)
228 #print "DEBUG: mg: %s" % mg
230 if 'LIMIT' in mg
and mg
['LIMIT'] != None:
231 info
['limitType'] = self
.limits
[mg
['LIMIT']]
232 if 'LIMIT2' in mg
and mg
['LIMIT2'] != None:
233 info
['limitType'] = self
.limits
[mg
['LIMIT2']]
234 if mg
['LIMIT'] == None and mg
['LIMIT2'] == None:
235 info
['limitType'] = 'fl'
237 (info
['base'], info
['category']) = self
.games
[mg
['GAME']]
238 if 'CASHBI' in mg
and mg
['CASHBI'] != None:
239 # The summary is using buyin rather then listing the blinds
240 # Only with NL games?
241 mg
['CASHBI'] = self
.clearMoneyString(mg
['CASHBI'])
242 m_20BBmin
= self
.re_20BBmin
.search(handText
)
243 if m_20BBmin
is not None:
244 info
['sb'] = self
.NLim_Blinds_20bb
[mg
['CASHBI']][0]
245 info
['bb'] = self
.NLim_Blinds_20bb
[mg
['CASHBI']][1]
247 bb
= Decimal(mg
['CASHBI'])/100
252 mg
['SB'] = self
.clearMoneyString(mg
['SB'])
253 mg
['BB'] = self
.clearMoneyString(mg
['BB'])
255 info
['sb'] = mg
['SB']
257 info
['bb'] = mg
['BB']
259 if mg
['CURRENCY'] == None:
260 info
['currency'] = self
.currencies
['$']
262 info
['currency'] = self
.currencies
[mg
['CURRENCY']]
263 if 'BUYIN_CURRENCY' in mg
:
264 if mg
['BUYIN_CURRENCY'] == None:
265 info
['currency'] = self
.currencies
['$']
267 info
['currency'] = mg
['BUYIN_CURRENCY']
269 if mg
['MIXED'] is not None: info
['mix'] = self
.mixes
[mg
['MIXED']]
271 if 'TOURNO' in mg
and mg
['TOURNO'] is None:
272 info
['type'] = 'ring'
274 info
['type'] = 'tour'
276 if info
['limitType'] == 'fl' and info
['bb'] is not None and info
['type'] == 'ring':
278 info
['sb'] = self
.Lim_Blinds
[mg
['BB']][0]
279 info
['bb'] = self
.Lim_Blinds
[mg
['BB']][1]
281 log
.error(_("Lim_Blinds has no lookup for '%s'") % mg
['BB'])
282 log
.error("determineGameType: " + _("Raising FpdbParseError"))
283 raise FpdbParseError(_("Lim_Blinds has no lookup for '%s'") % mg
['BB'])
284 #print "DEUBG: DGT.info: %s" % info
288 def readHandInfo(self
, hand
):
291 m
= self
.re_HandInfo
.search(hand
.handText
,re
.DOTALL
)
292 if hand
.gametype
['type'] == 'ring':
293 m2
= self
.re_GameInfo
.search(hand
.handText
)
295 m2
= self
.re_GameInfoTrny
.search(hand
.handText
)
296 if m
is None or m2
is None:
297 log
.error(_("No match in readHandInfo: '%s'") % hand
.handText
[0:100])
298 raise FpdbParseError(_("No match in readHandInfo: '%s'") % hand
.handText
[0:100])
299 info
.update(m
.groupdict())
300 info
.update(m2
.groupdict())
302 #print "DEBUG: readHand.info: %s" % info
304 # FIXME: it's dirty hack
305 # party doesnt subtract uncalled money from commited money
306 # so hand.totalPot calculation has to be redefined
307 from Hand
import Pot
, HoldemOmahaHand
308 def getNewTotalPot(origTotalPot
):
310 if self
.totalpot
is None:
312 self
.totalpot
= self
.pot
.total
313 for i
,v
in enumerate(self
.collected
):
314 if v
[0] in self
.pot
.returned
:
315 self
.collected
[i
][1] = Decimal(v
[1]) - self
.pot
.returned
[v
[0]]
316 self
.collectees
[v
[0]] -= self
.pot
.returned
[v
[0]]
317 self
.pot
.returned
[v
[0]] = 0
318 return origTotalPot()
320 instancemethod
= type(hand
.totalPot
)
321 hand
.totalPot
= instancemethod(getNewTotalPot(hand
.totalPot
), hand
, HoldemOmahaHand
)
325 if key
== 'DATETIME':
326 #Saturday, July 25, 07:53:52 EDT 2009
327 #Thursday, July 30, 21:40:41 MSKS 2009
328 #Sunday, October 25, 13:39:07 MSK 2009
329 #Mon Jul 12 13:38:32 EDT 2010
331 r
"\w+?,?\s+?(?P<M>\w+)\s+(?P<D>\d+),?\s+(?P<H>\d+):(?P<MIN>\d+):(?P<S>\d+)\s+(?P<TZ>[A-Z]+)\s+(?P<Y>\d+)",
335 month
= self
.months
[m2
.group('M')]
336 datetimestr
= "%s/%s/%s %s:%s:%s" % (m2
.group('Y'), month
,m2
.group('D'),m2
.group('H'),m2
.group('MIN'),m2
.group('S'))
337 hand
.startTime
= datetime
.datetime
.strptime(datetimestr
, "%Y/%m/%d %H:%M:%S")
338 # FIXME: some timezone correction required
339 #tzShift = defaultdict(lambda:0, {'EDT': -5, 'EST': -6, 'MSKS': 3})
340 #hand.starttime -= datetime.timedelta(hours=tzShift[m2.group('TZ')])
343 hand
.handid
= info
[key
]
345 hand
.tablename
= info
[key
]
346 if key
== 'MTTTABLE':
347 if info
[key
] != None:
348 hand
.tablename
= info
[key
]
349 hand
.tourNo
= info
['TABLE']
351 hand
.buttonpos
= info
[key
]
353 hand
.tourNo
= info
[key
]
354 if key
== 'TABLE_ID_WRAPPER':
356 # FIXME: there is no such property in Hand class
359 if info
[key
] == None:
363 hand
.buyinCurrency
= "FREE"
365 elif hand
.tourNo
!= None:
368 hand
.buyinCurrency
= "FREE"
370 if info
[key
].find("$")!=-1:
371 hand
.buyinCurrency
="USD"
372 elif info
[key
].find(u
"€")!=-1:
373 hand
.buyinCurrency
="EUR"
375 raise FpdbParseError(_("Failed to detect currency.") + " " + _("Hand ID: %s: '%s'") % (hand
.handid
, info
[key
]))
376 info
[key
] = info
[key
].strip(u
'$€')
377 hand
.buyin
= int(100*Decimal(info
[key
]))
379 hand
.level
= info
[key
]
380 if key
== 'PLAY' and info
['PLAY'] != 'Real':
381 # if realy party doesn's save play money hh
382 hand
.gametype
['currency'] = 'play'
383 if key
== 'MAX' and info
[key
] is not None:
384 hand
.maxseats
= int(info
[key
])
387 def readButton(self
, hand
):
388 m
= self
.re_Button
.search(hand
.handText
)
390 hand
.buttonpos
= int(m
.group('BUTTON'))
392 log
.info(_('readButton: not found'))
394 def readPlayerStacks(self
, hand
):
395 log
.debug("readPlayerStacks")
396 m
= self
.re_PlayerInfo
.finditer(hand
.handText
)
398 zeroStackPlayers
= []
400 if a
.group('CASH') > '0':
401 #record max known stack for use with players with unknown stack
402 maxKnownStack
= max(a
.group('CASH'),maxKnownStack
)
403 hand
.addPlayer(int(a
.group('SEAT')), a
.group('PNAME'), self
.clearMoneyString(a
.group('CASH')))
405 #zero stacked players are added later
406 zeroStackPlayers
.append([int(a
.group('SEAT')), a
.group('PNAME'), self
.clearMoneyString(a
.group('CASH'))])
407 if hand
.gametype
['type'] == 'ring':
408 #finds first vacant seat after an exact seat
409 def findFirstEmptySeat(startSeat
):
410 while startSeat
in occupiedSeats
:
411 if startSeat
>= hand
.maxseats
:
416 re_JoiningPlayers
= re
.compile(r
"(?P<PLAYERNAME>.*) has joined the table")
417 re_BBPostingPlayers
= re
.compile(r
"(?P<PLAYERNAME>.*) posts big blind")
419 match_JoiningPlayers
= re_JoiningPlayers
.findall(hand
.handText
)
420 match_BBPostingPlayers
= re_BBPostingPlayers
.findall(hand
.handText
)
422 #add every player with zero stack, but:
423 #if a zero stacked player is just joined the table in this very hand then set his stack to maxKnownStack
424 for p
in zeroStackPlayers
:
425 if p
[1] in match_JoiningPlayers
:
426 p
[2] = self
.clearMoneyString(maxKnownStack
)
427 hand
.addPlayer(p
[0],p
[1],p
[2])
429 seatedPlayers
= list([(f
[1]) for f
in hand
.players
])
431 #it works for all known cases as of 2010-09-28
432 #should be refined with using match_ActivePlayers instead of match_BBPostingPlayers
433 #as a leaving and rejoining player could be active without posting a BB (sample HH needed)
434 unseatedActivePlayers
= list(set(match_BBPostingPlayers
) - set(seatedPlayers
))
436 if unseatedActivePlayers
:
437 for player
in unseatedActivePlayers
:
438 previousBBPoster
= match_BBPostingPlayers
[match_BBPostingPlayers
.index(player
)-1]
439 previousBBPosterSeat
= dict([(f
[1], f
[0]) for f
in hand
.players
])[previousBBPoster
]
440 occupiedSeats
= list([(f
[0]) for f
in hand
.players
])
442 newPlayerSeat
= findFirstEmptySeat(previousBBPosterSeat
)
443 hand
.addPlayer(newPlayerSeat
,player
,self
.clearMoneyString(maxKnownStack
))
445 def markStreets(self
, hand
):
446 if hand
.gametype
['base'] in ("hold"):
447 m
= re
.search(r
"\*{2} Dealing down cards \*{2}"
449 r
"(?:\*{2} Dealing Flop \*{2} (?P<FLOP>\[ \S\S, \S\S, \S\S \].+?))?"
450 r
"(?:\*{2} Dealing Turn \*{2} (?P<TURN>\[ \S\S \].+?))?"
451 r
"(?:\*{2} Dealing River \*{2} (?P<RIVER>\[ \S\S \].+?))?$"
452 , hand
.handText
,re
.DOTALL
)
453 elif hand
.gametype
['base'] in ("stud"):
454 m
= re
.search(r
"(?P<ANTES>.+)"
455 r
"(?:\*{2} Dealing \*{2}(?P<THIRD>.+))?"
456 r
"(?:\*{2} Dealing Fourth street \*{2}(?P<FOURTH>.+))?"
457 r
"(?:\*{2} Dealing Fifth street \*{2}(?P<FIFTH>.+))?"
458 r
"(?:\*{2} Dealing Sixth street \*{2}(?P<SIXTH>.+))?"
459 r
"(?:\*{2} Dealing Seventh street \*{2}(?P<SEVENTH>.+))?"
460 , hand
.handText
,re
.DOTALL
)
464 def readCommunityCards(self
, hand
, street
):
465 if street
in ('FLOP','TURN','RIVER'):
466 m
= self
.re_Board
.search(hand
.streets
[street
])
467 hand
.setCommunityCards(street
, renderCards(m
.group('CARDS')))
469 def readAntes(self
, hand
):
470 log
.debug("reading antes")
471 m
= self
.re_Antes
.finditer(hand
.handText
)
473 hand
.addAnte(player
.group('PNAME'), player
.group('ANTE'))
475 def readBlinds(self
, hand
):
476 noSmallBlind
= bool(self
.re_NoSmallBlind
.search(hand
.handText
))
477 if hand
.gametype
['type'] == 'ring':
479 assert noSmallBlind
==False
481 for m
in self
.re_PostSB
.finditer(hand
.handText
):
483 hand
.addBlind(m
.group('PNAME'), 'small blind', m
.group('SB'))
486 # Post dead blinds as ante
487 hand
.addBlind(m
.group('PNAME'), 'secondsb', m
.group('SB'))
488 except: # no small blind
489 hand
.addBlind(None, None, None)
491 for a
in self
.re_PostBB
.finditer(hand
.handText
):
492 hand
.addBlind(a
.group('PNAME'), 'big blind', a
.group('BB'))
494 deadFilter
= lambda s
: s
.replace(',', '.')
495 for a
in self
.re_PostDead
.finditer(hand
.handText
):
496 hand
.addBlind(a
.group('PNAME'), 'both', deadFilter(a
.group('BBNDEAD')))
498 # party doesn't track blinds for tournaments
499 # so there're some cra^Wcaclulations
500 if hand
.buttonpos
== 0:
501 self
.readButton(hand
)
502 # NOTE: code below depends on Hand's implementation
503 # playersMap - dict {seat: (pname,stack)}
504 playersMap
= dict([(f
[0], f
[1:3]) for f
in hand
.players
])
505 maxSeat
= max(playersMap
)
507 def findFirstNonEmptySeat(startSeat
):
508 while startSeat
not in playersMap
:
509 if startSeat
>= maxSeat
:
513 smartMin
= lambda A
,B
: A
if float(A
) <= float(B
) else B
516 hand
.addBlind(None, None, None)
517 smallBlindSeat
= int(hand
.buttonpos
)
519 smallBlindSeat
= findFirstNonEmptySeat(int(hand
.buttonpos
) + 1)
520 blind
= smartMin(hand
.sb
, playersMap
[smallBlindSeat
][1])
521 hand
.addBlind(playersMap
[smallBlindSeat
][0], 'small blind', blind
)
523 bigBlindSeat
= findFirstNonEmptySeat(smallBlindSeat
+ 1)
524 blind
= smartMin(hand
.bb
, playersMap
[bigBlindSeat
][1])
525 hand
.addBlind(playersMap
[bigBlindSeat
][0], 'big blind', blind
)
527 def readBringIn(self
, hand
):
529 #m = self.re_BringIn.search(hand.handText,re.DOTALL)
531 # #~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
532 # hand.addBringIn(m.group('PNAME'), m.group('BRINGIN'))
534 def readHeroCards(self
, hand
):
535 # we need to grab hero's cards
536 for street
in ('PREFLOP',):
537 if street
in hand
.streets
.keys():
538 m
= self
.re_HeroCards
.finditer(hand
.streets
[street
])
540 hand
.hero
= found
.group('PNAME')
541 newcards
= renderCards(found
.group('NEWCARDS'))
542 hand
.addHoleCards(street
, hand
.hero
, closed
=newcards
, shown
=False, mucked
=False, dealt
=True)
544 def readAction(self
, hand
, street
):
545 m
= self
.re_Action
.finditer(hand
.streets
[street
])
547 acts
= action
.groupdict()
548 #print "DEBUG: acts: %s" % acts
549 playerName
= action
.group('PNAME')
550 amount
= self
.clearMoneyString(action
.group('BET')) if action
.group('BET') else None
551 actionType
= action
.group('ATYPE')
553 if actionType
== 'raises':
554 if street
== 'PREFLOP' and \
555 playerName
in [item
[0] for item
in hand
.actions
['BLINDSANTES'] if item
[2]!='ante']:
556 # preflop raise from blind
557 hand
.addCallandRaise( street
, playerName
, amount
)
559 hand
.addCallandRaise( street
, playerName
, amount
)
560 elif actionType
== 'calls':
561 hand
.addCall( street
, playerName
, amount
)
562 elif actionType
== 'bets':
563 hand
.addBet( street
, playerName
, amount
)
564 elif actionType
== 'folds':
565 hand
.addFold( street
, playerName
)
566 elif actionType
== 'checks':
567 hand
.addCheck( street
, playerName
)
568 elif actionType
== 'is all-In':
569 hand
.addAllIn(street
, playerName
, amount
)
571 raise FpdbParseError((_("Unimplemented %s: '%s' '%s'") + " hid:%s") % ("readAction", playerName
, actionType
, hand
.handid
))
573 def readShowdownActions(self
, hand
):
574 # all action in readShownCards
577 def readCollectPot(self
,hand
):
578 for m
in self
.re_CollectPot
.finditer(hand
.handText
):
579 hand
.addCollectPot(player
=m
.group('PNAME'),pot
=self
.clearMoneyString(m
.group('POT')))
581 def readShownCards(self
,hand
):
582 for m
in self
.re_ShownCards
.finditer(hand
.handText
):
583 if m
.group('CARDS') is not None:
584 cards
= renderCards(m
.group('CARDS'))
586 mucked
= m
.group('SHOWED') != "show"
588 hand
.addShownCards(cards
=cards
, player
=m
.group('PNAME'), shown
=True, mucked
=mucked
)
591 def getTableTitleRe(type, table_name
=None, tournament
= None, table_number
=None):
592 "Returns string to search in windows titles"
594 TableName
= table_name
.split(" ")
595 print 'party', 'getTableTitleRe', "%s.+Table\s#%s" % (TableName
[0], table_number
)
596 if len(TableName
[1]) > 6:
597 return "#%s" % (table_number
)
599 return "%s.+Table\s#%s" % (TableName
[0], table_number
)
603 def renderCards(string
):
604 "Splits strings like ' Js, 4d '"
605 cards
= string
.strip().split(' ')
606 return filter(len, map(lambda x
: x
.strip(' ,'), cards
))