2 # -*- coding: utf-8 -*-
4 #Copyright 2008-2011 Carl Gherardi
5 #This program is free software: you can redistribute it and/or modify
6 #it under the terms of the GNU Affero General Public License as published by
7 #the Free Software Foundation, version 3 of the License.
9 #This program is distributed in the hope that it will be useful,
10 #but WITHOUT ANY WARRANTY; without even the implied warranty of
11 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 #GNU General Public License for more details.
14 #You should have received a copy of the GNU Affero General Public License
15 #along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #In the "official" distribution you can find the license in agpl-3.0.txt.
19 _
= L10n
.get_translation()
21 # TODO: get writehand() encoding correct
28 from decimal_wrapper
import Decimal
31 from copy
import deepcopy
32 from string
import upper
36 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
37 log
= logging
.getLogger("parser")
41 from Exceptions
import *
47 ###############################################################3
49 UPS
= {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'}
50 LCS
= {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
51 SYMBOL
= {'USD': '$', 'CAD': '$', 'EUR': u
'$', 'GBP': '$', 'SEK': '$', 'T$': '', 'play': ''}
52 MS
= {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
53 ACTION
= {'ante': 1, 'small blind': 2, 'secondsb': 3, 'big blind': 4, 'both': 5, 'calls': 6, 'raises': 7,
54 'bets': 8, 'stands pat': 9, 'folds': 10, 'checks': 11, 'discards': 12, 'bringin': 13, 'completes': 14}
57 def __init__(self
, config
, sitename
, gametype
, handText
, builtFrom
= "HHC"):
58 #log.debug( _("Hand.init(): handText is ") + str(handText) )
60 self
.saveActions
= self
.config
.get_import_parameters().get('saveActions')
61 self
.callHud
= self
.config
.get_import_parameters().get("callFpdbHud")
62 self
.cacheSessions
= self
.config
.get_import_parameters().get("cacheSessions")
63 self
.sitename
= sitename
64 self
.siteId
= self
.config
.get_site_id(sitename
)
65 self
.stats
= DerivedStats
.DerivedStats(self
)
66 self
.gametype
= gametype
68 self
.handText
= handText
71 self
.cancelled
= False
79 self
.counted_seats
= 0
82 self
.uncalledbets
= False
87 self
.tourneyTypeId
= None
89 self
.buyinCurrency
= None
90 self
.buyInChips
= None
91 self
.fee
= None # the Database code is looking for this one .. ?
102 self
.isMatrix
= False
103 self
.isShootout
= False
105 self
.addedCurrency
= None
106 self
.tourneyComment
= None
111 self
.tourneysPlayersIds
= {}
113 # Collections indexed by street names
117 self
.actions
= {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
118 self
.board
= {} # dict from street names to community cards
121 self
.showdownStrings
= {}
122 for street
in self
.allStreets
:
123 self
.streets
[street
] = "" # portions of the handText, filled by markStreets()
124 self
.actions
[street
] = []
125 for street
in self
.actionStreets
:
126 self
.bets
[street
] = {}
127 self
.lastBet
[street
] = 0
128 self
.board
[street
] = []
129 for street
in self
.holeStreets
:
130 self
.holecards
[street
] = {} # dict from player names to holecards
131 self
.discards
[street
] = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
132 # Collections indexed by player names
134 self
.collected
= [] #list of ?
135 self
.collectees
= {} # dict from player names to amounts collected (?)
139 self
.dealt
= set() # 'dealt to' line to be printed
140 self
.shown
= set() # cards were shown
141 self
.mucked
= set() # cards were mucked at showdown
143 # Things to do with money
146 self
.totalcollected
= None
148 # currency symbol for this hand
149 self
.sym
= self
.SYMBOL
[self
.gametype
['currency']] # save typing! delete this attr when done
150 self
.pot
.setSym(self
.sym
)
151 self
.is_duplicate
= False # i.e. don't update hudcache if true
154 vars = ( (_("BB"), self
.bb
),
156 (_("BUTTON POS"), self
.buttonpos
),
157 (_("HAND NO."), self
.handid
),
158 (_("SITE"), self
.sitename
),
159 (_("TABLE NAME"), self
.tablename
),
160 (_("HERO"), self
.hero
),
161 (_("MAX SEATS"), self
.maxseats
),
162 (_("LEVEL"), self
.level
),
163 (_("MIXED"), self
.mixed
),
164 (_("LAST BET"), self
.lastBet
),
165 (_("ACTION STREETS"), self
.actionStreets
),
166 (_("STREETS"), self
.streets
),
167 (_("ALL STREETS"), self
.allStreets
),
168 (_("COMMUNITY STREETS"), self
.communityStreets
),
169 (_("HOLE STREETS"), self
.holeStreets
),
170 (_("COUNTED SEATS"), self
.counted_seats
),
171 (_("DEALT"), self
.dealt
),
172 (_("SHOWN"), self
.shown
),
173 (_("MUCKED"), self
.mucked
),
174 (_("TOTAL POT"), self
.totalpot
),
175 (_("TOTAL COLLECTED"), self
.totalcollected
),
176 (_("RAKE"), self
.rake
),
177 (_("START TIME"), self
.startTime
),
178 (_("TOURNAMENT NO"), self
.tourNo
),
179 (_("TOURNEY ID"), self
.tourneyId
),
180 (_("TOURNEY TYPE ID"), self
.tourneyTypeId
),
181 (_("BUYIN"), self
.buyin
),
182 (_("BUYIN CURRENCY"), self
.buyinCurrency
),
183 (_("BUYIN CHIPS"), self
.buyInChips
),
184 (_("FEE"), self
.fee
),
185 (_("IS REBUY"), self
.isRebuy
),
186 (_("IS ADDON"), self
.isAddOn
),
187 (_("IS KO"), self
.isKO
),
188 (_("KO BOUNTY"), self
.koBounty
),
189 (_("IS MATRIX"), self
.isMatrix
),
190 (_("IS SHOOTOUT"), self
.isShootout
),
191 (_("TOURNEY COMMENT"), self
.tourneyComment
),
194 structs
= ( (_("PLAYERS"), self
.players
),
195 (_("STACKS"), self
.stacks
),
196 (_("POSTED"), self
.posted
),
197 (_("POT"), self
.pot
),
198 (_("SEATING"), self
.seating
),
199 (_("GAMETYPE"), self
.gametype
),
200 (_("ACTION"), self
.actions
),
201 (_("COLLECTEES"), self
.collectees
),
202 (_("BETS"), self
.bets
),
203 (_("BOARD"), self
.board
),
204 (_("DISCARDS"), self
.discards
),
205 (_("HOLECARDS"), self
.holecards
),
206 (_("TOURNEYS PLAYER IDS"), self
.tourneysPlayersIds
),
209 for (name
, var
) in vars:
210 str = str + "\n%s = " % name
+ pprint
.pformat(var
)
212 for (name
, struct
) in structs
:
213 str = str + "\n%s =\n" % name
+ pprint
.pformat(struct
, 4)
216 def addHoleCards(self
, street
, player
, open=[], closed
=[], shown
=False, mucked
=False, dealt
=False):
217 """ Assigns observed holecards to a player.
218 cards list of card bigrams e.g. ['2h','Jc']
219 player (string) name of player
220 shown whether they were revealed at showdown
221 mucked whether they were mucked at showdown
222 dealt whether they were seen in a 'dealt to' line """
223 # log.debug("addHoleCards %s %s" % (open + closed, player))
224 self
.checkPlayerExists(player
, 'addHoleCards')
225 if dealt
: self
.dealt
.add(player
)
226 if shown
: self
.shown
.add(player
)
227 if mucked
: self
.mucked
.add(player
)
229 for i
in range(len(closed
)):
230 if closed
[i
] in ('', 'Xx', 'Null', 'null'):
234 self
.holecards
[street
][player
] = [open, closed
]
236 log
.error(_("Hand.addHoleCards: '%s': Major failure while adding holecards: '%s'") % (self
.handid
, e
))
239 def prepInsert(self
, db
, printtest
= False):
241 # Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables
242 # These functions are intended for prep insert eventually
244 self
.gametype
['maxSeats'] = self
.maxseats
#TODO: move up to individual parsers
245 self
.dbid_pids
= db
.getSqlPlayerIDs([p
[1] for p
in self
.players
], self
.siteId
)
246 self
.dbid_gt
= db
.getSqlGameTypeId(self
.siteId
, self
.gametype
, printdata
= printtest
)
250 if self
.gametype
['category'] in ['studhilo', 'omahahilo']:
252 elif self
.gametype
['category'] in ['razz','27_3draw','badugi', '27_1draw']:
255 self
.gametyperow
= (self
.siteId
, self
.gametype
['currency'], self
.gametype
['type'], self
.gametype
['base'],
256 self
.gametype
['category'], self
.gametype
['limitType'], hilo
, self
.gametype
['mix'],
257 int(Decimal(self
.gametype
['sb'])*100), int(Decimal(self
.gametype
['bb'])*100),
258 int(Decimal(self
.gametype
['bb'])*100), int(Decimal(self
.gametype
['bb'])*200),
259 int(self
.gametype
['maxSeats']), int(self
.gametype
['ante']*100))
260 # Note: the above data is calculated in db.getGameTypeId
261 # Only being calculated above so we can grab the testdata
262 if self
.tourNo
!=None:
263 self
.tourneyTypeId
= db
.getSqlTourneyTypeIDs(self
)
264 self
.tourneyId
= db
.getSqlTourneyIDs(self
)
265 self
.tourneysPlayersIds
= db
.getSqlTourneysPlayersIDs(self
)
267 def assembleHand(self
):
268 self
.stats
.getStats(self
)
269 self
.hands
= self
.stats
.getHands()
270 self
.handsplayers
= self
.stats
.getHandsPlayers()
271 self
.handsstove
= self
.stats
.getHandsStove()
273 def getHandId(self
, db
, id):
274 if db
.isDuplicate(self
.dbid_gt
, self
.hands
['siteHandNo']):
275 #log.info(_("Hand.insert(): hid #: %s is a duplicate") % hh['siteHandNo'])
276 self
.is_duplicate
= True # i.e. don't update hudcache
278 raise FpdbHandDuplicate(self
.hands
['siteHandNo'])
281 self
.hands
['id'] = self
.dbid_hands
282 next
= id + db
.hand_inc
285 def insertHands(self
, db
, fileId
, doinsert
= False, printtest
= False):
286 """ Function to insert Hand into database
287 Should not commit, and do minimal selects. Callers may want to cache commits
288 db: a connected Database object"""
289 self
.hands
['gametypeId'] = self
.dbid_gt
290 self
.hands
['seats'] = len(self
.dbid_pids
)
291 self
.hands
['fileId'] = fileId
292 db
.storeHand(self
.hands
, doinsert
, printtest
)
293 db
.storeBoards(self
.dbid_hands
, self
.hands
['boards'], doinsert
)
295 def insertHandsPlayers(self
, db
, doinsert
= False, printtest
= False):
296 """ Function to inserts HandsPlayers into database"""
297 db
.storeHandsPlayers(self
.dbid_hands
, self
.dbid_pids
, self
.handsplayers
, doinsert
, printtest
)
299 def insertHandsActions(self
, db
, doinsert
= False, printtest
= False):
300 """ Function to inserts HandsActions into database"""
301 handsactions
= self
.stats
.getHandsActions()
302 db
.storeHandsActions(self
.dbid_hands
, self
.dbid_pids
, handsactions
, doinsert
, printtest
)
304 def insertHandsStove(self
, db
, doinsert
= False):
305 """ Function to inserts HandsActions into database"""
307 for hs
in self
.handsstove
: hs
[0] = self
.dbid_hands
308 db
.storeHandsStove(self
.handsstove
, doinsert
)
310 def updateHudCache(self
, db
, doinsert
= False):
311 """ Function to update the HudCache"""
313 db
.storeHudCache(self
.dbid_gt
, self
.dbid_pids
, self
.startTime
, self
.handsplayers
, doinsert
)
315 def updateSessionsCache(self
, db
, tz
, doinsert
= False):
316 """ Function to update the SessionsCache"""
317 if self
.cacheSessions
:
319 if self
.hero
in self
.dbid_pids
:
320 heroes
= [self
.dbid_pids
[self
.hero
]]
322 db
.storeSessionsCache(self
.dbid_hands
, self
.dbid_pids
, self
.startTime
, heroes
, doinsert
)
323 db
.storeGamesCache(self
.dbid_hands
, self
.dbid_pids
, self
.startTime
, self
.dbid_gt
, self
.gametype
, self
.handsplayers
, tz
, heroes
, doinsert
)
324 db
.updateTourneysPlayersSessions(self
.dbid_pids
, self
.tourneyId
, self
.startTime
, self
.handsplayers
, heroes
, doinsert
)
326 def updateCardsCache(self
, db
, doinsert
= False):
327 """ Function to update the HandsCache"""
329 if self
.hero
in self
.dbid_pids
:
330 heroes
= [self
.dbid_pids
[self
.hero
]]
331 db
.storeCardsCache(self
.gametype
, self
.dbid_pids
, heroes
, self
.handsplayers
, doinsert
)
333 def updatePositionsCache(self
, db
, doinsert
= False):
334 """ Function to update the PositionsCache"""
336 if self
.hero
in self
.dbid_pids
:
337 heroes
= [self
.dbid_pids
[self
.hero
]]
338 db
.storePositionsCache(self
.gametype
, self
.dbid_pids
, heroes
, self
.handsplayers
, doinsert
)
340 def select(self
, db
, handId
):
341 """ Function to create Hand object from database """
343 q
= db
.sql
.query
['playerHand']
344 q
= q
.replace('%s', db
.sql
.query
['placeholder'])
347 c
.execute(q
, (handId
,))
348 # See NOTE: below on what this does.
350 # Discripter must be set to lowercase as postgres returns all descriptors lower case and SQLight returns them as they are
351 res
= [dict(line
) for line
in [zip([ column
[0].lower() for column
in c
.description
], row
) for row
in c
.fetchall()]]
353 #print "DEBUG: addPlayer(%s, %s, %s)" %(seat,name,str(chips))
354 self
.addPlayer(row
['seatno'],row
['name'],str(row
['chips']), str(row
['position']))
356 cardlist
.append(Card
.valueSuitFromCard(row
['card1']))
357 cardlist
.append(Card
.valueSuitFromCard(row
['card2']))
358 cardlist
.append(Card
.valueSuitFromCard(row
['card3']))
359 cardlist
.append(Card
.valueSuitFromCard(row
['card4']))
360 cardlist
.append(Card
.valueSuitFromCard(row
['card5']))
361 cardlist
.append(Card
.valueSuitFromCard(row
['card6']))
362 cardlist
.append(Card
.valueSuitFromCard(row
['card7']))
363 cardlist
.append(Card
.valueSuitFromCard(row
['card8']))
364 cardlist
.append(Card
.valueSuitFromCard(row
['card9']))
365 cardlist
.append(Card
.valueSuitFromCard(row
['card10']))
366 cardlist
.append(Card
.valueSuitFromCard(row
['card11']))
367 cardlist
.append(Card
.valueSuitFromCard(row
['card12']))
368 cardlist
.append(Card
.valueSuitFromCard(row
['card13']))
369 cardlist
.append(Card
.valueSuitFromCard(row
['card14']))
370 cardlist
.append(Card
.valueSuitFromCard(row
['card15']))
371 cardlist
.append(Card
.valueSuitFromCard(row
['card16']))
372 cardlist
.append(Card
.valueSuitFromCard(row
['card17']))
373 cardlist
.append(Card
.valueSuitFromCard(row
['card18']))
374 cardlist
.append(Card
.valueSuitFromCard(row
['card19']))
375 cardlist
.append(Card
.valueSuitFromCard(row
['card20']))
376 if cardlist
[0] == '':
378 elif self
.gametype
['category'] == 'holdem':
379 self
.addHoleCards('PREFLOP', row
['name'], closed
=cardlist
[0:2], shown
=False, mucked
=False, dealt
=True)
380 elif self
.gametype
['category'] in ('omahahi', 'omahahilo'):
381 self
.addHoleCards('PREFLOP', row
['name'], closed
=cardlist
[0:4], shown
=False, mucked
=False, dealt
=True)
382 elif self
.gametype
['category'] in ('27_3draw', '27_1draw', 'fivedraw'):
383 self
.addHoleCards('DEAL', row
['name'], closed
=cardlist
[0:5], shown
=False, mucked
=False, dealt
=True)
384 elif self
.gametype
['category'] in ('razz', 'studhi', 'studhilo'):
385 #print "DEBUG: cardlist: %s" % cardlist
386 self
.addHoleCards('THIRD', row
['name'], open=[cardlist
[2]], closed
=cardlist
[0:2], shown
=False, dealt
=True)
387 self
.addHoleCards('FOURTH', row
['name'], open=[cardlist
[3]], closed
=[cardlist
[0:3]], shown
=False, mucked
=False)
388 self
.addHoleCards('FIFTH', row
['name'], open=[cardlist
[4]], closed
=cardlist
[0:4], shown
=False, mucked
=False)
389 self
.addHoleCards('SIXTH', row
['name'], open=[cardlist
[5]], closed
=cardlist
[0:5], shown
=False, mucked
=False)
390 self
.addHoleCards('SEVENTH', row
['name'], open=[cardlist
[6]], closed
=cardlist
[0:6], shown
=False, mucked
=False)
391 if row
['winnings'] > 0:
392 self
.addCollectPot(row
['name'], str(row
['winnings']))
393 if row
['position'] == 'B': #FIXME or Remove if not needed ... B is BigBlind ... 0 is the actual button position. Maybe unneeded field as position has been added to players list.
394 self
.buttonpos
= row
['seatno']
398 q
= db
.sql
.query
['singleHand']
399 q
= q
.replace('%s', db
.sql
.query
['placeholder'])
400 c
.execute(q
, (handId
,))
402 # NOTE: This relies on row_factory = sqlite3.Row (set in connect() params)
403 # Need to find MySQL and Postgres equivalents
404 # MySQL maybe: cursorclass=MySQLdb.cursors.DictCursor
407 # Using row_factory is global, and affects the rest of fpdb. The following 2 line achieves
410 # Discripter must be set to lowercase as supported dbs differ on what is returned.
411 res
= [dict(line
) for line
in [zip([ column
[0].lower() for column
in c
.description
], row
) for row
in c
.fetchall()]]
414 #res['tourneyId'] #res['seats'] #res['rush']
415 self
.tablename
= res
['tablename']
416 self
.handid
= res
['sitehandno']
417 # FIXME: Need to figure out why some times come out of the DB as %Y-%m-%d %H:%M:%S+00:00,
418 # and others as %Y-%m-%d %H:%M:%S
419 #print "DBEUG: res['startTime']: %s" % res['startTime']
421 #self.startTime currently unused in the replayer and commented out.
422 # Can't be done like this because not all dbs return the same type for starttime
424 # self.startTime = datetime.datetime.strptime(res['starttime'], "%Y-%m-%d %H:%M:%S+00:00")
426 # self.startTime = datetime.datetime.strptime(res['starttime'], "%Y-%m-%d %H:%M:%S")
428 cards
= map(Card
.valueSuitFromCard
, [res
['boardcard1'], res
['boardcard2'], res
['boardcard3'], res
['boardcard4'], res
['boardcard5']])
429 #print "DEBUG: res['boardcard1']: %s" % res['boardcard1']
430 #print "DEBUG: cards: %s" % cards
432 self
.setCommunityCards('FLOP', cards
[0:3])
434 self
.setCommunityCards('TURN', [cards
[3]])
436 self
.setCommunityCards('RIVER', [cards
[4]])
437 # playersVpi | playersAtStreet1 | playersAtStreet2 | playersAtStreet3 |
438 # playersAtStreet4 | playersAtShowdown | street0Raises | street1Raises |
439 # street2Raises | street3Raises | street4Raises | street1Pot | street2Pot |
440 # street3Pot | street4Pot | showdownPot | comment | commentTs | texture
443 q
= db
.sql
.query
['handActions']
444 q
= q
.replace('%s', db
.sql
.query
['placeholder'])
445 c
.execute(q
, (handId
,))
447 # Discripter must be set to lowercase as supported dbs differ on what is returned.
448 res
= [dict(line
) for line
in [zip([ column
[0].lower() for column
in c
.description
], row
) for row
in c
.fetchall()]]
451 street
= row
['street']
452 act
= row
['actionid']
453 # allin True/False if row['allIn'] == 0
454 bet
= str(row
['bet'])
455 street
= self
.allStreets
[int(street
)+1]
456 discards
= row
['cardsdiscarded']
457 #print "DEBUG: name: '%s' street: '%s' act: '%s' bet: '%s'" %(name, street, act, bet)
459 self
.addAnte(name
, bet
)
460 elif act
== 2: # Small Blind
461 self
.addBlind(name
, 'small blind', bet
)
462 elif act
== 3: # Second small blind
463 self
.addBlind(name
, 'secondsb', bet
)
464 elif act
== 4: # Big Blind
465 self
.addBlind(name
, 'big blind', bet
)
466 elif act
== 5: # Post both blinds
467 self
.addBlind(name
, 'both', bet
)
468 elif act
== 6: # Call
469 self
.addCall(street
, name
, bet
)
470 elif act
== 7: # Raise
471 self
.addRaiseBy(street
, name
, bet
)
473 self
.addBet(street
, name
, bet
)
474 elif act
== 9: # Stands pat
475 self
.addStandsPat(street
, name
, discards
)
476 elif act
== 10: # Fold
477 self
.addFold(street
, name
)
478 elif act
== 11: # Check
479 self
.addCheck(street
, name
)
480 elif act
== 12: # Discard
481 self
.addDiscard(street
, name
, row
['numdiscarded'], discards
)
482 elif act
== 13: # Bringin
483 self
.addBringIn(name
, bet
)
484 elif act
== 14: # Complete
485 self
.addComplete(street
, name
, bet
)
487 print "DEBUG: unknown action: '%s'" % act
490 self
.rake
= self
.totalpot
- self
.totalcollected
493 #hhc.readShowdownActions(self)
494 #hc.readShownCards(self)
497 def addPlayer(self
, seat
, name
, chips
, position
=None):
498 """ Adds a player to the hand, and initialises data structures indexed by player.
499 seat (int) indicating the seat
500 name (string) player name
501 chips (string) the chips the player has at the start of the hand (can be None)
502 position (string) indicating the position of the player (S,B, 0-7) (optional, not needed on Hand import from Handhistory).
503 If a player has None chips he won't be added."""
504 log
.debug("addPlayer: %s %s (%s)" % (seat
, name
, chips
))
505 if chips
is not None:
506 chips
= chips
.replace(u
',', u
'') #some sites have commas
507 self
.players
.append([seat
, name
, chips
, position
]) #removed most likely unused 0s from list and added position... former list: [seat, name, chips, 0, 0]
508 self
.stacks
[name
] = Decimal(chips
)
509 self
.pot
.addPlayer(name
)
510 for street
in self
.actionStreets
:
511 self
.bets
[street
][name
] = []
512 #self.holecards[name] = {} # dict from street names.
513 #self.discards[name] = {} # dict from street names.
516 def addStreets(self
, match
):
517 # go through m and initialise actions to empty list for each street.
519 self
.streets
.update(match
.groupdict())
520 log
.debug("markStreets:\n"+ str(self
.streets
))
522 tmp
= self
.handText
[0:100]
523 self
.cancelled
= True
524 raise FpdbHandPartial(_("Streets didn't match - Assuming hand '%s' was cancelled.") % (self
.handid
) + " " + _("First 100 characters: %s") % tmp
)
526 def checkPlayerExists(self
,player
,source
= None):
527 if player
not in [p
[1] for p
in self
.players
]:
528 log
.error(_("Hand.%s: '%s' unknown player: '%s'") % (source
, self
.handid
, player
))
531 def setCommunityCards(self
, street
, cards
):
532 log
.debug("setCommunityCards %s %s" %(street
, cards
))
533 self
.board
[street
] = [self
.card(c
) for c
in cards
]
534 # print "DEBUG: self.board: %s" % self.board
537 """upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
538 for k
,v
in self
.UPS
.items():
542 def addAllIn(self
, street
, player
, amount
):
543 """ For sites (currently only Merge & Microgaming) which record "all in" as a special action,
544 which can mean either "calls and is all in" or "raises all in"."""
545 self
.checkPlayerExists(player
, 'addAllIn')
546 amount
= amount
.replace(u
',', u
'') #some sites have commas
548 Bp
= self
.lastBet
[street
]
549 Bc
= sum(self
.bets
[street
][player
])
552 self
.addCall(street
, player
, amount
)
554 self
.addBet(street
, player
, amount
)
557 self
._addRaise
(street
, player
, C
, Rb
, Ai
)
559 def addAnte(self
, player
, ante
):
560 #log.debug("%s %s antes %s" % ('BLINDSANTES', player, ante))
561 if player
is not None:
562 ante
= ante
.replace(u
',', u
'') #some sites have commas
563 self
.checkPlayerExists(player
, 'addAnte')
565 self
.bets
['BLINDSANTES'][player
].append(ante
)
566 self
.stacks
[player
] -= ante
567 act
= (player
, 'ante', ante
, self
.stacks
[player
]==0)
568 self
.actions
['BLINDSANTES'].append(act
)
569 self
.pot
.addCommonMoney(player
, ante
)
570 if not 'ante' in self
.gametype
.keys() or self
.gametype
['ante'] == 0:
571 self
.gametype
['ante'] = ante
572 #I think the antes should be common money, don't have enough hand history to check
574 def addBlind(self
, player
, blindtype
, amount
):
575 # if player is None, it's a missing small blind.
576 # The situation we need to cover are:
577 # Player in small blind posts
578 # - this is a bet of 1 sb, as yet uncalled.
579 # Player in the big blind posts
580 # - this is a call of 1 sb and a raise to 1 bb
582 log
.debug("addBlind: %s posts %s, %s" % (player
, blindtype
, amount
))
583 if player
is not None:
584 self
.checkPlayerExists(player
, 'addBlind')
585 amount
= amount
.replace(u
',', u
'') #some sites have commas
586 amount
= Decimal(amount
)
587 self
.stacks
[player
] -= amount
588 act
= (player
, blindtype
, amount
, self
.stacks
[player
]==0)
589 self
.actions
['BLINDSANTES'].append(act
)
591 if blindtype
== 'both':
592 # work with the real amount. limit games are listed as $1, $2, where
593 # the SB 0.50 and the BB is $1, after the turn the minimum bet amount is $2....
594 amount
= Decimal(str(self
.bb
))
595 sb
= Decimal(str(self
.sb
))
596 self
.bets
['BLINDSANTES'][player
].append(sb
)
597 self
.pot
.addCommonMoney(player
, sb
)
599 if blindtype
== 'secondsb':
601 sb
= Decimal(str(self
.sb
))
602 self
.bets
['BLINDSANTES'][player
].append(sb
)
603 self
.pot
.addCommonMoney(player
, sb
)
607 if self
.gametype
['base'] == 'hold':
609 elif self
.gametype
['base'] == 'draw':
612 self
.bets
[street
][player
].append(amount
)
613 self
.pot
.addMoney(player
, amount
)
614 self
.lastBet
[street
] = amount
615 self
.posted
= self
.posted
+ [[player
,blindtype
]]
619 def addCall(self
, street
, player
=None, amount
=None):
621 amount
= amount
.replace(u
',', u
'') #some sites have commas
622 log
.debug(_("%s %s calls %s") %(street
, player
, amount
))
623 # Potentially calculate the amount of the call if not supplied
624 # corner cases include if player would be all in
625 if amount
is not None:
626 self
.checkPlayerExists(player
, 'addCall')
627 amount
= Decimal(amount
)
628 self
.bets
[street
][player
].append(amount
)
629 #self.lastBet[street] = amount
630 self
.stacks
[player
] -= amount
631 #print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player])
632 act
= (player
, 'calls', amount
, self
.stacks
[player
] == 0)
633 self
.actions
[street
].append(act
)
634 self
.pot
.addMoney(player
, amount
)
636 def addCallTo(self
, street
, player
=None, amountTo
=None):
638 amountTo
= amountTo
.replace(u
',', u
'') #some sites have commas
639 #log.debug(_("%s %s calls %s") %(street, player, amount))
640 # Potentially calculate the amount of the callTo if not supplied
641 # corner cases include if player would be all in
642 if amountTo
is not None:
643 self
.checkPlayerExists(player
, 'addCallTo')
644 Bc
= sum(self
.bets
[street
][player
])
645 Ct
= Decimal(amountTo
)
648 self
.bets
[street
][player
].append(amount
)
649 #self.lastBet[street] = amount
650 self
.stacks
[player
] -= amount
651 #print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player])
652 act
= (player
, 'calls', amount
, self
.stacks
[player
] == 0)
653 self
.actions
[street
].append(act
)
654 self
.pot
.addMoney(player
, amount
)
656 def addRaiseBy(self
, street
, player
, amountBy
):
657 """ Add a raise by amountBy on [street] by [player] """
658 #Given only the amount raised by, the amount of the raise can be calculated by
659 # working out how much this player has already in the pot
660 # (which is the sum of self.bets[street][player])
661 # and how much he needs to call to match the previous player
662 # (which is tracked by self.lastBet)
663 # let Bp = previous bet
664 # Bc = amount player has committed so far
666 # then: C = Bp - Bc (amount to call)
667 # Rt = Bp + Rb (raise to)
669 amountBy
= amountBy
.replace(u
',', u
'') #some sites have commas
670 self
.checkPlayerExists(player
, 'addRaiseBy')
671 Rb
= Decimal(amountBy
)
672 Bp
= self
.lastBet
[street
]
673 Bc
= sum(self
.bets
[street
][player
])
677 self
._addRaise
(street
, player
, C
, Rb
, Rt
)
678 #~ self.bets[street][player].append(C + Rb)
679 #~ self.stacks[player] -= (C + Rb)
680 #~ self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)]
681 #~ self.lastBet[street] = Rt
683 def addCallandRaise(self
, street
, player
, amount
):
684 """ For sites which by "raises x" mean "calls and raises putting a total of x in the por". """
685 self
.checkPlayerExists(player
, 'addCallandRaise')
686 amount
= amount
.replace(u
',', u
'') #some sites have commas
687 CRb
= Decimal(amount
)
688 Bp
= self
.lastBet
[street
]
689 Bc
= sum(self
.bets
[street
][player
])
694 self
._addRaise
(street
, player
, C
, Rb
, Rt
)
696 def addRaiseTo(self
, street
, player
, amountTo
):
697 """ Add a raise on [street] by [player] to [amountTo] """
698 self
.checkPlayerExists(player
, 'addRaiseTo')
699 amountTo
= amountTo
.replace(u
',', u
'') #some sites have commas
700 Bp
= self
.lastBet
[street
]
701 Bc
= sum(self
.bets
[street
][player
])
702 Rt
= Decimal(amountTo
)
705 self
._addRaise
(street
, player
, C
, Rb
, Rt
)
707 def _addRaise(self
, street
, player
, C
, Rb
, Rt
, action
= 'raises'):
708 log
.debug(_("%s %s raise %s") %(street
, player
, Rt
))
709 self
.bets
[street
][player
].append(C
+ Rb
)
710 self
.stacks
[player
] -= (C
+ Rb
)
711 act
= (player
, action
, Rb
, Rt
, C
, self
.stacks
[player
]==0)
712 self
.actions
[street
].append(act
)
713 self
.lastBet
[street
] = Rt
# TODO check this is correct
714 self
.pot
.addMoney(player
, C
+Rb
)
718 def addBet(self
, street
, player
, amount
):
719 log
.debug(_("%s %s bets %s") %(street
, player
, amount
))
720 amount
= amount
.replace(u
',', u
'') #some sites have commas
721 amount
= Decimal(amount
)
722 self
.checkPlayerExists(player
, 'addBet')
723 self
.bets
[street
][player
].append(amount
)
724 self
.stacks
[player
] -= amount
725 #print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player])
726 act
= (player
, 'bets', amount
, self
.stacks
[player
]==0)
727 self
.actions
[street
].append(act
)
728 self
.lastBet
[street
] = amount
729 self
.pot
.addMoney(player
, amount
)
732 def addStandsPat(self
, street
, player
, cards
=None):
733 self
.checkPlayerExists(player
, 'addStandsPat')
734 act
= (player
, 'stands pat')
735 self
.actions
[street
].append(act
)
737 cards
= cards
.split(' ')
738 self
.addHoleCards(street
, player
, open=[], closed
=cards
)
741 def addFold(self
, street
, player
):
742 log
.debug(_("%s %s folds") % (street
, player
))
743 self
.checkPlayerExists(player
, 'addFold')
744 self
.folded
.add(player
)
745 self
.pot
.addFold(player
)
746 self
.actions
[street
].append((player
, 'folds'))
749 def addCheck(self
, street
, player
):
750 #print "DEBUG: %s %s checked" % (street, player)
751 logging
.debug(_("%s %s checks") % (street
, player
))
752 self
.checkPlayerExists(player
, 'addCheck')
753 self
.actions
[street
].append((player
, 'checks'))
756 def addCollectPot(self
,player
, pot
):
757 log
.debug("%s collected %s" % (player
, pot
))
758 self
.checkPlayerExists(player
, 'addCollectPot')
759 self
.collected
= self
.collected
+ [[player
, pot
]]
760 if player
not in self
.collectees
:
761 self
.collectees
[player
] = Decimal(pot
)
763 self
.collectees
[player
] += Decimal(pot
)
766 def addShownCards(self
, cards
, player
, holeandboard
=None, shown
=True, mucked
=False, string
=None):
767 """ For when a player shows cards for any reason (for showdown or out of choice).
768 Card ranks will be uppercased """
769 log
.debug("addShownCards %s hole=%s all=%s" % (player
, cards
, holeandboard
))
770 if cards
is not None:
771 self
.addHoleCards(cards
,player
,shown
, mucked
)
772 if string
is not None:
773 self
.showdownStrings
[player
] = string
774 elif holeandboard
is not None:
775 holeandboard
= set([self
.card(c
) for c
in holeandboard
])
776 board
= set([c
for s
in self
.board
.values() for c
in s
])
777 self
.addHoleCards(holeandboard
.difference(board
),player
,shown
, mucked
)
779 def setUncalledBets(self
, value
):
780 self
.uncalledbets
= value
783 """ If all bets and blinds have been added, totals up the total pot size"""
785 # This gives us the total amount put in the pot
786 if self
.totalpot
is None:
788 self
.totalpot
= self
.pot
.total
790 if self
.uncalledbets
:
791 for i
,v
in enumerate(self
.collected
):
792 if v
[0] in self
.pot
.returned
:
793 self
.collected
[i
][1] = Decimal(v
[1]) - self
.pot
.returned
[v
[0]]
794 self
.collectees
[v
[0]] -= self
.pot
.returned
[v
[0]]
795 self
.pot
.returned
[v
[0]] = 0
797 # This gives us the amount collected, i.e. after rake
798 if self
.totalcollected
is None:
799 self
.totalcollected
= 0;
800 #self.collected looks like [[p1,amount][px,amount]]
801 for entry
in self
.collected
:
802 self
.totalcollected
+= Decimal(entry
[1])
804 def getGameTypeAsString(self
):
805 """ Map the tuple self.gametype onto the pokerstars string describing it """
806 # currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
807 gs
= {"holdem" : "Hold'em",
809 "omahahilo" : "Omaha Hi/Lo",
811 "studhi" : "7 Card Stud",
812 "studhilo" : "7 Card Stud Hi/Lo",
813 "fivedraw" : "5 Card Draw",
814 "27_1draw" : "Single Draw 2-7 Lowball",
815 "27_3draw" : "Triple Draw 2-7 Lowball",
816 "5studhi" : "5 Card Stud",
819 ls
= {"nl" : "No Limit",
822 "cn" : "Cap No Limit",
823 "cp" : "Cap Pot Limit"
826 log
.debug("gametype: %s" %(self
.gametype
))
827 retstring
= "%s %s" %(gs
[self
.gametype
['category']], ls
[self
.gametype
['limitType']])
831 def writeHand(self
, fh
=sys
.__stdout
__):
832 print >>fh
, "Override me"
835 self
.writeHand(sys
.stdout
)
837 def actionString(self
, act
, street
=None):
838 if act
[1] == 'folds':
839 return ("%s: folds " %(act
[0]))
840 elif act
[1] == 'checks':
841 return ("%s: checks " %(act
[0]))
842 elif act
[1] == 'calls':
843 return ("%s: calls %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
844 elif act
[1] == 'bets':
845 return ("%s: bets %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
846 elif act
[1] == 'raises':
847 return ("%s: raises %s%s to %s%s%s" %(act
[0], self
.sym
, act
[2], self
.sym
, act
[3], ' and is all-in' if act
[5] else ''))
848 elif act
[1] == 'completea':
849 return ("%s: completes to %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
850 elif act
[1] == 'posts':
851 if(act
[2] == "small blind"):
852 return ("%s: posts small blind %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
853 elif(act
[2] == "big blind"):
854 return ("%s: posts big blind %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
855 elif(act
[2] == "both"):
856 return ("%s: posts small & big blinds %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
857 elif(act
[2] == "ante"):
858 return ("%s: posts the ante %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
859 elif act
[1] == 'bringin':
860 return ("%s: brings in for %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
861 elif act
[1] == 'discards':
862 return ("%s: discards %s %s%s" %(act
[0], act
[2], 'card' if act
[2] == 1 else 'cards' , " [" + " ".join(self
.discards
[street
][act
[0]]) + "]" if self
.hero
== act
[0] else ''))
863 elif act
[1] == 'stands pat':
864 return ("%s: stands pat" %(act
[0]))
866 def get_actions_short(self
, player
, street
):
867 """ Returns a string with shortcuts for the actions of the given player and the given street
868 F ... fold, X ... Check, B ...Bet, C ... Call, R ... Raise
870 actions
= self
.actions
[street
]
872 for action
in actions
:
874 if action
[1] == 'folds':
876 elif action
[1] == 'checks':
878 elif action
[1] == 'bets':
880 elif action
[1] == 'calls':
882 elif action
[1] == 'raises':
887 def get_actions_short_streets(self
, player
, *streets
):
888 """ Returns a string with shortcuts for the actions of the given player on all given streets seperated by ',' """
890 for street
in streets
:
891 str = self
.get_actions_short(player
, street
)
892 if len(str) > 0: #if there is no action on later streets, nothing is added.
894 return ','.join(list)
896 def get_player_position(self
, player
):
897 """ Returns the given players postion (S, B, 0-7) """
898 #position has been added to the players list. It could be calculated from buttonpos and player seatnums,
899 #but whats the point in calculating a value that has been there anyway?
900 for p
in self
.players
:
904 def getStakesAsString(self
):
905 """Return a string of the stakes of the current hand."""
906 return "%s%s/%s%s" % (self
.sym
, self
.sb
, self
.sym
, self
.bb
)
908 def getStreetTotals(self
):
911 def writeGameLine(self
):
912 """Return the first HH line for the current hand."""
913 gs
= "PokerStars Game #%s: " % self
.handid
915 if self
.tourNo
is not None and self
.mixed
is not None: # mixed tournament
916 gs
= gs
+ "Tournament #%s, %s %s (%s) - Level %s (%s) - " % (self
.tourNo
, self
.buyin
, self
.MS
[self
.mixed
], self
.getGameTypeAsString(), self
.level
, self
.getStakesAsString())
917 elif self
.tourNo
is not None: # all other tournaments
918 gs
= gs
+ "Tournament #%s, %s %s - Level %s (%s) - " % (self
.tourNo
,
919 self
.buyin
, self
.getGameTypeAsString(), self
.level
, self
.getStakesAsString())
920 elif self
.mixed
is not None: # all other mixed games
921 gs
= gs
+ " %s (%s, %s) - " % (self
.MS
[self
.mixed
],
922 self
.getGameTypeAsString(), self
.getStakesAsString())
923 else: # non-mixed cash games
924 gs
= gs
+ " %s (%s) - " % (self
.getGameTypeAsString(), self
.getStakesAsString())
927 timestr
= datetime
.datetime
.strftime(self
.startTime
, '%Y/%m/%d %H:%M:%S ET')
929 print _("*** ERROR - HAND: calling writeGameLine with unexpected STARTTIME value, expecting datetime.date object, received:"), self
.startTime
930 print _("*** Make sure your HandHistoryConverter is setting hand.startTime properly!")
931 print _("*** Game String:"), gs
936 def writeTableLine(self
):
937 table_string
= "Table "
938 if self
.gametype
['type'] == 'tour':
939 table_string
= table_string
+ "\'%s %s\' %s-max" % (self
.tourNo
, self
.tablename
, self
.maxseats
)
941 table_string
= table_string
+ "\'%s\' %s-max" % (self
.tablename
, self
.maxseats
)
942 if self
.gametype
['currency'] == 'play':
943 table_string
= table_string
+ " (Play Money)"
944 if self
.buttonpos
!= None and self
.buttonpos
!= 0:
945 table_string
= table_string
+ " Seat #%s is the button" % self
.buttonpos
949 def writeHand(self
, fh
=sys
.__stdout
__):
951 print >>fh
, self
.writeGameLine()
952 print >>fh
, self
.writeTableLine()
955 class HoldemOmahaHand(Hand
):
956 def __init__(self
, config
, hhc
, sitename
, gametype
, handText
, builtFrom
= "HHC", handid
=None):
958 if gametype
['base'] != 'hold':
959 pass # or indeed don't pass and complain instead
960 log
.debug("HoldemOmahaHand")
961 self
.allStreets
= ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER']
962 self
.holeStreets
= ['PREFLOP']
963 self
.communityStreets
= ['FLOP', 'TURN', 'RIVER']
964 self
.actionStreets
= ['BLINDSANTES','PREFLOP','FLOP','TURN','RIVER']
965 Hand
.__init
__(self
, self
.config
, sitename
, gametype
, handText
, builtFrom
= "HHC")
966 self
.sb
= gametype
['sb']
967 self
.bb
= gametype
['bb']
968 if hasattr(hhc
, "in_path"):
969 self
.in_path
= hhc
.in_path
971 self
.in_path
= "database"
973 #Populate a HoldemOmahaHand
974 #Generally, we call 'read' methods here, which get the info according to the particular filter (hhc)
975 # which then invokes a 'addXXX' callback
976 if builtFrom
== "HHC":
977 hhc
.readHandInfo(self
)
978 if self
.gametype
['type'] == 'tour':
979 self
.tablename
= "%s %s" % (self
.tourNo
, self
.tablename
)
980 hhc
.readPlayerStacks(self
)
981 hhc
.compilePlayerRegexs(self
)
982 hhc
.markStreets(self
)
991 hhc
.readHeroCards(self
)
992 hhc
.readShowdownActions(self
)
993 # Read actions in street order
994 for street
, text
in self
.streets
.iteritems():
995 if text
and (street
is not "PREFLOP"): #TODO: the except PREFLOP shouldn't be necessary, but regression-test-files/cash/Everleaf/Flop/NLHE-10max-USD-0.01-0.02-201008.2Way.All-in.pre.txt fails without it
996 hhc
.readCommunityCards(self
, street
)
997 for street
in self
.actionStreets
:
998 if self
.streets
[street
]:
999 hhc
.readAction(self
, street
)
1000 self
.pot
.markTotal(street
)
1001 hhc
.readCollectPot(self
)
1002 hhc
.readShownCards(self
)
1003 self
.pot
.handid
= self
.handid
# This is only required so Pot can throw it in totalPot
1004 self
.totalPot() # finalise it (total the pot)
1006 if self
.maxseats
is None:
1007 self
.maxseats
= hhc
.guessMaxSeats(self
)
1009 #print "\nHand:\n"+str(self)
1010 elif builtFrom
== "DB":
1011 # Creator expected to call hhc.select(hid) to fill out object
1012 log
.debug("HoldemOmahaHand.__init__: " + _("DEBUG:") + " " +_("HoldemOmaha hand initialised for %s") % "select()")
1015 log
.warning("HoldemOmahaHand.__init__: " + _("Neither HHC nor DB+handID provided"))
1019 def addShownCards(self
, cards
, player
, shown
=True, mucked
=False, dealt
=False, string
=None):
1020 if player
== self
.hero
: # we have hero's cards just update shown/mucked
1021 if shown
: self
.shown
.add(player
)
1022 if mucked
: self
.mucked
.add(player
)
1024 if len(cards
) in (2, 4): # avoid adding board by mistake (Everleaf problem)
1025 self
.addHoleCards('PREFLOP', player
, open=[], closed
=cards
, shown
=shown
, mucked
=mucked
, dealt
=dealt
)
1026 elif len(cards
) == 5: # cards holds a winning hand, not hole cards
1027 # filter( lambda x: x not in b, a ) # calcs a - b where a and b are lists
1028 # so diff is set to the winning hand minus the board cards, if we're lucky that leaves the hole cards
1029 diff
= filter( lambda x
: x
not in self
.board
['FLOP']+self
.board
['TURN']+self
.board
['RIVER'], cards
)
1030 if len(diff
) == 2 and self
.gametype
['category'] in ('holdem'):
1031 self
.addHoleCards('PREFLOP', player
, open=[], closed
=diff
, shown
=shown
, mucked
=mucked
, dealt
=dealt
)
1032 if string
is not None:
1033 self
.showdownStrings
[player
] = string
1035 def getStreetTotals(self
):
1036 # street1Pot INT, /* pot size at flop/street4 */
1037 # street2Pot INT, /* pot size at turn/street5 */
1038 # street3Pot INT, /* pot size at river/street6 */
1039 # street4Pot INT, /* pot size at sd/street7 */
1040 # showdownPot INT, /* pot size at sd/street7 */
1041 tmp1
= self
.pot
.getTotalAtStreet('FLOP')
1042 tmp2
= self
.pot
.getTotalAtStreet('TURN')
1043 tmp3
= self
.pot
.getTotalAtStreet('RIVER')
1046 return (tmp1
,tmp2
,tmp3
,tmp4
,tmp5
)
1048 def join_holecards(self
, player
, asList
=False):
1049 """With asList = True it returns the set cards for a player including down cards if they aren't know"""
1050 hcs
= [u
'0x', u
'0x', u
'0x', u
'0x']
1052 for street
in self
.holeStreets
:
1053 if player
in self
.holecards
[street
].keys():
1055 hcs
[i
] = self
.holecards
[street
][player
][1][i
]
1056 hcs
[i
] = upper(hcs
[i
][0:1])+hcs
[i
][1:2]
1059 hcs
[i
] = self
.holecards
[street
][player
][1][i
]
1060 hcs
[i
] = upper(hcs
[i
][0:1])+hcs
[i
][1:2]
1066 return " ".join(hcs
)
1071 def writeHTMLHand(self
):
1072 from nevow
import tags
as T
1073 from nevow
import flat
1074 players_who_act_preflop
= (([x
[0] for x
in self
.actions
['PREFLOP']]+[x
[0] for x
in self
.actions
['BLINDSANTES']]))
1075 players_stacks
= [x
for x
in self
.players
if x
[1] in players_who_act_preflop
]
1076 action_streets
= [x
for x
in self
.actionStreets
if len(self
.actions
[x
]) > 0]
1077 def render_stack(context
,data
):
1078 pat
= context
.tag
.patternGenerator('list_item')
1080 x
= "Seat %s: %s (%s%s in chips) " %(player
[0], player
[1],
1081 self
.sym
, player
[2])
1082 context
.tag
[ pat().fillSlots('playerStack', x
)]
1085 def render_street(context
,data
):
1086 pat
= context
.tag
.patternGenerator('list_item')
1089 if street
in self
.holeStreets
and self
.holecards
[street
]:
1091 T
.ol(class_
='dealclosed', data
=street
,
1092 render
=render_deal
) [
1093 T
.li(pattern
='list_item')[ T
.slot(name
='deal') ]
1096 if street
in self
.communityStreets
and self
.board
[street
]:
1098 T
.ol(class_
='community', data
=street
,
1099 render
=render_deal_community
)[
1100 T
.li(pattern
='list_item')[ T
.slot(name
='deal') ]
1103 if street
in self
.actionStreets
and self
.actions
[street
]:
1105 T
.ol(class_
='actions', data
=self
.actions
[street
], render
=render_action
) [
1106 T
.li(pattern
='list_item')[ T
.slot(name
='action') ]
1110 context
.tag
[ pat().fillSlots('street', [ T
.h3
[ street
] ]+lines
)]
1113 def render_deal(context
,data
):
1114 # data is streetname
1115 # we can have open+closed, or just open, or just closed.
1117 if self
.holecards
[data
]:
1118 for player
in self
.holecards
[data
]:
1119 somestuff
= 'dealt to %s %s' % (player
, self
.holecards
[data
][player
])
1120 pat
= context
.tag
.patternGenerator('list_item')
1121 context
.tag
[ pat().fillSlots('deal', somestuff
)]
1124 def render_deal_community(context
,data
):
1125 # data is streetname
1126 if self
.board
[data
]:
1127 somestuff
= '[' + ' '.join(self
.board
[data
]) + ']'
1128 pat
= context
.tag
.patternGenerator('list_item')
1129 context
.tag
[ pat().fillSlots('deal', somestuff
)]
1131 def render_action(context
,data
):
1132 pat
= context
.tag
.patternGenerator('list_item')
1134 x
= self
.actionString(act
)
1135 context
.tag
[ pat().fillSlots('action', x
)]
1140 T
.span(class_
='site')["%s Game #%s]" % ('PokerStars', self
.handid
)],
1141 T
.span(class_
='type_limit')[ "%s ($%s/$%s)" %(self
.getGameTypeAsString(), self
.sb
, self
.bb
) ],
1142 T
.span(class_
='date')[ datetime
.datetime
.strftime(self
.startTime
,'%Y/%m/%d - %H:%M:%S ET') ]
1144 T
.h2
[ "Table '%s' %d-max Seat #%s is the button" %(self
.tablename
,
1145 self
.maxseats
, self
.buttonpos
)],
1146 T
.ol(class_
='stacks', data
= players_stacks
, render
=render_stack
)[
1147 T
.li(pattern
='list_item')[ T
.slot(name
='playerStack') ]
1149 T
.ol(class_
='streets', data
= self
.allStreets
,
1150 render
=render_street
)[
1151 T
.li(pattern
='list_item')[ T
.slot(name
='street')]
1156 options
= dict(input_xml
=True,
1163 return str(tidy
.parseString(flat
.flatten(s
), **options
))
1166 def writeHand(self
, fh
=sys
.__stdout
__):
1167 # PokerStars format.
1168 super(HoldemOmahaHand
, self
).writeHand(fh
)
1170 players_who_act_preflop
= set(([x
[0] for x
in self
.actions
['PREFLOP']]+[x
[0] for x
in self
.actions
['BLINDSANTES']]))
1171 log
.debug(self
.actions
['PREFLOP'])
1172 for player
in [x
for x
in self
.players
if x
[1] in players_who_act_preflop
]:
1173 #Only print stacks of players who do something preflop
1174 print >>fh
, ("Seat %s: %s ($%.2f in chips) " %(player
[0], player
[1], float(player
[2])))
1176 if self
.actions
['BLINDSANTES']:
1177 for act
in self
.actions
['BLINDSANTES']:
1178 print >>fh
, self
.actionString(act
)
1180 print >>fh
, ("*** HOLE CARDS ***")
1181 for player
in self
.dealt
:
1182 print >>fh
, ("Dealt to %s [%s]" %(player
, " ".join(self
.holecards
['PREFLOP'][player
][1])))
1184 for player
in self
.shown
.difference(self
.dealt
):
1185 print >>fh
, ("Dealt to %s [%s]" %(player
, " ".join(self
.holecards
['PREFLOP'][player
][1])))
1187 if self
.actions
['PREFLOP']:
1188 for act
in self
.actions
['PREFLOP']:
1189 print >>fh
, self
.actionString(act
)
1191 if self
.board
['FLOP']:
1192 print >>fh
, ("*** FLOP *** [%s]" %( " ".join(self
.board
['FLOP'])))
1193 if self
.actions
['FLOP']:
1194 for act
in self
.actions
['FLOP']:
1195 print >>fh
, self
.actionString(act
)
1197 if self
.board
['TURN']:
1198 print >>fh
, ("*** TURN *** [%s] [%s]" %( " ".join(self
.board
['FLOP']), " ".join(self
.board
['TURN'])))
1199 if self
.actions
['TURN']:
1200 for act
in self
.actions
['TURN']:
1201 print >>fh
, self
.actionString(act
)
1203 if self
.board
['RIVER']:
1204 print >>fh
, ("*** RIVER *** [%s] [%s]" %(" ".join(self
.board
['FLOP']+self
.board
['TURN']), " ".join(self
.board
['RIVER']) ))
1205 if self
.actions
['RIVER']:
1206 for act
in self
.actions
['RIVER']:
1207 print >>fh
, self
.actionString(act
)
1210 #Some sites don't have a showdown section so we have to figure out if there should be one
1211 # The logic for a showdown is: at the end of river action there are at least two players in the hand
1212 # we probably don't need a showdown section in pseudo stars format for our filtering purposes
1214 print >>fh
, ("*** SHOW DOWN ***")
1215 for name
in self
.shown
:
1216 # TODO: legacy importer can't handle only one holecard here, make sure there are 2 for holdem, 4 for omaha
1217 # TOOD: If HoldHand subclass supports more than omahahi, omahahilo, holdem, add them here
1218 numOfHoleCardsNeeded
= None
1219 if self
.gametype
['category'] in ('omahahi','omahahilo'):
1220 numOfHoleCardsNeeded
= 4
1221 elif self
.gametype
['category'] in ('holdem'):
1222 numOfHoleCardsNeeded
= 2
1223 if len(self
.holecards
['PREFLOP'][name
]) == numOfHoleCardsNeeded
:
1224 print >>fh
, ("%s shows [%s] (a hand...)" % (name
, " ".join(self
.holecards
['PREFLOP'][name
][1])))
1226 # Current PS format has the lines:
1227 # Uncalled bet ($111.25) returned to s0rrow
1228 # s0rrow collected $5.15 from side pot
1229 # stervels: shows [Ks Qs] (two pair, Kings and Queens)
1230 # stervels collected $45.35 from main pot
1231 # Immediately before the summary.
1232 # The current importer uses those lines for importing winning rather than the summary
1233 for name
in self
.pot
.returned
:
1234 print >>fh
, ("Uncalled bet (%s%s) returned to %s" %(self
.sym
, self
.pot
.returned
[name
],name
))
1235 for entry
in self
.collected
:
1236 print >>fh
, ("%s collected %s%s from x pot" %(entry
[0], self
.sym
, entry
[1]))
1238 print >>fh
, ("*** SUMMARY ***")
1239 print >>fh
, "%s | Rake %s%.2f" % (self
.pot
, self
.sym
, self
.rake
)
1242 for street
in ["FLOP", "TURN", "RIVER"]:
1243 board
+= self
.board
[street
]
1244 if board
: # sometimes hand ends preflop without a board
1245 print >>fh
, ("Board [%s]" % (" ".join(board
)))
1247 for player
in [x
for x
in self
.players
if x
[1] in players_who_act_preflop
]:
1250 if name
in self
.collectees
and name
in self
.shown
:
1251 print >>fh
, ("Seat %d: %s showed [%s] and won (%s%s)" % (seatnum
, name
, " ".join(self
.holecards
['PREFLOP'][name
][1]), self
.sym
, self
.collectees
[name
]))
1252 elif name
in self
.collectees
:
1253 print >>fh
, ("Seat %d: %s collected (%s%s)" % (seatnum
, name
, self
.sym
, self
.collectees
[name
]))
1254 #~ elif name in self.shown:
1255 #~ print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]['PREFLOP'])))
1256 elif name
in self
.folded
:
1257 print >>fh
, ("Seat %d: %s folded" % (seatnum
, name
))
1259 if name
in self
.shown
:
1260 print >>fh
, ("Seat %d: %s showed [%s] and lost with..." % (seatnum
, name
, " ".join(self
.holecards
['PREFLOP'][name
][1])))
1261 elif name
in self
.mucked
:
1262 print >>fh
, ("Seat %d: %s mucked [%s] " % (seatnum
, name
, " ".join(self
.holecards
['PREFLOP'][name
][1])))
1264 print >>fh
, ("Seat %d: %s mucked" % (seatnum
, name
))
1268 class DrawHand(Hand
):
1269 def __init__(self
, config
, hhc
, sitename
, gametype
, handText
, builtFrom
= "HHC", handid
=None):
1270 self
.config
= config
1271 if gametype
['base'] != 'draw':
1272 pass # or indeed don't pass and complain instead
1273 self
.streetList
= ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1274 self
.allStreets
= ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1275 self
.holeStreets
= ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1276 self
.actionStreets
= ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1277 self
.communityStreets
= []
1278 Hand
.__init
__(self
, self
.config
, sitename
, gametype
, handText
)
1279 self
.sb
= gametype
['sb']
1280 self
.bb
= gametype
['bb']
1281 if hasattr(hhc
, "in_path"):
1282 self
.in_path
= hhc
.in_path
1284 self
.in_path
= "database"
1285 # Populate the draw hand.
1286 if builtFrom
== "HHC":
1287 hhc
.readHandInfo(self
)
1288 if self
.gametype
['type'] == 'tour':
1289 self
.tablename
= "%s %s" % (self
.tourNo
, self
.tablename
)
1290 hhc
.readPlayerStacks(self
)
1291 hhc
.compilePlayerRegexs(self
)
1292 hhc
.markStreets(self
)
1293 # markStreets in Draw may match without dealing cards
1294 if self
.streets
['DEAL'] == None:
1295 log
.error("DrawHand.__init__: " + _("Street 'DEAL' is empty. Was hand '%s' cancelled?") % self
.handid
)
1296 raise FpdbParseError
1297 hhc
.readBlinds(self
)
1299 hhc
.readButton(self
)
1300 hhc
.readHeroCards(self
)
1301 hhc
.readShowdownActions(self
)
1302 # Read actions in street order
1303 for street
in self
.streetList
:
1304 if self
.streets
[street
]:
1305 hhc
.readAction(self
, street
)
1306 self
.pot
.markTotal(street
)
1307 hhc
.readCollectPot(self
)
1308 hhc
.readShownCards(self
)
1309 self
.pot
.handid
= self
.handid
# This is only required so Pot can throw it in totalPot
1310 self
.totalPot() # finalise it (total the pot)
1312 if self
.maxseats
is None:
1313 self
.maxseats
= hhc
.guessMaxSeats(self
)
1315 elif builtFrom
== "DB":
1316 # Creator expected to call hhc.select(hid) to fill out object
1317 print "DEBUG: DrawHand initialised for select()"
1320 def addShownCards(self
, cards
, player
, shown
=True, mucked
=False, dealt
=False, string
=None):
1321 if player
== self
.hero
: # we have hero's cards just update shown/mucked
1322 if shown
: self
.shown
.add(player
)
1323 if mucked
: self
.mucked
.add(player
)
1325 # TODO: Probably better to find the last street with action and add the hole cards to that street
1326 self
.addHoleCards('DRAWTHREE', player
, open=[], closed
=cards
, shown
=shown
, mucked
=mucked
, dealt
=dealt
)
1327 if string
is not None:
1328 self
.showdownStrings
[player
] = string
1331 def discardDrawHoleCards(self
, cards
, player
, street
):
1332 log
.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards
, player
, street
))
1333 self
.discards
[street
][player
] = set([cards
])
1336 def addDiscard(self
, street
, player
, num
, cards
=None):
1337 self
.checkPlayerExists(player
, 'addCollectPot')
1339 act
= (player
, 'discards', Decimal(num
), cards
)
1340 self
.discardDrawHoleCards(cards
, player
, street
)
1342 act
= (player
, 'discards', Decimal(num
))
1343 self
.actions
[street
].append(act
)
1345 def holecardsAsSet(self
, street
, player
):
1346 """Return holdcards: (oc, nc) as set()"""
1347 (nc
,oc
) = self
.holecards
[street
][player
]
1352 def getStreetTotals(self
):
1353 # street1Pot INT, /* pot size at flop/street4 */
1354 # street2Pot INT, /* pot size at turn/street5 */
1355 # street3Pot INT, /* pot size at river/street6 */
1356 # street4Pot INT, /* pot size at sd/street7 */
1357 # showdownPot INT, /* pot size at sd/street7 */
1360 def join_holecards(self
, player
, asList
=False, street
=False):
1361 """With asList = True it returns the set cards for a player including down cards if they aren't know"""
1362 holecards
= [u
'0x']*20
1364 for i
, _street
in enumerate(self
.holeStreets
):
1365 if player
in self
.holecards
[_street
].keys():
1366 allhole
= self
.holecards
[_street
][player
][1] + self
.holecards
[_street
][player
][0]
1367 for c
in range(len(allhole
)):
1369 holecards
[idx
] = allhole
[c
]
1373 return " ".join(holecards
)
1376 if street
in self
.holeStreets
:
1377 if street
== 'DEAL':
1378 return holecards
[0:5] if asList
else " ".join(holecards
[0:5])
1379 elif street
== 'DRAWONE':
1380 return holecards
[5:10] if asList
else " ".join(holecards
[5:10])
1381 elif street
== 'DRAWTWO':
1382 return holecards
[10:15] if asList
else " ".join(holecards
[10:15])
1383 elif street
== 'DRAWTHREE':
1384 return holecards
[15:20] if asList
else " ".join(holecards
[15:20])
1387 def writeHand(self
, fh
=sys
.__stdout
__):
1388 # PokerStars format.
1389 # HH output should not be translated
1390 super(DrawHand
, self
).writeHand(fh
)
1392 players_who_act_ondeal
= set(([x
[0] for x
in self
.actions
['DEAL']]+[x
[0] for x
in self
.actions
['BLINDSANTES']]))
1394 for player
in [x
for x
in self
.players
if x
[1] in players_who_act_ondeal
]:
1395 #Only print stacks of players who do something on deal
1396 print >>fh
, (("Seat %s: %s (%s%s in chips) ") % (player
[0], player
[1], self
.sym
, player
[2]))
1398 if 'BLINDSANTES' in self
.actions
:
1399 for act
in self
.actions
['BLINDSANTES']:
1400 print >>fh
, ("%s: %s %s %s%s" % (act
[0], act
[1], act
[2], self
.sym
, act
[3]))
1402 if 'DEAL' in self
.actions
:
1403 print >>fh
, ("*** DEALING HANDS ***")
1404 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_act_ondeal
]:
1405 if 'DEAL' in self
.holecards
:
1406 if self
.holecards
['DEAL'].has_key(player
):
1407 (nc
,oc
) = self
.holecards
['DEAL'][player
]
1408 print >>fh
, ("Dealt to %s: [%s]") % (player
, " ".join(nc
))
1409 for act
in self
.actions
['DEAL']:
1410 print >>fh
, self
.actionString(act
, 'DEAL')
1412 if 'DRAWONE' in self
.actions
:
1413 print >>fh
, ("*** FIRST DRAW ***")
1414 for act
in self
.actions
['DRAWONE']:
1415 print >>fh
, self
.actionString(act
, 'DRAWONE')
1416 if act
[0] == self
.hero
and act
[1] == 'discards':
1417 (nc
,oc
) = self
.holecardsAsSet('DRAWONE', act
[0])
1418 dc
= self
.discards
['DRAWONE'][act
[0]]
1420 print >>fh
, (("Dealt to %s [%s] [%s]") % (act
[0], " ".join(kc
), " ".join(nc
)))
1422 if 'DRAWTWO' in self
.actions
:
1423 print >>fh
, ("*** SECOND DRAW ***")
1424 for act
in self
.actions
['DRAWTWO']:
1425 print >>fh
, self
.actionString(act
, 'DRAWTWO')
1426 if act
[0] == self
.hero
and act
[1] == 'discards':
1427 (nc
,oc
) = self
.holecardsAsSet('DRAWONE', act
[0])
1428 dc
= self
.discards
['DRAWTWO'][act
[0]]
1430 print >>fh
, (("Dealt to %s [%s] [%s]") % (act
[0], " ".join(kc
), " ".join(nc
)))
1432 if 'DRAWTHREE' in self
.actions
:
1433 print >>fh
, ("*** THIRD DRAW ***")
1434 for act
in self
.actions
['DRAWTHREE']:
1435 print >>fh
, self
.actionString(act
, 'DRAWTHREE')
1436 if act
[0] == self
.hero
and act
[1] == 'discards':
1437 (nc
,oc
) = self
.holecardsAsSet('DRAWONE', act
[0])
1438 dc
= self
.discards
['DRAWTHREE'][act
[0]]
1440 print >>fh
, (("Dealt to %s [%s] [%s]") % (act
[0], " ".join(kc
), " ".join(nc
)))
1442 if 'SHOWDOWN' in self
.actions
:
1443 print >>fh
, ("*** SHOW DOWN ***")
1444 #TODO: Complete SHOWDOWN
1446 # Current PS format has the lines:
1447 # Uncalled bet ($111.25) returned to s0rrow
1448 # s0rrow collected $5.15 from side pot
1449 # stervels: shows [Ks Qs] (two pair, Kings and Queens)
1450 # stervels collected $45.35 from main pot
1451 # Immediately before the summary.
1452 # The current importer uses those lines for importing winning rather than the summary
1453 for name
in self
.pot
.returned
:
1454 print >>fh
, ("Uncalled bet (%s%s) returned to %s" % (self
.sym
, self
.pot
.returned
[name
],name
))
1455 for entry
in self
.collected
:
1456 print >>fh
, ("%s collected %s%s from x pot" % (entry
[0], self
.sym
, entry
[1]))
1458 print >>fh
, ("*** SUMMARY ***")
1459 print >>fh
, "%s | Rake %s%.2f" % (self
.pot
, self
.sym
, self
.rake
)
1464 class StudHand(Hand
):
1465 def __init__(self
, config
, hhc
, sitename
, gametype
, handText
, builtFrom
= "HHC", handid
=None):
1466 self
.config
= config
1467 if gametype
['base'] != 'stud':
1468 pass # or indeed don't pass and complain instead
1470 self
.allStreets
= ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
1471 self
.communityStreets
= []
1472 self
.actionStreets
= ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
1474 self
.streetList
= ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
1475 self
.holeStreets
= ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
1476 Hand
.__init
__(self
, self
.config
, sitename
, gametype
, handText
)
1477 self
.sb
= gametype
['sb']
1478 self
.bb
= gametype
['bb']
1479 if hasattr(hhc
, "in_path"):
1480 self
.in_path
= hhc
.in_path
1482 self
.in_path
= "database"
1483 #Populate the StudHand
1484 #Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc)
1485 # which then invokes a 'addXXX' callback
1486 if builtFrom
== "HHC":
1487 hhc
.readHandInfo(self
)
1488 if self
.gametype
['type'] == 'tour':
1489 self
.tablename
= "%s %s" % (self
.tourNo
, self
.tablename
)
1490 hhc
.readPlayerStacks(self
)
1491 hhc
.compilePlayerRegexs(self
)
1492 hhc
.markStreets(self
)
1494 hhc
.readBringIn(self
)
1495 hhc
.readHeroCards(self
)
1496 # Read actions in street order
1497 for street
in self
.actionStreets
:
1498 if street
== 'BLINDSANTES': continue # OMG--sometime someone folds in the ante round
1499 if self
.streets
[street
]:
1500 log
.debug(street
+ self
.streets
[street
])
1501 hhc
.readAction(self
, street
)
1502 self
.pot
.markTotal(street
)
1503 hhc
.readCollectPot(self
)
1504 hhc
.readShownCards(self
) # not done yet
1505 self
.pot
.handid
= self
.handid
# This is only required so Pot can throw it in totalPot
1506 self
.totalPot() # finalise it (total the pot)
1508 if self
.maxseats
is None:
1509 self
.maxseats
= hhc
.guessMaxSeats(self
)
1511 elif builtFrom
== "DB":
1512 # Creator expected to call hhc.select(hid) to fill out object
1513 print "DEBUG: StudHand initialised for select()"
1516 def addShownCards(self
, cards
, player
, shown
=True, mucked
=False, dealt
=False, string
=None):
1517 if player
== self
.hero
: # we have hero's cards just update shown/mucked
1518 if shown
: self
.shown
.add(player
)
1519 if mucked
: self
.mucked
.add(player
)
1522 self
.addHoleCards('THIRD', player
, open=[cards
[2]], closed
=cards
[0:2], shown
=shown
, mucked
=mucked
)
1523 self
.addHoleCards('FOURTH', player
, open=[cards
[3]], closed
=[cards
[2]], shown
=shown
, mucked
=mucked
)
1524 self
.addHoleCards('FIFTH', player
, open=[cards
[4]], closed
=cards
[2:4], shown
=shown
, mucked
=mucked
)
1525 self
.addHoleCards('SIXTH', player
, open=[cards
[5]], closed
=cards
[2:5], shown
=shown
, mucked
=mucked
)
1526 self
.addHoleCards('SEVENTH', player
, open=[], closed
=[cards
[6]], shown
=shown
, mucked
=mucked
)
1527 if string
is not None:
1528 self
.showdownStrings
[player
] = string
1531 def addPlayerCards(self
, player
, street
, open=[], closed
=[]):
1533 Assigns observed cards to a player.
1534 player (string) name of player
1535 street (string) the street name (in streetList)
1536 open list of card bigrams e.g. ['2h','Jc'], dealt face up
1537 closed likewise, but known only to player
1539 log
.debug("addPlayerCards %s, o%s x%s" % (player
, open, closed
))
1540 self
.checkPlayerExists(player
, 'addPlayerCards')
1541 self
.holecards
[street
][player
] = (open, closed
)
1543 # TODO: def addComplete(self, player, amount):
1544 def addComplete(self
, street
, player
, amountTo
):
1545 # assert street=='THIRD'
1546 # This needs to be called instead of addRaiseTo, and it needs to take account of self.lastBet['THIRD'] to determine the raise-by size
1548 Add a complete on [street] by [player] to [amountTo]
1550 log
.debug(_("%s %s completes %s") % (street
, player
, amountTo
))
1551 amountTo
= amountTo
.replace(u
',', u
'') #some sites have commas
1552 self
.checkPlayerExists(player
, 'addComplete')
1553 Bp
= self
.lastBet
['THIRD']
1554 Bc
= sum(self
.bets
[street
][player
])
1555 Rt
= Decimal(amountTo
)
1558 self
._addRaise
(street
, player
, C
, Rb
, Rt
, 'completes')
1559 #~ self.bets[street][player].append(C + Rb)
1560 #~ self.stacks[player] -= (C + Rb)
1561 #~ act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
1562 #~ self.actions[street].append(act)
1563 #~ self.lastBet[street] = Rt # TODO check this is correct
1564 #~ self.pot.addMoney(player, C+Rb)
1566 def addBringIn(self
, player
, bringin
):
1567 if player
is not None:
1568 log
.debug(_("Bringin: %s, %s") % (player
, bringin
))
1569 bringin
= bringin
.replace(u
',', u
'') #some sites have commas
1570 self
.checkPlayerExists(player
, 'addBringIn')
1571 bringin
= Decimal(bringin
)
1572 self
.bets
['THIRD'][player
].append(bringin
)
1573 self
.stacks
[player
] -= bringin
1574 act
= (player
, 'bringin', bringin
, self
.stacks
[player
]==0)
1575 self
.actions
['THIRD'].append(act
)
1576 self
.lastBet
['THIRD'] = bringin
1577 self
.pot
.addMoney(player
, bringin
)
1579 def getStreetTotals(self
):
1580 # street1Pot INT, /* pot size at flop/street4 */
1581 # street2Pot INT, /* pot size at turn/street5 */
1582 # street3Pot INT, /* pot size at river/street6 */
1583 # street4Pot INT, /* pot size at sd/street7 */
1584 # showdownPot INT, /* pot size at sd/street7 */
1588 def writeHand(self
, fh
=sys
.__stdout
__):
1589 # PokerStars format.
1590 # HH output should not be translated
1591 super(StudHand
, self
).writeHand(fh
)
1593 players_who_post_antes
= set([x
[0] for x
in self
.actions
['BLINDSANTES']])
1595 for player
in [x
for x
in self
.players
if x
[1] in players_who_post_antes
]:
1596 #Only print stacks of players who do something preflop
1597 print >>fh
, ("Seat %s: %s (%s%s in chips)" %(player
[0], player
[1], self
.sym
, player
[2]))
1599 if 'BLINDSANTES' in self
.actions
:
1600 for act
in self
.actions
['BLINDSANTES']:
1601 print >>fh
, ("%s: posts the ante %s%s" %(act
[0], self
.sym
, act
[3]))
1603 if 'THIRD' in self
.actions
:
1605 #~ print >>fh, ("*** 3RD STREET ***")
1606 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1607 if self
.holecards
['THIRD'].has_key(player
):
1608 (open, closed
) = self
.holecards
['THIRD'][player
]
1611 print >>fh
, ("*** 3RD STREET ***")
1612 # print >>fh, ("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "")
1613 print >>fh
, self
.writeHoleCards('THIRD', player
)
1614 for act
in self
.actions
['THIRD']:
1615 #FIXME: Need some logic here for bringin vs completes
1616 print >>fh
, self
.actionString(act
)
1618 if 'FOURTH' in self
.actions
:
1620 #~ print >>fh, ("*** 4TH STREET ***")
1621 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1622 if player
in self
.holecards
['FOURTH']:
1625 print >>fh
, ("*** 4TH STREET ***")
1626 print >>fh
, self
.writeHoleCards('FOURTH', player
)
1627 for act
in self
.actions
['FOURTH']:
1628 print >>fh
, self
.actionString(act
)
1630 if 'FIFTH' in self
.actions
:
1632 #~ print >>fh, ("*** 5TH STREET ***")
1633 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1634 if self
.holecards
['FIFTH'].has_key(player
):
1637 print >>fh
, ("*** 5TH STREET ***")
1638 print >>fh
, self
.writeHoleCards('FIFTH', player
)
1639 for act
in self
.actions
['FIFTH']:
1640 print >>fh
, self
.actionString(act
)
1642 if 'SIXTH' in self
.actions
:
1644 #~ print >>fh, ("*** 6TH STREET ***")
1645 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1646 if self
.holecards
['SIXTH'].has_key(player
):
1649 print >>fh
, ("*** 6TH STREET ***")
1650 print >>fh
, self
.writeHoleCards('SIXTH', player
)
1651 for act
in self
.actions
['SIXTH']:
1652 print >>fh
, self
.actionString(act
)
1654 if 'SEVENTH' in self
.actions
:
1655 # OK. It's possible that they're all in at an earlier street, but only closed cards are dealt.
1656 # Then we have no 'dealt to' lines, no action lines, but still 7th street should appear.
1657 # The only way I can see to know whether to print this line is by knowing the state of the hand
1658 # i.e. are all but one players folded; is there an allin showdown; and all that.
1659 print >>fh
, ("*** RIVER ***")
1660 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1661 if self
.holecards
['SEVENTH'].has_key(player
):
1662 if self
.writeHoleCards('SEVENTH', player
):
1663 print >>fh
, self
.writeHoleCards('SEVENTH', player
)
1664 for act
in self
.actions
['SEVENTH']:
1665 print >>fh
, self
.actionString(act
)
1667 #Some sites don't have a showdown section so we have to figure out if there should be one
1668 # The logic for a showdown is: at the end of river action there are at least two players in the hand
1669 # we probably don't need a showdown section in pseudo stars format for our filtering purposes
1670 if 'SHOWDOWN' in self
.actions
:
1671 print >>fh
, ("*** SHOW DOWN ***")
1672 # TODO: print showdown lines.
1674 # Current PS format has the lines:
1675 # Uncalled bet ($111.25) returned to s0rrow
1676 # s0rrow collected $5.15 from side pot
1677 # stervels: shows [Ks Qs] (two pair, Kings and Queens)
1678 # stervels collected $45.35 from main pot
1679 # Immediately before the summary.
1680 # The current importer uses those lines for importing winning rather than the summary
1681 for name
in self
.pot
.returned
:
1682 print >>fh
, ("Uncalled bet (%s%s) returned to %s" %(self
.sym
, self
.pot
.returned
[name
],name
))
1683 for entry
in self
.collected
:
1684 print >>fh
, ("%s collected %s%s from x pot" %(entry
[0], self
.sym
, entry
[1]))
1686 print >>fh
, ("*** SUMMARY ***")
1687 print >>fh
, "%s | Rake %s%.2f" % (self
.pot
, self
.sym
, self
.rake
)
1691 for s
in self
.board
.values():
1693 if board
: # sometimes hand ends preflop without a board
1694 print >>fh
, ("Board [%s]" % (" ".join(board
)))
1696 for player
in [x
for x
in self
.players
if x
[1] in players_who_post_antes
]:
1699 if name
in self
.collectees
and name
in self
.shown
:
1700 print >>fh
, ("Seat %d: %s showed [%s] and won (%s%s)" % (seatnum
, name
, self
.join_holecards(name
), self
.sym
, self
.collectees
[name
]))
1701 elif name
in self
.collectees
:
1702 print >>fh
, ("Seat %d: %s collected (%s%s)" % (seatnum
, name
, self
.sym
, self
.collectees
[name
]))
1703 elif name
in self
.shown
:
1704 print >>fh
, ("Seat %d: %s showed [%s]" % (seatnum
, name
, self
.join_holecards(name
)))
1705 elif name
in self
.mucked
:
1706 print >>fh
, ("Seat %d: %s mucked [%s]" % (seatnum
, name
, self
.join_holecards(name
)))
1707 elif name
in self
.folded
:
1708 print >>fh
, ("Seat %d: %s folded" % (seatnum
, name
))
1710 print >>fh
, ("Seat %d: %s mucked" % (seatnum
, name
))
1715 def writeHoleCards(self
, street
, player
):
1716 hc
= "Dealt to %s [" % player
1717 if street
== 'THIRD':
1718 if player
== self
.hero
:
1719 return hc
+ " ".join(self
.holecards
[street
][player
][1]) + " " + " ".join(self
.holecards
[street
][player
][0]) + ']'
1721 return hc
+ " ".join(self
.holecards
[street
][player
][0]) + ']'
1723 if street
== 'SEVENTH' and player
!= self
.hero
: return # only write 7th st line for hero, LDO
1724 return hc
+ " ".join(self
.holecards
[street
][player
][1]) + "] [" + " ".join(self
.holecards
[street
][player
][0]) + "]"
1726 def join_holecards(self
, player
, asList
=False):
1727 """Function returns a string for the stud writeHand method by default
1728 With asList = True it returns the set cards for a player including down cards if they aren't know"""
1730 for street
in self
.holeStreets
:
1731 if self
.holecards
[street
].has_key(player
):
1732 if street
== 'THIRD':
1733 holecards
= holecards
+ self
.holecards
[street
][player
][1] + self
.holecards
[street
][player
][0]
1734 elif street
== 'SEVENTH':
1735 if player
== self
.hero
:
1736 holecards
= holecards
+ self
.holecards
[street
][player
][0]
1738 holecards
= holecards
+ self
.holecards
[street
][player
][1]
1740 holecards
= holecards
+ self
.holecards
[street
][player
][0]
1741 #print "DEBUG: street, holecards, player, self.holecards[street][player][0], self.holecards[street][player][1]
1744 return " ".join(holecards
)
1746 if player
== self
.hero
or len(holecards
) == 7:
1748 elif len(holecards
) <= 4:
1749 #Non hero folded before showdown, add first two downcards
1750 holecards
= [u
'0x', u
'0x'] + holecards
1752 log
.warning(_("join_holecards: # of holecards should be either < 4, 4 or 7 - 5 and 6 should be impossible for anyone who is not a hero"))
1753 log
.warning("join_holcards: holecards(%s): %s" % (player
, holecards
))
1754 if holecards
== [u
'0x', u
'0x']:
1755 log
.warning(_("join_holecards: Player '%s' appears not to have been dealt a card") % player
)
1756 # If a player is listed but not dealt a card in a cash game this can occur
1757 # Noticed in FTP Razz hand. Return 3 empty cards in this case
1758 holecards
= [u
'0x', u
'0x', u
'0x']
1766 self
.contenders
= set()
1768 self
.streettotals
= {}
1772 self
.sym
= u
'$' # this is the default currency symbol
1776 def setSym(self
, sym
):
1779 def addPlayer(self
,player
):
1780 self
.committed
[player
] = Decimal(0)
1781 self
.common
[player
] = Decimal(0)
1783 def addFold(self
, player
):
1784 # addFold must be called when a player folds
1785 self
.contenders
.discard(player
)
1787 def addCommonMoney(self
, player
, amount
):
1788 self
.common
[player
] += amount
1790 def addMoney(self
, player
, amount
):
1791 # addMoney must be called for any actions that put money in the pot, in the order they occur
1792 #print "DEBUG: %s adds %s" %(player, amount)
1793 self
.contenders
.add(player
)
1794 self
.committed
[player
] += amount
1796 def markTotal(self
, street
):
1797 self
.streettotals
[street
] = sum(self
.committed
.values()) + sum(self
.common
.values())
1799 def getTotalAtStreet(self
, street
):
1800 if street
in self
.streettotals
:
1801 return self
.streettotals
[street
]
1805 self
.total
= sum(self
.committed
.values()) + sum(self
.common
.values())
1807 # Return any uncalled bet.
1808 committed
= sorted([ (v
,k
) for (k
,v
) in self
.committed
.items()])
1809 #print "DEBUG: committed: %s" % committed
1810 #ERROR below. lastbet is correct in most cases, but wrong when
1811 # additional money is committed to the pot in cash games
1812 # due to an additional sb being posted. (Speculate that
1813 # posting sb+bb is also potentially wrong)
1815 lastbet
= committed
[-1][0] - committed
[-2][0]
1816 if lastbet
> 0: # uncalled
1817 returnto
= committed
[-1][1]
1818 #print "DEBUG: returning %f to %s" % (lastbet, returnto)
1819 self
.total
-= lastbet
1820 self
.committed
[returnto
] -= lastbet
1821 self
.returned
[returnto
] = lastbet
1822 except IndexError, e
:
1823 log
.error(_("Pot.end(): '%s': Major failure while calculating pot: '%s'") % (self
.handid
, e
))
1824 raise FpdbParseError
1826 # Work out side pots
1827 commitsall
= sorted([(v
,k
) for (k
,v
) in self
.committed
.items() if v
>0])
1830 while len(commitsall
) > 0:
1831 commitslive
= [(v
,k
) for (v
,k
) in commitsall
if k
in self
.contenders
]
1832 v1
= commitslive
[0][0]
1833 self
.pots
+= [(sum([min(v
,v1
) for (v
,k
) in commitsall
]), set(k
for (v
,k
) in commitsall
if k
in self
.contenders
))]
1834 commitsall
= [((v
-v1
),k
) for (v
,k
) in commitsall
if v
-v1
>0]
1835 except IndexError, e
:
1836 log
.error(_("Pot.end(): '%s': Major failure while calculating pot: '%s'") % (self
.handid
, e
))
1837 raise FpdbParseError
1839 # TODO: I think rake gets taken out of the pots.
1841 # total pot x. main pot y, side pot z. | rake r
1844 # Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2
1847 if self
.sym
is None:
1849 if self
.total
is None:
1850 # NB if I'm sure end() is idempotent, call it here.
1851 log
.error(_("Error in printing Hand object"))
1852 raise FpdbParseError
1854 ret
= "Total pot %s%.2f" % (self
.sym
, self
.total
)
1855 if len(self
.pots
) < 2:
1857 ret
+= " Main pot %s%.2f" % (self
.sym
, self
.pots
[0][0])
1859 return ret
+ ''.join([ (" Side pot %s%.2f." % (self
.sym
, self
.pots
[x
][0]) ) for x
in xrange(1, len(self
.pots
)) ])