2 # -*- coding: utf-8 -*-
4 # Copyright 2008-2011, Carl Gherardi
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()
26 from HandHistoryConverter
import *
30 class Win2day(HandHistoryConverter
):
38 re_GameInfo
= re
.compile("""<HISTORY\sID="(?P<HID>[0-9]+)"\sSESSION="session[0-9]+\.xml"\s
39 TABLE="(?P<TABLE>[-\sa-zA-Z0-9\xc0-\xfc/.]+)"\s
40 GAME="(?P<GAME>[_A-Z]+)"\sGAMETYPE="[_a-zA-Z]+"\sGAMEKIND="[_a-zA-Z]+"\s
41 TABLECURRENCY="(?P<CURRENCY>[A-Z]+)"\s
42 LIMIT="(?P<LIMIT>NL|PL)"\s
43 STAKES="(?P<SB>[.0-9]+)/(?P<BB>[.0-9]+)"\s
44 DATE="(?P<DATETIME>[0-9]+)"\s
45 (TABLETOURNEYID=""\s)?
46 WIN="[.0-9]+"\sLOSS="[.0-9]+"
47 """, re
.MULTILINE| re
.VERBOSE
)
48 re_SplitHands
= re
.compile('</HISTORY>')
49 re_HandInfo
= re
.compile("^Table \'(?P<TABLE>[- a-zA-Z]+)\'(?P<TABLEATTRIBUTES>.+?$)?", re
.MULTILINE
)
50 re_Button
= re
.compile('<ACTION TYPE="HAND_DEAL" PLAYER="(?P<BUTTON>[^"]+)">\n<CARD LINK="[0-9b]+"></CARD>\n<CARD LINK="[0-9b]+"></CARD></ACTION>\n<ACTION TYPE="ACTION_', re
.MULTILINE
)
51 #<PLAYER NAME="prato" SEAT="1" AMOUNT="61.29"></PLAYER>
52 re_PlayerInfo
= re
.compile('^<PLAYER NAME="(?P<PNAME>.*)" SEAT="(?P<SEAT>[0-9]+)" AMOUNT="(?P<CASH>[.0-9]+)"></PLAYER>', re
.MULTILINE
)
53 re_Card
= re
.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD>', re
.MULTILINE
)
54 re_BoardLast
= re
.compile('^<CARD LINK="(?P<CARD>[0-9]+)"></CARD></ACTION>', re
.MULTILINE
)
57 def compilePlayerRegexs(self
, hand
):
58 players
= set([player
[1] for player
in hand
.players
])
59 if not players
<= self
.compiledPlayers
: # x <= y means 'x is subset of y'
60 # we need to recompile the player regexs.
61 self
.compiledPlayers
= players
62 player_re
= "(?P<PNAME>" + "|".join(map(re
.escape
, players
)) + ")"
63 logging
.debug("player_re: " + player_re
)
64 #<ACTION TYPE="HAND_BLINDS" PLAYER="prato" KIND="HAND_SB" VALUE="0.25"></ACTION>
66 self
.re_PostSB
= re
.compile(r
'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_SB" VALUE="(?P<SB>[.0-9]+)"></ACTION>' % player_re
, re
.MULTILINE
)
67 self
.re_PostBB
= re
.compile(r
'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_BB" VALUE="(?P<BB>[.0-9]+)"></ACTION>' % player_re
, re
.MULTILINE
)
68 self
.re_Antes
= re
.compile(r
"^%s: posts the ante \$?(?P<ANTE>[.0-9]+)" % player_re
, re
.MULTILINE
)
69 self
.re_BringIn
= re
.compile(r
"^%s: brings[- ]in( low|) for \$?(?P<BRINGIN>[.0-9]+)" % player_re
, re
.MULTILINE
)
70 self
.re_PostBoth
= re
.compile(r
'^<ACTION TYPE="HAND_BLINDS" PLAYER="%s" KIND="HAND_AB" VALUE="(?P<SBBB>[.0-9]+)"></ACTION>' % player_re
, re
.MULTILINE
)
72 #r'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n<CARD LINK="(?P<CARD1>[0-9]+)"></CARD>\n<CARD LINK="(?P<CARD2>[0-9]+)"></CARD></ACTION>'
73 self
.re_HeroCards
= re
.compile(r
'<ACTION TYPE="HAND_DEAL" PLAYER="%s">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</ACTION>' % player_re
, re
.MULTILINE
)
75 #'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>'
76 self
.re_Action
= re
.compile(r
'^<ACTION TYPE="(?P<ATYPE>[_A-Z]+)" PLAYER="%s"( VALUE="(?P<BET>[.0-9]+)")?></ACTION>' % player_re
, re
.MULTILINE
)
78 self
.re_ShowdownAction
= re
.compile(r
'<RESULT PLAYER="%s" WIN="[.0-9]+" HAND="(?P<HAND>\(\$STR_G_FOLD\)|[\$\(\)_ A-Z]+)">\n(?P<CARDS><CARD LINK="[0-9]+"></CARD>\n<CARD LINK="[0-9]+"></CARD>)</RESULT>' % player_re
, re
.MULTILINE
)
79 #<RESULT PLAYER="wig0r" WIN="4.10" HAND="$(STR_G_WIN_TWOPAIR) $(STR_G_CARDS_TENS) $(STR_G_ANDTEXT) $(STR_G_CARDS_EIGHTS)">
81 self
.re_CollectPot
= re
.compile(r
'<RESULT PLAYER="%s" WIN="(?P<POT>[.\d]+)" HAND=".+">' % player_re
, re
.MULTILINE
)
82 self
.re_sitsOut
= re
.compile("^%s sits out" % player_re
, re
.MULTILINE
)
83 self
.re_ShownCards
= re
.compile("^Seat (?P<SEAT>[0-9]+): %s \(.*\) showed \[(?P<CARDS>.*)\].*" % player_re
, re
.MULTILINE
)
86 def readSupportedGames(self
):
87 return [["ring", "hold", "nl"],
88 ["ring", "hold", "pl"],
89 ["ring", "hold", "fl"],
90 ["ring", "stud", "fl"],
91 ["ring", "draw", "fl"],
92 ["ring", "omaha", "pl"]
95 def determineGameType(self
, handText
):
96 info
= {'type':'ring'}
98 m
= self
.re_GameInfo
.search(handText
)
100 tmp
= handText
[0:1000]
101 log
.error(_("Unable to recognise gametype from: '%s'") % tmp
)
102 log
.error("determineGameType: " + _("Raising FpdbParseError"))
103 raise FpdbParseError(_("Unable to recognise gametype from: '%s'") % tmp
)
106 #print "DEBUG: mg: %s" % mg
108 # translations from captured groups to our info strings
109 #limits = { 'NL':'nl', 'PL':'pl', 'Limit':'fl' }
110 limits
= { 'NL':'nl', 'PL':'pl'}
111 games
= { # base, category
112 "GAME_THM" : ('hold','holdem'),
113 "GAME_OMA" : ('hold','omahahi'),
115 #'Omaha Hi/Lo' : ('hold','omahahilo'),
116 # 'Razz' : ('stud','razz'),
117 #'7 Card Stud' : ('stud','studhi'),
118 # 'Badugi' : ('draw','badugi')
121 info
['limitType'] = limits
[mg
['LIMIT']]
123 (info
['base'], info
['category']) = games
[mg
['GAME']]
125 info
['sb'] = mg
['SB']
127 info
['bb'] = mg
['BB']
129 info
['currency'] = mg
['CURRENCY']
130 # NB: SB, BB must be interpreted as blinds or bets depending on limit type.
135 def readHandInfo(self
, hand
):
137 m
= self
.re_HandInfo
.search(hand
.handText
,re
.DOTALL
)
139 info
.update(m
.groupdict())
140 # TODO: Be less lazy and parse maxseats from the HandInfo regex
141 if m
.group('TABLEATTRIBUTES'):
142 m2
= re
.search("\s*(\d+)-max", m
.group('TABLEATTRIBUTES'))
143 hand
.maxseats
= int(m2
.group(1))
144 m
= self
.re_GameInfo
.search(hand
.handText
)
145 if m
: info
.update(m
.groupdict())
146 m
= self
.re_Button
.search(hand
.handText
)
147 if m
: info
.update(m
.groupdict())
148 # TODO : I rather like the idea of just having this dict as hand.info
149 logging
.debug("readHandInfo: %s" % info
)
151 if key
== 'DATETIME':
152 # Win2day uses UTC timestamp
153 hand
.startTime
= datetime
.datetime
.fromtimestamp(int(info
[key
]))
155 hand
.handid
= info
[key
]
157 hand
.tablename
= info
[key
]
159 hand
.buttonpos
= info
[key
]
161 def readButton(self
, hand
):
162 m
= self
.re_Button
.search(hand
.handText
)
164 for player
in hand
.players
:
165 if player
[1] == m
.group('BUTTON'):
166 hand
.buttonpos
= player
[0]
169 logging
.info(_('readButton: not found'))
171 def readPlayerStacks(self
, hand
):
172 logging
.debug("readPlayerStacks")
173 m
= self
.re_PlayerInfo
.finditer(hand
.handText
)
176 hand
.addPlayer(int(a
.group('SEAT')), a
.group('PNAME'), a
.group('CASH'))
178 def markStreets(self
, hand
):
179 # PREFLOP = ** Dealing down cards **
180 # This re fails if, say, river is missing; then we don't get the ** that starts the river.
181 if hand
.gametype
['base'] in ("hold"):
182 #m = re.search(r"\*\*\* HOLE CARDS \*\*\*(?P<PREFLOP>.+(?=\*\*\* FLOP \*\*\*)|.+)"
183 # r"(\*\*\* FLOP \*\*\*(?P<FLOP> \[\S\S \S\S \S\S\].+(?=\*\*\* TURN \*\*\*)|.+))?"
184 # r"(\*\*\* TURN \*\*\* \[\S\S \S\S \S\S] (?P<TURN>\[\S\S\].+(?=\*\*\* RIVER \*\*\*)|.+))?"
185 # r"(\*\*\* RIVER \*\*\* \[\S\S \S\S \S\S \S\S] (?P<RIVER>\[\S\S\].+))?", hand.handText,re.DOTALL)
187 m
= re
.search('<ACTION TYPE="HAND_BLINDS" PLAYER=".+" KIND="HAND_BB" VALUE="[.0-9]+"></ACTION>(?P<PREFLOP>.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP")|.+)'
188 '((?P<FLOP><ACTION TYPE="HAND_BOARD" VALUE="BOARD_FLOP" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN")|.+))?'
189 '((?P<TURN><ACTION TYPE="HAND_BOARD" VALUE="BOARD_TURN" POT="[.0-9]+">.+(?=<ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER")|.+))?'
190 '((?P<RIVER><ACTION TYPE="HAND_BOARD" VALUE="BOARD_RIVER" POT="[.0-9]+">.+(?=<SHOWDOWN NAME="HAND_SHOWDOWN")|.+))?', hand
.handText
,re
.DOTALL
)
194 def readCommunityCards(self
, hand
, street
): # street has been matched by markStreets, so exists in this hand
195 if street
in ('FLOP','TURN','RIVER'): # a list of streets which get dealt community cards (i.e. all but PREFLOP)
196 #print "DEBUG readCommunityCards:", street, hand.streets.group(street)
200 m
= self
.re_Card
.findall(hand
.streets
[street
])
202 boardCards
.append(self
.convertWin2dayCards(card
))
204 m
= self
.re_BoardLast
.search(hand
.streets
[street
])
205 boardCards
.append(self
.convertWin2dayCards(m
.group('CARD')))
207 hand
.setCommunityCards(street
, boardCards
)
209 def readAntes(self
, hand
):
210 logging
.debug(_("reading antes"))
211 m
= self
.re_Antes
.finditer(hand
.handText
)
213 #~ logging.debug("hand.addAnte(%s,%s)" %(player.group('PNAME'), player.group('ANTE')))
214 hand
.addAnte(player
.group('PNAME'), player
.group('ANTE'))
216 def readBringIn(self
, hand
):
217 m
= self
.re_BringIn
.search(hand
.handText
,re
.DOTALL
)
219 #~ logging.debug("readBringIn: %s for %s" %(m.group('PNAME'), m.group('BRINGIN')))
220 hand
.addBringIn(m
.group('PNAME'), m
.group('BRINGIN'))
222 def readBlinds(self
, hand
):
224 m
= self
.re_PostSB
.search(hand
.handText
)
225 hand
.addBlind(m
.group('PNAME'), 'small blind', m
.group('SB'))
226 except: # no small blind
227 hand
.addBlind(None, None, None)
228 for a
in self
.re_PostBB
.finditer(hand
.handText
):
229 hand
.addBlind(a
.group('PNAME'), 'big blind', a
.group('BB'))
230 for a
in self
.re_PostBoth
.finditer(hand
.handText
):
231 hand
.addBlind(a
.group('PNAME'), 'both', a
.group('SBBB'))
233 def readHeroCards(self
, hand
):
234 # streets PREFLOP, PREDRAW, and THIRD are special cases beacause
235 # we need to grab hero's cards
236 m
= self
.re_HeroCards
.finditer(hand
.streets
['PREFLOP'])
239 hand
.hero
= found
.group('PNAME')
240 for card
in self
.re_Card
.finditer(found
.group('CARDS')):
241 #print self.convertWin2dayCards(card.group('CARD'))
242 newcards
.append(self
.convertWin2dayCards(card
.group('CARD')))
244 #hand.addHoleCards(holeCards, m.group('PNAME'))
245 hand
.addHoleCards('PREFLOP', hand
.hero
, closed
=newcards
, shown
=False, mucked
=False, dealt
=True)
247 def convertWin2dayCards(self
, card
):
250 cardconvert
= { 1:'A',
255 realNumber
= card
% 13 + 1
256 if(realNumber
in cardconvert
):
257 retCard
+= cardconvert
[realNumber
]
259 retCard
+= str(realNumber
)
272 def readDrawCards(self
, hand
, street
):
273 logging
.debug("readDrawCards")
274 m
= self
.re_HeroCards
.finditer(hand
.streets
[street
])
276 hand
.involved
= False
279 hand
.hero
= player
.group('PNAME') # Only really need to do this once
280 newcards
= player
.group('NEWCARDS')
281 oldcards
= player
.group('OLDCARDS')
285 newcards
= newcards
.split(' ')
289 oldcards
= oldcards
.split(' ')
290 hand
.addDrawHoleCards(newcards
, oldcards
, player
.group('PNAME'), street
)
293 def readStudPlayerCards(self
, hand
, street
):
294 # See comments of reference implementation in FullTiltToFpdb.py
295 # logging.debug("readStudPlayerCards")
296 m
= self
.re_HeroCards
.finditer(hand
.streets
[street
])
298 #~ logging.debug(player.groupdict())
299 (pname
, oldcards
, newcards
) = (player
.group('PNAME'), player
.group('OLDCARDS'), player
.group('NEWCARDS'))
301 oldcards
= [c
.strip() for c
in oldcards
.split(' ')]
303 newcards
= [c
.strip() for c
in newcards
.split(' ')]
306 elif street
=='THIRD':
307 # we'll have observed hero holecards in CARDS and thirdstreet open cards in 'NEWCARDS'
310 hand
.addPlayerCards(player
= player
.group('PNAME'), street
= street
, closed
= oldcards
, open = newcards
)
311 elif street
in ('FOURTH', 'FIFTH', 'SIXTH'):
321 hand
.addPlayerCards(player
= player
.group('PNAME'), street
= street
, open = newcards
)
322 # we may additionally want to check the earlier streets tally with what we have but lets trust it for now.
323 elif street
=='SEVENTH' and newcards
:
325 # others: not reported.
326 hand
.addPlayerCards(player
= player
.group('PNAME'), street
= street
, closed
= newcards
)
328 def readAction(self
, hand
, street
):
329 m
= self
.re_Action
.finditer(hand
.streets
[street
])
331 if action
.group('ATYPE') == 'ACTION_RAISE':
332 hand
.addRaiseBy( street
, action
.group('PNAME'), action
.group('BET') )
333 elif action
.group('ATYPE') == 'ACTION_CALL':
334 hand
.addCall( street
, action
.group('PNAME'), action
.group('BET') )
335 elif action
.group('ATYPE') == 'ACTION_ALLIN':
336 hand
.addRaiseBy( street
, action
.group('PNAME'), action
.group('BET') )
337 elif action
.group('ATYPE') == 'ACTION_BET':
338 hand
.addBet( street
, action
.group('PNAME'), action
.group('BET') )
339 elif action
.group('ATYPE') == 'ACTION_FOLD':
340 hand
.addFold( street
, action
.group('PNAME'))
341 elif action
.group('ATYPE') == 'ACTION_CHECK':
342 hand
.addCheck( street
, action
.group('PNAME'))
343 elif action
.group('ATYPE') == 'ACTION_DISCARD':
344 hand
.addDiscard(street
, action
.group('PNAME'), action
.group('NODISCARDED'), action
.group('DISCARDED'))
345 elif action
.group('ATYPE') == 'ACTION_STAND':
346 hand
.addStandsPat( street
, action
.group('PNAME'))
348 print (_("DEBUG:") + _("Unimplemented %s: '%s' '%s'") % ("readAction", action
.group('PNAME'), action
.group('ATYPE')))
351 def readShowdownActions(self
, hand
):
352 for shows
in self
.re_ShowdownAction
.finditer(hand
.handText
):
354 for card
in self
.re_Card
.finditer(shows
.group('CARDS')):
355 #print "DEBUG:", card, card.group('CARD'), self.convertWin2dayCards(card.group('CARD'))
356 showdownCards
.append(self
.convertWin2dayCards(card
.group('CARD')))
358 hand
.addShownCards(showdownCards
, shows
.group('PNAME'))
360 def readCollectPot(self
,hand
):
361 for m
in self
.re_CollectPot
.finditer(hand
.handText
):
362 potcoll
= Decimal(m
.group('POT'))
364 hand
.addCollectPot(player
=m
.group('PNAME'),pot
=potcoll
)
366 def readShownCards(self
,hand
):
367 for m
in self
.re_ShownCards
.finditer(hand
.handText
):
368 if m
.group('CARDS') is not None:
369 cards
= m
.group('CARDS')
370 cards
= cards
.split(' ')
371 hand
.addShownCards(cards
=cards
, player
=m
.group('PNAME'))