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
35 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
36 log
= logging
.getLogger("parser")
40 from Exceptions
import *
46 ###############################################################3
48 UPS
= {'a':'A', 't':'T', 'j':'J', 'q':'Q', 'k':'K', 'S':'s', 'C':'c', 'H':'h', 'D':'d'}
49 LCS
= {'H':'h', 'D':'d', 'C':'c', 'S':'s'}
50 SYMBOL
= {'USD': '$', 'CAD': '$', 'EUR': u
'$', 'GBP': '$', 'T$': '', 'play': ''}
51 MS
= {'horse' : 'HORSE', '8game' : '8-Game', 'hose' : 'HOSE', 'ha': 'HA'}
52 ACTION
= {'ante': 1, 'small blind': 2, 'secondsb': 3, 'big blind': 4, 'both': 5, 'calls': 6, 'raises': 7,
53 'bets': 8, 'stands pat': 9, 'folds': 10, 'checks': 11, 'discards': 12, 'bringin': 13, 'completes': 14}
56 def __init__(self
, config
, sitename
, gametype
, handText
, builtFrom
= "HHC"):
57 #log.debug( _("Hand.init(): handText is ") + str(handText) )
59 self
.saveActions
= self
.config
.get_import_parameters().get('saveActions')
60 self
.callHud
= self
.config
.get_import_parameters().get("callFpdbHud")
61 self
.cacheSessions
= self
.config
.get_import_parameters().get("cacheSessions")
62 #log = Configuration.get_logger("logging.conf", "db", log_dir=self.config.dir_log)
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
70 self
.cancelled
= False
78 self
.counted_seats
= 0
85 self
.tourneyTypeId
= None
87 self
.buyinCurrency
= None
88 self
.buyInChips
= None
89 self
.fee
= None # the Database code is looking for this one .. ?
98 self
.isShootout
= False
100 self
.addedCurrency
= None
101 self
.tourneyComment
= None
106 self
.tourneysPlayersIds
= {}
108 # Collections indexed by street names
112 self
.actions
= {} # [['mct','bets','$10'],['mika','folds'],['carlg','raises','$20']]
113 self
.board
= {} # dict from street names to community cards
116 self
.showdownStrings
= {}
117 for street
in self
.allStreets
:
118 self
.streets
[street
] = "" # portions of the handText, filled by markStreets()
119 self
.actions
[street
] = []
120 for street
in self
.actionStreets
:
121 self
.bets
[street
] = {}
122 self
.lastBet
[street
] = 0
123 self
.board
[street
] = []
124 for street
in self
.holeStreets
:
125 self
.holecards
[street
] = {} # dict from player names to holecards
126 self
.discards
[street
] = {} # dict from player names to dicts by street ... of tuples ... of discarded holecards
127 # Collections indexed by player names
129 self
.collected
= [] #list of ?
130 self
.collectees
= {} # dict from player names to amounts collected (?)
134 self
.dealt
= set() # 'dealt to' line to be printed
135 self
.shown
= set() # cards were shown
136 self
.mucked
= set() # cards were mucked at showdown
138 # Things to do with money
141 self
.totalcollected
= None
143 # currency symbol for this hand
144 self
.sym
= self
.SYMBOL
[self
.gametype
['currency']] # save typing! delete this attr when done
145 self
.pot
.setSym(self
.sym
)
146 self
.is_duplicate
= False # i.e. don't update hudcache if true
149 vars = ( (_("BB"), self
.bb
),
151 (_("BUTTONPOS"), self
.buttonpos
),
152 (_("HAND NO."), self
.handid
),
153 (_("SITE"), self
.sitename
),
154 (_("TABLE NAME"), self
.tablename
),
155 (_("HERO"), self
.hero
),
156 (_("MAXSEATS"), self
.maxseats
),
157 (_("LEVEL"), self
.level
),
158 (_("MIXED"), self
.mixed
),
159 (_("LASTBET"), self
.lastBet
),
160 (_("ACTION STREETS"), self
.actionStreets
),
161 (_("STREETS"), self
.streets
),
162 (_("ALL STREETS"), self
.allStreets
),
163 (_("COMMUNITY STREETS"), self
.communityStreets
),
164 (_("HOLE STREETS"), self
.holeStreets
),
165 (_("COUNTED SEATS"), self
.counted_seats
),
166 (_("DEALT"), self
.dealt
),
167 (_("SHOWN"), self
.shown
),
168 (_("MUCKED"), self
.mucked
),
169 (_("TOTAL POT"), self
.totalpot
),
170 (_("TOTAL COLLECTED"), self
.totalcollected
),
171 (_("RAKE"), self
.rake
),
172 (_("START TIME"), self
.startTime
),
173 (_("TOURNAMENT NO"), self
.tourNo
),
174 (_("TOURNEY ID"), self
.tourneyId
),
175 (_("TOURNEY TYPE ID"), self
.tourneyTypeId
),
176 (_("BUYIN"), self
.buyin
),
177 (_("BUYIN CURRENCY"), self
.buyinCurrency
),
178 (_("BUYIN CHIPS"), self
.buyInChips
),
179 (_("FEE"), self
.fee
),
180 (_("IS REBUY"), self
.isRebuy
),
181 (_("IS ADDON"), self
.isAddOn
),
182 (_("IS KO"), self
.isKO
),
183 (_("KO BOUNTY"), self
.koBounty
),
184 (_("IS MATRIX"), self
.isMatrix
),
185 (_("IS SHOOTOUT"), self
.isShootout
),
186 (_("TOURNEY COMMENT"), self
.tourneyComment
),
189 structs
= ( (_("PLAYERS"), self
.players
),
190 (_("STACKS"), self
.stacks
),
191 (_("POSTED"), self
.posted
),
192 (_("POT"), self
.pot
),
193 (_("SEATING"), self
.seating
),
194 (_("GAMETYPE"), self
.gametype
),
195 (_("ACTION"), self
.actions
),
196 (_("COLLECTEES"), self
.collectees
),
197 (_("BETS"), self
.bets
),
198 (_("BOARD"), self
.board
),
199 (_("DISCARDS"), self
.discards
),
200 (_("HOLECARDS"), self
.holecards
),
201 (_("TOURNEYS PLAYER IDS"), self
.tourneysPlayersIds
),
204 for (name
, var
) in vars:
205 str = str + "\n%s = " % name
+ pprint
.pformat(var
)
207 for (name
, struct
) in structs
:
208 str = str + "\n%s =\n" % name
+ pprint
.pformat(struct
, 4)
211 def addHoleCards(self
, street
, player
, open=[], closed
=[], shown
=False, mucked
=False, dealt
=False):
213 Assigns observed holecards to a player.
214 cards list of card bigrams e.g. ['2h','Jc']
215 player (string) name of player
216 shown whether they were revealed at showdown
217 mucked whether they were mucked at showdown
218 dealt whether they were seen in a 'dealt to' line
220 # log.debug("addHoleCards %s %s" % (open + closed, player))
222 self
.checkPlayerExists(player
)
223 except FpdbParseError
, e
:
224 print _("[ERROR] Tried to add holecards for unknown player: %s") % (player
,)
227 if dealt
: self
.dealt
.add(player
)
228 if shown
: self
.shown
.add(player
)
229 if mucked
: self
.mucked
.add(player
)
231 self
.holecards
[street
][player
] = [open, closed
]
233 def prepInsert(self
, db
, printtest
= False):
235 # Players, Gametypes, TourneyTypes are all shared functions that are needed for additional tables
236 # These functions are intended for prep insert eventually
238 self
.gametype
['maxSeats'] = self
.maxseats
#TODO: move up to individual parsers
239 self
.gametype
['ante'] = 0 #TODO store actual ante
240 self
.dbid_pids
= db
.getSqlPlayerIDs([p
[1] for p
in self
.players
], self
.siteId
)
241 self
.dbid_gt
= db
.getSqlGameTypeId(self
.siteId
, self
.gametype
, printdata
= printtest
)
245 if self
.gametype
['category'] in ['studhilo', 'omahahilo']:
247 elif self
.gametype
['category'] in ['razz','27_3draw','badugi', '27_1draw']:
250 self
.gametyperow
= (self
.siteId
, self
.gametype
['currency'], self
.gametype
['type'], self
.gametype
['base'],
251 self
.gametype
['category'], self
.gametype
['limitType'], hilo
,
252 int(Decimal(self
.gametype
['sb'])*100), int(Decimal(self
.gametype
['bb'])*100),
253 int(Decimal(self
.gametype
['bb'])*100), int(Decimal(self
.gametype
['bb'])*200), int(self
.gametype
['maxSeats']), int(self
.gametype
['ante']))
254 # Note: the above data is calculated in db.getGameTypeId
255 # Only being calculated above so we can grab the testdata
257 if self
.tourNo
!=None:
258 self
.tourneyTypeId
= db
.getSqlTourneyTypeIDs(self
)
259 self
.tourneyId
= db
.getSqlTourneyIDs(self
)
260 self
.tourneysPlayersIds
= db
.getSqlTourneysPlayersIDs(self
)
262 def assembleHand(self
):
263 self
.stats
.getStats(self
)
264 self
.hands
= self
.stats
.getHands()
265 self
.handsplayers
= self
.stats
.getHandsPlayers()
267 def getHandId(self
, db
, id):
268 if db
.isDuplicate(self
.dbid_gt
, self
.hands
['siteHandNo']):
269 #log.info(_("Hand.insert(): hid #: %s is a duplicate") % hh['siteHandNo'])
270 self
.is_duplicate
= True # i.e. don't update hudcache
272 raise FpdbHandDuplicate(self
.hands
['siteHandNo'])
275 self
.hands
['id'] = self
.dbid_hands
279 def insertHands(self
, db
, hbulk
, fileId
, doinsert
= False, printtest
= False):
280 """ Function to insert Hand into database
281 Should not commit, and do minimal selects. Callers may want to cache commits
282 db: a connected Database object"""
283 self
.hands
['gametypeId'] = self
.dbid_gt
284 self
.hands
['seats'] = len(self
.dbid_pids
)
285 self
.hands
['fileId'] = fileId
286 hbulk
= db
.storeHand(self
.hands
, hbulk
, doinsert
, printtest
)
289 def insertHandsPlayers(self
, db
, hpbulk
, doinsert
= False, printtest
= False):
290 """ Function to inserts HandsPlayers into database"""
291 hpbulk
= db
.storeHandsPlayers(self
.dbid_hands
, self
.dbid_pids
, self
.handsplayers
, hpbulk
, doinsert
, printtest
)
294 def insertHandsActions(self
, db
, habulk
, doinsert
= False, printtest
= False):
295 """ Function to inserts HandsActions into database"""
296 handsactions
= self
.stats
.getHandsActions()
297 habulk
= db
.storeHandsActions(self
.dbid_hands
, self
.dbid_pids
, handsactions
, habulk
, doinsert
, printtest
)
300 def updateHudCache(self
, db
, hcbulk
, doinsert
= False):
301 """ Function to update the HudCache"""
303 hcbulk
= db
.storeHudCache(self
.dbid_gt
, self
.dbid_pids
, self
.startTime
, self
.handsplayers
, hcbulk
, doinsert
)
306 def updateSessionsCache(self
, db
, sc
, gsc
, tz
, doinsert
= False):
307 """ Function to update the SessionsCache"""
308 if self
.cacheSessions
:
309 self
.heros
= db
.getHeroIds(self
.dbid_pids
, self
.sitename
)
310 sc
= db
.prepSessionsCache(self
.dbid_hands
, self
.dbid_pids
, self
.startTime
, sc
, self
.heros
, doinsert
)
311 gsc
= db
.storeSessionsCache(self
.dbid_hands
, self
.dbid_pids
, self
.startTime
, self
.gametype
312 ,self
.dbid_gt
, self
.handsplayers
, sc
, gsc
, tz
, self
.heros
, doinsert
)
313 if doinsert
and sc
['bk'] and gsc
['bk']:
314 self
.hands
['sc'] = sc
315 self
.hands
['gsc'] = gsc
317 self
.hands
['sc'] = None
318 self
.hands
['gsc'] = None
321 def select(self
, db
, handId
):
322 """ Function to create Hand object from database """
326 round(hp.winnings / 100.0,2) as winnings,
328 round(hp.startCash / 100.0,2) as chips,
329 hp.card1,hp.card2,hp.card3,hp.card4,
336 and p.id = hp.playerId
340 q
= q
.replace('%s', db
.sql
.query
['placeholder'])
343 c
.execute(q
, (handId
,))
344 for (seat
, winnings
, name
, chips
, card1
, card2
, card3
, card4
, position
) in c
.fetchall():
345 #print "DEBUG: addPlayer(%s, %s, %s)" %(seat,name,str(chips))
346 self
.addPlayer(seat
,name
,str(chips
))
347 #print "DEBUG: card1: %s" % card1
348 # map() should work, but is returning integers... FIXME later
349 #cardlist = map(Card.valueSuitFromCard, [card1, card2, card3, card4])
350 cardlist
= [Card
.valueSuitFromCard(card1
), Card
.valueSuitFromCard(card2
), Card
.valueSuitFromCard(card3
), Card
.valueSuitFromCard(card4
)]
351 #print "DEUBG: cardlist: '%s'" % cardlist
352 if cardlist
[0] == '':
354 elif self
.gametype
['category'] == 'holdem':
355 self
.addHoleCards('PREFLOP', name
, closed
=cardlist
[0:2], shown
=False, mucked
=False, dealt
=True)
356 elif self
.gametype
['category'] == 'omaha':
357 self
.addHoleCards('PREFLOP', name
, closed
=cardlist
, shown
=False, mucked
=False, dealt
=True)
359 self
.addCollectPot(name
, str(winnings
))
361 self
.buttonpos
= seat
369 q
= q
.replace('%s', db
.sql
.query
['placeholder'])
370 c
.execute(q
, (handId
,))
372 # NOTE: This relies on row_factory = sqlite3.Row (set in connect() params)
373 # Need to find MySQL and Postgres equivalents
374 # MySQL maybe: cursorclass=MySQLdb.cursors.DictCursor
377 # Using row_factory is global, and affects the rest of fpdb. The following 2 line achieves
379 res
= [dict(line
) for line
in [zip([ column
[0] for column
in c
.description
], row
) for row
in c
.fetchall()]]
382 #res['tourneyId'] #res['seats'] #res['rush']
383 self
.tablename
= res
['tableName']
384 self
.handid
= res
['siteHandNo']
385 #print "DBEUG: res['startTime']: %s" % res['startTime']
386 self
.startTime
= datetime
.datetime
.strptime(res
['startTime'], "%Y-%m-%d %H:%M:%S+00:00")
388 cards
= map(Card
.valueSuitFromCard
, [res
['boardcard1'], res
['boardcard2'], res
['boardcard3'], res
['boardcard4'], res
['boardcard5']])
389 #print "DEBUG: res['boardcard1']: %s" % res['boardcard1']
390 #print "DEBUG: cards: %s" % cards
392 self
.setCommunityCards('FLOP', cards
[0:3])
394 self
.setCommunityCards('TURN', [cards
[3]])
396 self
.setCommunityCards('RIVER', [cards
[4]])
397 # playersVpi | playersAtStreet1 | playersAtStreet2 | playersAtStreet3 |
398 # playersAtStreet4 | playersAtShowdown | street0Raises | street1Raises |
399 # street2Raises | street3Raises | street4Raises | street1Pot | street2Pot |
400 # street3Pot | street4Pot | showdownPot | comment | commentTs | texture
410 round(ha.amount / 100.0,2) as bet
419 AND ha.playerId = hp.playerid
420 AND hp.playerId = p.id
426 q
= q
.replace('%s', db
.sql
.query
['placeholder'])
427 c
.execute(q
, (handId
,))
428 res
= [dict(line
) for line
in [zip([ column
[0] for column
in c
.description
], row
) for row
in c
.fetchall()]]
431 street
= row
['street']
432 act
= row
['actionId']
433 # allin True/False if row['allIn'] == 0
435 street
= self
.allStreets
[int(street
)+1]
436 #print "DEBUG: name: '%s' street: '%s' act: '%s' bet: '%s'" %(name, street, act, bet)
437 if act
== 2: # Small Blind
438 self
.addBlind(name
, 'small blind', str(bet
))
439 elif act
== 4: # Big Blind
440 self
.addBlind(name
, 'big blind', str(bet
))
441 elif act
== 6: # Call
442 self
.addCall(street
, name
, str(bet
))
444 self
.addBet(street
, name
, str(bet
))
445 elif act
== 10: # Fold
446 self
.addFold(street
, name
)
447 elif act
== 11: # Check
448 self
.addCheck(street
, name
)
449 elif act
== 7: # Raise
450 self
.addRaiseBy(street
, name
, str(bet
))
452 print "DEBUG: unknown action: '%s'" % act
455 self
.rake
= self
.totalpot
- self
.totalcollected
458 #hhc.readShowdownActions(self)
459 #hc.readShownCards(self)
462 def addPlayer(self
, seat
, name
, chips
):
464 Adds a player to the hand, and initialises data structures indexed by player.
465 seat (int) indicating the seat
466 name (string) player name
467 chips (string) the chips the player has at the start of the hand (can be None)
468 If a player has None chips he won't be added."""
469 log
.debug("addPlayer: %s %s (%s)" % (seat
, name
, chips
))
470 if chips
is not None:
471 chips
= chips
.replace(u
',', u
'') #some sites have commas
472 self
.players
.append([seat
, name
, chips
, 0, 0])
473 self
.stacks
[name
] = Decimal(chips
)
474 self
.pot
.addPlayer(name
)
475 for street
in self
.actionStreets
:
476 self
.bets
[street
][name
] = []
477 #self.holecards[name] = {} # dict from street names.
478 #self.discards[name] = {} # dict from street names.
481 def addPlayerRank(self
, name
, winnings
, rank
):
483 name (string) player name
484 winnings (int) winnings
485 rank (int) rank the player finished the tournament"""
486 log
.debug("addPlayerRank: %s %s (%s)" % (name
, winnings
, rank
))
487 for player
in self
.players
:
488 if player
[1] == name
:
492 def addStreets(self
, match
):
493 # go through m and initialise actions to empty list for each street.
495 self
.streets
.update(match
.groupdict())
496 log
.debug("markStreets:\n"+ str(self
.streets
))
498 tmp
= self
.handText
[0:100]
499 log
.error(_("markstreets didn't match - Assuming hand %s was cancelled") % self
.handid
)
500 self
.cancelled
= True
501 raise FpdbParseError(_("markStreets appeared to fail: First 100 chars: '%s'") % tmp
)
503 def checkPlayerExists(self
,player
):
504 if player
not in [p
[1] for p
in self
.players
]:
505 print (_("DEBUG:") + " checkPlayerExists: " + _("%s fail on hand number %s") % (player
, self
.handid
))
506 raise FpdbParseError("checkPlayerExists: " + _("%s fail on hand number %s") % (player
, self
.handid
))
508 def setCommunityCards(self
, street
, cards
):
509 log
.debug("setCommunityCards %s %s" %(street
, cards
))
510 self
.board
[street
] = [self
.card(c
) for c
in cards
]
511 # print "DEBUG: self.board: %s" % self.board
514 """upper case the ranks but not suits, 'atjqk' => 'ATJQK'"""
515 for k
,v
in self
.UPS
.items():
519 def addAllIn(self
, street
, player
, amount
):
521 For sites (currently only Carbon Poker) which record "all in" as a special action, which can mean either "calls and is all in" or "raises all in".
523 self
.checkPlayerExists(player
)
524 amount
= amount
.replace(u
',', u
'') #some sites have commas
526 Bp
= self
.lastBet
[street
]
527 Bc
= sum(self
.bets
[street
][player
])
530 self
.addCall(street
, player
, amount
)
532 self
.addBet(street
, player
, amount
)
535 self
._addRaise
(street
, player
, C
, Rb
, Ai
)
537 def addAnte(self
, player
, ante
):
538 log
.debug("%s %s antes %s" % ('BLINDSANTES', player
, ante
))
539 if player
is not None:
540 ante
= ante
.replace(u
',', u
'') #some sites have commas
542 self
.bets
['BLINDSANTES'][player
].append(ante
)
543 self
.stacks
[player
] -= ante
544 act
= (player
, 'ante', ante
, self
.stacks
[player
]==0)
545 self
.actions
['BLINDSANTES'].append(act
)
546 # self.pot.addMoney(player, ante)
547 self
.pot
.addCommonMoney(player
, ante
)
548 #I think the antes should be common money, don't have enough hand history to check
550 def addBlind(self
, player
, blindtype
, amount
):
551 # if player is None, it's a missing small blind.
552 # The situation we need to cover are:
553 # Player in small blind posts
554 # - this is a bet of 1 sb, as yet uncalled.
555 # Player in the big blind posts
556 # - this is a call of 1 sb and a raise to 1 bb
558 log
.debug("addBlind: %s posts %s, %s" % (player
, blindtype
, amount
))
559 if player
is not None:
560 amount
= amount
.replace(u
',', u
'') #some sites have commas
561 amount
= Decimal(amount
)
562 self
.stacks
[player
] -= amount
563 act
= (player
, blindtype
, amount
, self
.stacks
[player
]==0)
564 self
.actions
['BLINDSANTES'].append(act
)
566 if blindtype
== 'both':
567 # work with the real amount. limit games are listed as $1, $2, where
568 # the SB 0.50 and the BB is $1, after the turn the minimum bet amount is $2....
569 amount
= Decimal(self
.bb
)
570 sb
= Decimal(self
.sb
)
571 self
.bets
['BLINDSANTES'][player
].append(sb
)
572 self
.pot
.addCommonMoney(player
, sb
)
574 if blindtype
== 'secondsb':
576 sb
= Decimal(self
.sb
)
577 self
.bets
['BLINDSANTES'][player
].append(sb
)
578 self
.pot
.addCommonMoney(player
, sb
)
582 if self
.gametype
['base'] == 'hold':
584 elif self
.gametype
['base'] == 'draw':
587 self
.bets
[street
][player
].append(amount
)
588 self
.pot
.addMoney(player
, amount
)
589 self
.lastBet
[street
] = amount
590 self
.posted
= self
.posted
+ [[player
,blindtype
]]
594 def addCall(self
, street
, player
=None, amount
=None):
596 amount
= amount
.replace(u
',', u
'') #some sites have commas
597 log
.debug(_("%s %s calls %s") %(street
, player
, amount
))
598 # Potentially calculate the amount of the call if not supplied
599 # corner cases include if player would be all in
600 if amount
is not None:
601 amount
= Decimal(amount
)
602 self
.bets
[street
][player
].append(amount
)
603 #self.lastBet[street] = amount
604 self
.stacks
[player
] -= amount
605 #print "DEBUG %s calls %s, stack %s" % (player, amount, self.stacks[player])
606 act
= (player
, 'calls', amount
, self
.stacks
[player
] == 0)
607 self
.actions
[street
].append(act
)
608 self
.pot
.addMoney(player
, amount
)
610 def addRaiseBy(self
, street
, player
, amountBy
):
612 Add a raise by amountBy on [street] by [player]
614 #Given only the amount raised by, the amount of the raise can be calculated by
615 # working out how much this player has already in the pot
616 # (which is the sum of self.bets[street][player])
617 # and how much he needs to call to match the previous player
618 # (which is tracked by self.lastBet)
619 # let Bp = previous bet
620 # Bc = amount player has committed so far
622 # then: C = Bp - Bc (amount to call)
623 # Rt = Bp + Rb (raise to)
625 amountBy
= amountBy
.replace(u
',', u
'') #some sites have commas
626 self
.checkPlayerExists(player
)
627 Rb
= Decimal(amountBy
)
628 Bp
= self
.lastBet
[street
]
629 Bc
= sum(self
.bets
[street
][player
])
633 self
._addRaise
(street
, player
, C
, Rb
, Rt
)
634 #~ self.bets[street][player].append(C + Rb)
635 #~ self.stacks[player] -= (C + Rb)
636 #~ self.actions[street] += [(player, 'raises', Rb, Rt, C, self.stacks[player]==0)]
637 #~ self.lastBet[street] = Rt
639 def addCallandRaise(self
, street
, player
, amount
):
641 For sites which by "raises x" mean "calls and raises putting a total of x in the por". """
642 self
.checkPlayerExists(player
)
643 amount
= amount
.replace(u
',', u
'') #some sites have commas
644 CRb
= Decimal(amount
)
645 Bp
= self
.lastBet
[street
]
646 Bc
= sum(self
.bets
[street
][player
])
651 self
._addRaise
(street
, player
, C
, Rb
, Rt
)
653 def addRaiseTo(self
, street
, player
, amountTo
):
655 Add a raise on [street] by [player] to [amountTo]
657 #CG - No idea if this function has been test/verified
658 self
.checkPlayerExists(player
)
659 amountTo
= amountTo
.replace(u
',', u
'') #some sites have commas
660 Bp
= self
.lastBet
[street
]
661 Bc
= sum(self
.bets
[street
][player
])
662 Rt
= Decimal(amountTo
)
665 self
._addRaise
(street
, player
, C
, Rb
, Rt
)
667 def _addRaise(self
, street
, player
, C
, Rb
, Rt
, action
= 'raises'):
668 log
.debug(_("%s %s raise %s") %(street
, player
, Rt
))
669 self
.bets
[street
][player
].append(C
+ Rb
)
670 self
.stacks
[player
] -= (C
+ Rb
)
671 act
= (player
, action
, Rb
, Rt
, C
, self
.stacks
[player
]==0)
672 self
.actions
[street
].append(act
)
673 self
.lastBet
[street
] = Rt
# TODO check this is correct
674 self
.pot
.addMoney(player
, C
+Rb
)
678 def addBet(self
, street
, player
, amount
):
679 log
.debug(_("%s %s bets %s") %(street
, player
, amount
))
680 amount
= amount
.replace(u
',', u
'') #some sites have commas
681 amount
= Decimal(amount
)
682 self
.checkPlayerExists(player
)
683 self
.bets
[street
][player
].append(amount
)
684 self
.stacks
[player
] -= amount
685 #print "DEBUG %s bets %s, stack %s" % (player, amount, self.stacks[player])
686 act
= (player
, 'bets', amount
, self
.stacks
[player
]==0)
687 self
.actions
[street
].append(act
)
688 self
.lastBet
[street
] = amount
689 self
.pot
.addMoney(player
, amount
)
692 def addStandsPat(self
, street
, player
, cards
):
693 self
.checkPlayerExists(player
)
694 act
= (player
, 'stands pat')
695 self
.actions
[street
].append(act
)
697 cards
= cards
.split(' ')
698 self
.addHoleCards(street
, player
, open=[], closed
=cards
)
701 def addFold(self
, street
, player
):
702 log
.debug(_("%s %s folds") % (street
, player
))
703 self
.checkPlayerExists(player
)
704 self
.folded
.add(player
)
705 self
.pot
.addFold(player
)
706 self
.actions
[street
].append((player
, 'folds'))
709 def addCheck(self
, street
, player
):
710 #print "DEBUG: %s %s checked" % (street, player)
711 logging
.debug(_("%s %s checks") % (street
, player
))
712 self
.checkPlayerExists(player
)
713 self
.actions
[street
].append((player
, 'checks'))
716 def addCollectPot(self
,player
, pot
):
717 log
.debug("%s collected %s" % (player
, pot
))
718 self
.checkPlayerExists(player
)
719 self
.collected
= self
.collected
+ [[player
, pot
]]
720 if player
not in self
.collectees
:
721 self
.collectees
[player
] = Decimal(pot
)
723 self
.collectees
[player
] += Decimal(pot
)
726 def addShownCards(self
, cards
, player
, holeandboard
=None, shown
=True, mucked
=False, string
=None):
728 For when a player shows cards for any reason (for showdown or out of choice).
729 Card ranks will be uppercased
731 log
.debug(_("addShownCards %s hole=%s all=%s") % (player
, cards
, holeandboard
))
732 if cards
is not None:
733 self
.addHoleCards(cards
,player
,shown
, mucked
)
734 if string
is not None:
735 self
.showdownStrings
[player
] = string
736 elif holeandboard
is not None:
737 holeandboard
= set([self
.card(c
) for c
in holeandboard
])
738 board
= set([c
for s
in self
.board
.values() for c
in s
])
739 self
.addHoleCards(holeandboard
.difference(board
),player
,shown
, mucked
)
742 """If all bets and blinds have been added, totals up the total pot size"""
744 # This gives us the total amount put in the pot
745 if self
.totalpot
is None:
747 self
.totalpot
= self
.pot
.total
749 # This gives us the amount collected, i.e. after rake
750 if self
.totalcollected
is None:
751 self
.totalcollected
= 0;
752 #self.collected looks like [[p1,amount][px,amount]]
753 for entry
in self
.collected
:
754 self
.totalcollected
+= Decimal(entry
[1])
756 def getGameTypeAsString(self
):
758 Map the tuple self.gametype onto the pokerstars string describing it
760 # currently it appears to be something like ["ring", "hold", "nl", sb, bb]:
761 gs
= {"holdem" : "Hold'em",
763 "omahahilo" : "Omaha Hi/Lo",
765 "studhi" : "7 Card Stud",
766 "studhilo" : "7 Card Stud Hi/Lo",
767 "fivedraw" : "5 Card Draw",
768 "27_1draw" : "Single Draw 2-7 Lowball",
769 "27_3draw" : "Triple Draw 2-7 Lowball",
770 "5studhi" : "5 Card Stud",
773 ls
= {"nl" : "No Limit",
776 "cn" : "Cap No Limit",
777 "cp" : "Cap Pot Limit"
780 log
.debug("gametype: %s" %(self
.gametype
))
781 retstring
= "%s %s" %(gs
[self
.gametype
['category']], ls
[self
.gametype
['limitType']])
785 def writeHand(self
, fh
=sys
.__stdout
__):
786 print >>fh
, "Override me"
789 self
.writeHand(sys
.stdout
)
791 def actionString(self
, act
, street
=None):
792 if act
[1] == 'folds':
793 return ("%s: folds " %(act
[0]))
794 elif act
[1] == 'checks':
795 return ("%s: checks " %(act
[0]))
796 elif act
[1] == 'calls':
797 return ("%s: calls %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
798 elif act
[1] == 'bets':
799 return ("%s: bets %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
800 elif act
[1] == 'raises':
801 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 ''))
802 elif act
[1] == 'completea':
803 return ("%s: completes to %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
804 elif act
[1] == 'posts':
805 if(act
[2] == "small blind"):
806 return ("%s: posts small blind %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
807 elif(act
[2] == "big blind"):
808 return ("%s: posts big blind %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
809 elif(act
[2] == "both"):
810 return ("%s: posts small & big blinds %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
811 elif(act
[2] == "ante"):
812 return ("%s: posts the ante %s%s%s" %(act
[0], self
.sym
, act
[3], ' and is all-in' if act
[4] else ''))
813 elif act
[1] == 'bringin':
814 return ("%s: brings in for %s%s%s" %(act
[0], self
.sym
, act
[2], ' and is all-in' if act
[3] else ''))
815 elif act
[1] == 'discards':
816 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 ''))
817 elif act
[1] == 'stands pat':
818 return ("%s: stands pat" %(act
[0]))
820 def getStakesAsString(self
):
821 """Return a string of the stakes of the current hand."""
822 return "%s%s/%s%s" % (self
.sym
, self
.sb
, self
.sym
, self
.bb
)
824 def getStreetTotals(self
):
827 def writeGameLine(self
):
828 """Return the first HH line for the current hand."""
829 gs
= "PokerStars Game #%s: " % self
.handid
831 if self
.tourNo
is not None and self
.mixed
is not None: # mixed tournament
832 gs
= gs
+ "Tournament #%s, %s %s (%s) - Level %s (%s) - " % (self
.tourNo
, self
.buyin
, self
.MS
[self
.mixed
], self
.getGameTypeAsString(), self
.level
, self
.getStakesAsString())
833 elif self
.tourNo
is not None: # all other tournaments
834 gs
= gs
+ "Tournament #%s, %s %s - Level %s (%s) - " % (self
.tourNo
,
835 self
.buyin
, self
.getGameTypeAsString(), self
.level
, self
.getStakesAsString())
836 elif self
.mixed
is not None: # all other mixed games
837 gs
= gs
+ " %s (%s, %s) - " % (self
.MS
[self
.mixed
],
838 self
.getGameTypeAsString(), self
.getStakesAsString())
839 else: # non-mixed cash games
840 gs
= gs
+ " %s (%s) - " % (self
.getGameTypeAsString(), self
.getStakesAsString())
843 timestr
= datetime
.datetime
.strftime(self
.startTime
, '%Y/%m/%d %H:%M:%S ET')
845 print _("*** ERROR - HAND: calling writeGameLine with unexpected STARTTIME value, expecting datetime.date object, received:"), self
.startTime
846 print _("*** Make sure your HandHistoryConverter is setting hand.startTime properly!")
847 print _("*** Game String:"), gs
852 def writeTableLine(self
):
853 table_string
= "Table "
854 if self
.gametype
['type'] == 'tour':
855 table_string
= table_string
+ "\'%s %s\' %s-max" % (self
.tourNo
, self
.tablename
, self
.maxseats
)
857 table_string
= table_string
+ "\'%s\' %s-max" % (self
.tablename
, self
.maxseats
)
858 if self
.gametype
['currency'] == 'play':
859 table_string
= table_string
+ " (Play Money)"
860 if self
.buttonpos
!= None and self
.buttonpos
!= 0:
861 table_string
= table_string
+ " Seat #%s is the button" % self
.buttonpos
865 def writeHand(self
, fh
=sys
.__stdout
__):
867 print >>fh
, self
.writeGameLine()
868 print >>fh
, self
.writeTableLine()
871 class HoldemOmahaHand(Hand
):
872 def __init__(self
, config
, hhc
, sitename
, gametype
, handText
, builtFrom
= "HHC", handid
=None):
874 if gametype
['base'] != 'hold':
875 pass # or indeed don't pass and complain instead
876 log
.debug("HoldemOmahaHand")
877 self
.allStreets
= ['BLINDSANTES', 'PREFLOP','FLOP','TURN','RIVER']
878 self
.holeStreets
= ['PREFLOP']
879 self
.communityStreets
= ['FLOP', 'TURN', 'RIVER']
880 self
.actionStreets
= ['BLINDSANTES','PREFLOP','FLOP','TURN','RIVER']
881 Hand
.__init
__(self
, self
.config
, sitename
, gametype
, handText
, builtFrom
= "HHC")
882 self
.sb
= gametype
['sb']
883 self
.bb
= gametype
['bb']
885 #Populate a HoldemOmahaHand
886 #Generally, we call 'read' methods here, which get the info according to the particular filter (hhc)
887 # which then invokes a 'addXXX' callback
888 if builtFrom
== "HHC":
889 hhc
.readHandInfo(self
)
890 if self
.gametype
['type'] == 'tour':
891 self
.tablename
= "%s %s" % (self
.tourNo
, self
.tablename
)
892 hhc
.readPlayerStacks(self
)
893 hhc
.compilePlayerRegexs(self
)
894 hhc
.markStreets(self
)
903 hhc
.readHeroCards(self
)
904 hhc
.readShowdownActions(self
)
905 # Read actions in street order
906 for street
, text
in self
.streets
.iteritems():
907 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
908 hhc
.readCommunityCards(self
, street
)
909 for street
in self
.actionStreets
:
910 if self
.streets
[street
]:
911 hhc
.readAction(self
, street
)
912 self
.pot
.markTotal(street
)
913 hhc
.readCollectPot(self
)
914 hhc
.readShownCards(self
)
915 self
.totalPot() # finalise it (total the pot)
917 if self
.maxseats
is None:
918 self
.maxseats
= hhc
.guessMaxSeats(self
)
920 #print "\nHand:\n"+str(self)
921 elif builtFrom
== "DB":
922 #if handid is not None:
923 # self.select(handid) # Will need a handId
925 # log.warning(_("HoldemOmahaHand.__init__:Can't assemble hand from db without a handid"))
926 print "DEBUG: HoldemOmaha hand initialised for select()"
928 log
.warning(_("HoldemOmahaHand.__init__:Neither HHC nor DB+handid provided"))
932 def addShownCards(self
, cards
, player
, shown
=True, mucked
=False, dealt
=False, string
=None):
933 if player
== self
.hero
: # we have hero's cards just update shown/mucked
934 if shown
: self
.shown
.add(player
)
935 if mucked
: self
.mucked
.add(player
)
937 if len(cards
) in (2, 4): # avoid adding board by mistake (Everleaf problem)
938 self
.addHoleCards('PREFLOP', player
, open=[], closed
=cards
, shown
=shown
, mucked
=mucked
, dealt
=dealt
)
939 elif len(cards
) == 5: # cards holds a winning hand, not hole cards
940 # filter( lambda x: x not in b, a ) # calcs a - b where a and b are lists
941 # so diff is set to the winning hand minus the board cards, if we're lucky that leaves the hole cards
942 diff
= filter( lambda x
: x
not in self
.board
['FLOP']+self
.board
['TURN']+self
.board
['RIVER'], cards
)
943 if len(diff
) == 2 and self
.gametype
['category'] in ('holdem'):
944 self
.addHoleCards('PREFLOP', player
, open=[], closed
=diff
, shown
=shown
, mucked
=mucked
, dealt
=dealt
)
945 if string
is not None:
946 self
.showdownStrings
[player
] = string
948 def getStreetTotals(self
):
949 # street1Pot INT, /* pot size at flop/street4 */
950 # street2Pot INT, /* pot size at turn/street5 */
951 # street3Pot INT, /* pot size at river/street6 */
952 # street4Pot INT, /* pot size at sd/street7 */
953 # showdownPot INT, /* pot size at sd/street7 */
954 tmp1
= self
.pot
.getTotalAtStreet('FLOP')
955 tmp2
= self
.pot
.getTotalAtStreet('TURN')
956 tmp3
= self
.pot
.getTotalAtStreet('RIVER')
959 return (tmp1
,tmp2
,tmp3
,tmp4
,tmp5
)
961 def join_holecards(self
, player
, asList
=False):
962 """With asList = True it returns the set cards for a player including down cards if they aren't know"""
963 hcs
= [u
'0x', u
'0x', u
'0x', u
'0x']
965 for street
in self
.holeStreets
:
966 if player
in self
.holecards
[street
].keys():
967 hcs
[0] = self
.holecards
[street
][player
][1][0]
968 hcs
[1] = self
.holecards
[street
][player
][1][1]
970 hcs
[2] = self
.holecards
[street
][player
][1][2]
971 hcs
[3] = self
.holecards
[street
][player
][1][3]
981 def writeHTMLHand(self
):
982 from nevow
import tags
as T
983 from nevow
import flat
984 players_who_act_preflop
= (([x
[0] for x
in self
.actions
['PREFLOP']]+[x
[0] for x
in self
.actions
['BLINDSANTES']]))
985 players_stacks
= [x
for x
in self
.players
if x
[1] in players_who_act_preflop
]
986 action_streets
= [x
for x
in self
.actionStreets
if len(self
.actions
[x
]) > 0]
987 def render_stack(context
,data
):
988 pat
= context
.tag
.patternGenerator('list_item')
990 x
= "Seat %s: %s (%s%s in chips) " %(player
[0], player
[1],
992 context
.tag
[ pat().fillSlots('playerStack', x
)]
995 def render_street(context
,data
):
996 pat
= context
.tag
.patternGenerator('list_item')
999 if street
in self
.holeStreets
and self
.holecards
[street
]:
1001 T
.ol(class_
='dealclosed', data
=street
,
1002 render
=render_deal
) [
1003 T
.li(pattern
='list_item')[ T
.slot(name
='deal') ]
1006 if street
in self
.communityStreets
and self
.board
[street
]:
1008 T
.ol(class_
='community', data
=street
,
1009 render
=render_deal_community
)[
1010 T
.li(pattern
='list_item')[ T
.slot(name
='deal') ]
1013 if street
in self
.actionStreets
and self
.actions
[street
]:
1015 T
.ol(class_
='actions', data
=self
.actions
[street
], render
=render_action
) [
1016 T
.li(pattern
='list_item')[ T
.slot(name
='action') ]
1020 context
.tag
[ pat().fillSlots('street', [ T
.h3
[ street
] ]+lines
)]
1023 def render_deal(context
,data
):
1024 # data is streetname
1025 # we can have open+closed, or just open, or just closed.
1027 if self
.holecards
[data
]:
1028 for player
in self
.holecards
[data
]:
1029 somestuff
= 'dealt to %s %s' % (player
, self
.holecards
[data
][player
])
1030 pat
= context
.tag
.patternGenerator('list_item')
1031 context
.tag
[ pat().fillSlots('deal', somestuff
)]
1034 def render_deal_community(context
,data
):
1035 # data is streetname
1036 if self
.board
[data
]:
1037 somestuff
= '[' + ' '.join(self
.board
[data
]) + ']'
1038 pat
= context
.tag
.patternGenerator('list_item')
1039 context
.tag
[ pat().fillSlots('deal', somestuff
)]
1041 def render_action(context
,data
):
1042 pat
= context
.tag
.patternGenerator('list_item')
1044 x
= self
.actionString(act
)
1045 context
.tag
[ pat().fillSlots('action', x
)]
1050 T
.span(class_
='site')["%s Game #%s]" % ('PokerStars', self
.handid
)],
1051 T
.span(class_
='type_limit')[ "%s ($%s/$%s)" %(self
.getGameTypeAsString(), self
.sb
, self
.bb
) ],
1052 T
.span(class_
='date')[ datetime
.datetime
.strftime(self
.startTime
,'%Y/%m/%d - %H:%M:%S ET') ]
1054 T
.h2
[ "Table '%s' %d-max Seat #%s is the button" %(self
.tablename
,
1055 self
.maxseats
, self
.buttonpos
)],
1056 T
.ol(class_
='stacks', data
= players_stacks
, render
=render_stack
)[
1057 T
.li(pattern
='list_item')[ T
.slot(name
='playerStack') ]
1059 T
.ol(class_
='streets', data
= self
.allStreets
,
1060 render
=render_street
)[
1061 T
.li(pattern
='list_item')[ T
.slot(name
='street')]
1066 options
= dict(input_xml
=True,
1073 return str(tidy
.parseString(flat
.flatten(s
), **options
))
1076 def writeHand(self
, fh
=sys
.__stdout
__):
1077 # PokerStars format.
1078 super(HoldemOmahaHand
, self
).writeHand(fh
)
1080 players_who_act_preflop
= set(([x
[0] for x
in self
.actions
['PREFLOP']]+[x
[0] for x
in self
.actions
['BLINDSANTES']]))
1081 log
.debug(self
.actions
['PREFLOP'])
1082 for player
in [x
for x
in self
.players
if x
[1] in players_who_act_preflop
]:
1083 #Only print stacks of players who do something preflop
1084 print >>fh
, ("Seat %s: %s ($%.2f in chips) " %(player
[0], player
[1], float(player
[2])))
1086 if self
.actions
['BLINDSANTES']:
1087 for act
in self
.actions
['BLINDSANTES']:
1088 print >>fh
, self
.actionString(act
)
1090 print >>fh
, ("*** HOLE CARDS ***")
1091 for player
in self
.dealt
:
1092 print >>fh
, ("Dealt to %s [%s]" %(player
, " ".join(self
.holecards
['PREFLOP'][player
][1])))
1094 for player
in self
.shown
.difference(self
.dealt
):
1095 print >>fh
, ("Dealt to %s [%s]" %(player
, " ".join(self
.holecards
['PREFLOP'][player
][1])))
1097 if self
.actions
['PREFLOP']:
1098 for act
in self
.actions
['PREFLOP']:
1099 print >>fh
, self
.actionString(act
)
1101 if self
.board
['FLOP']:
1102 print >>fh
, ("*** FLOP *** [%s]" %( " ".join(self
.board
['FLOP'])))
1103 if self
.actions
['FLOP']:
1104 for act
in self
.actions
['FLOP']:
1105 print >>fh
, self
.actionString(act
)
1107 if self
.board
['TURN']:
1108 print >>fh
, ("*** TURN *** [%s] [%s]" %( " ".join(self
.board
['FLOP']), " ".join(self
.board
['TURN'])))
1109 if self
.actions
['TURN']:
1110 for act
in self
.actions
['TURN']:
1111 print >>fh
, self
.actionString(act
)
1113 if self
.board
['RIVER']:
1114 print >>fh
, ("*** RIVER *** [%s] [%s]" %(" ".join(self
.board
['FLOP']+self
.board
['TURN']), " ".join(self
.board
['RIVER']) ))
1115 if self
.actions
['RIVER']:
1116 for act
in self
.actions
['RIVER']:
1117 print >>fh
, self
.actionString(act
)
1120 #Some sites don't have a showdown section so we have to figure out if there should be one
1121 # The logic for a showdown is: at the end of river action there are at least two players in the hand
1122 # we probably don't need a showdown section in pseudo stars format for our filtering purposes
1124 print >>fh
, ("*** SHOW DOWN ***")
1125 for name
in self
.shown
:
1126 # TODO: legacy importer can't handle only one holecard here, make sure there are 2 for holdem, 4 for omaha
1127 # TOOD: If HoldHand subclass supports more than omahahi, omahahilo, holdem, add them here
1128 numOfHoleCardsNeeded
= None
1129 if self
.gametype
['category'] in ('omahahi','omahahilo'):
1130 numOfHoleCardsNeeded
= 4
1131 elif self
.gametype
['category'] in ('holdem'):
1132 numOfHoleCardsNeeded
= 2
1133 if len(self
.holecards
['PREFLOP'][name
]) == numOfHoleCardsNeeded
:
1134 print >>fh
, ("%s shows [%s] (a hand...)" % (name
, " ".join(self
.holecards
['PREFLOP'][name
][1])))
1136 # Current PS format has the lines:
1137 # Uncalled bet ($111.25) returned to s0rrow
1138 # s0rrow collected $5.15 from side pot
1139 # stervels: shows [Ks Qs] (two pair, Kings and Queens)
1140 # stervels collected $45.35 from main pot
1141 # Immediately before the summary.
1142 # The current importer uses those lines for importing winning rather than the summary
1143 for name
in self
.pot
.returned
:
1144 print >>fh
, ("Uncalled bet (%s%s) returned to %s" %(self
.sym
, self
.pot
.returned
[name
],name
))
1145 for entry
in self
.collected
:
1146 print >>fh
, ("%s collected %s%s from x pot" %(entry
[0], self
.sym
, entry
[1]))
1148 print >>fh
, ("*** SUMMARY ***")
1149 print >>fh
, "%s | Rake %s%.2f" % (self
.pot
, self
.sym
, self
.rake
)
1152 for street
in ["FLOP", "TURN", "RIVER"]:
1153 board
+= self
.board
[street
]
1154 if board
: # sometimes hand ends preflop without a board
1155 print >>fh
, ("Board [%s]" % (" ".join(board
)))
1157 for player
in [x
for x
in self
.players
if x
[1] in players_who_act_preflop
]:
1160 if name
in self
.collectees
and name
in self
.shown
:
1161 print >>fh
, ("Seat %d: %s showed [%s] and won (%s%s)" % (seatnum
, name
, " ".join(self
.holecards
['PREFLOP'][name
][1]), self
.sym
, self
.collectees
[name
]))
1162 elif name
in self
.collectees
:
1163 print >>fh
, ("Seat %d: %s collected (%s%s)" % (seatnum
, name
, self
.sym
, self
.collectees
[name
]))
1164 #~ elif name in self.shown:
1165 #~ print >>fh, _("Seat %d: %s showed [%s]" % (seatnum, name, " ".join(self.holecards[name]['PREFLOP'])))
1166 elif name
in self
.folded
:
1167 print >>fh
, ("Seat %d: %s folded" % (seatnum
, name
))
1169 if name
in self
.shown
:
1170 print >>fh
, ("Seat %d: %s showed [%s] and lost with..." % (seatnum
, name
, " ".join(self
.holecards
['PREFLOP'][name
][1])))
1171 elif name
in self
.mucked
:
1172 print >>fh
, ("Seat %d: %s mucked [%s] " % (seatnum
, name
, " ".join(self
.holecards
['PREFLOP'][name
][1])))
1174 print >>fh
, ("Seat %d: %s mucked" % (seatnum
, name
))
1178 class DrawHand(Hand
):
1179 def __init__(self
, config
, hhc
, sitename
, gametype
, handText
, builtFrom
= "HHC"):
1180 self
.config
= config
1181 if gametype
['base'] != 'draw':
1182 pass # or indeed don't pass and complain instead
1183 self
.streetList
= ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1184 self
.allStreets
= ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1185 self
.holeStreets
= ['DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1186 self
.actionStreets
= ['BLINDSANTES', 'DEAL', 'DRAWONE', 'DRAWTWO', 'DRAWTHREE']
1187 self
.communityStreets
= []
1188 Hand
.__init
__(self
, self
.config
, sitename
, gametype
, handText
)
1189 self
.sb
= gametype
['sb']
1190 self
.bb
= gametype
['bb']
1191 # Populate the draw hand.
1192 if builtFrom
== "HHC":
1193 hhc
.readHandInfo(self
)
1194 if self
.gametype
['type'] == 'tour':
1195 self
.tablename
= "%s %s" % (self
.tourNo
, self
.tablename
)
1196 hhc
.readPlayerStacks(self
)
1197 hhc
.compilePlayerRegexs(self
)
1198 hhc
.markStreets(self
)
1199 # markStreets in Draw may match without dealing cards
1200 if self
.streets
['DEAL'] == None:
1201 raise FpdbParseError(_("DrawHand.__init__: street 'DEAL' is empty. Hand cancelled? HandID: '%s'") % self
.handid
)
1202 hhc
.readBlinds(self
)
1204 hhc
.readButton(self
)
1205 hhc
.readHeroCards(self
)
1206 hhc
.readShowdownActions(self
)
1207 # Read actions in street order
1208 for street
in self
.streetList
:
1209 if self
.streets
[street
]:
1210 hhc
.readAction(self
, street
)
1211 self
.pot
.markTotal(street
)
1212 hhc
.readCollectPot(self
)
1213 hhc
.readShownCards(self
)
1214 self
.totalPot() # finalise it (total the pot)
1216 if self
.maxseats
is None:
1217 self
.maxseats
= hhc
.guessMaxSeats(self
)
1219 elif builtFrom
== "DB":
1220 self
.select("dummy") # Will need a handId
1222 def addShownCards(self
, cards
, player
, shown
=True, mucked
=False, dealt
=False, string
=None):
1223 if player
== self
.hero
: # we have hero's cards just update shown/mucked
1224 if shown
: self
.shown
.add(player
)
1225 if mucked
: self
.mucked
.add(player
)
1227 # TODO: Probably better to find the last street with action and add the hole cards to that street
1228 self
.addHoleCards('DRAWTHREE', player
, open=[], closed
=cards
, shown
=shown
, mucked
=mucked
, dealt
=dealt
)
1229 if string
is not None:
1230 self
.showdownStrings
[player
] = string
1233 def discardDrawHoleCards(self
, cards
, player
, street
):
1234 log
.debug("discardDrawHoleCards '%s' '%s' '%s'" % (cards
, player
, street
))
1235 self
.discards
[street
][player
] = set([cards
])
1238 def addDiscard(self
, street
, player
, num
, cards
):
1239 self
.checkPlayerExists(player
)
1241 act
= (player
, 'discards', Decimal(num
), cards
)
1242 self
.discardDrawHoleCards(cards
, player
, street
)
1244 act
= (player
, 'discards', Decimal(num
))
1245 self
.actions
[street
].append(act
)
1247 def holecardsAsSet(self
, street
, player
):
1248 """Return holdcards: (oc, nc) as set()"""
1249 (nc
,oc
) = self
.holecards
[street
][player
]
1254 def getStreetTotals(self
):
1255 # street1Pot INT, /* pot size at flop/street4 */
1256 # street2Pot INT, /* pot size at turn/street5 */
1257 # street3Pot INT, /* pot size at river/street6 */
1258 # street4Pot INT, /* pot size at sd/street7 */
1259 # showdownPot INT, /* pot size at sd/street7 */
1262 def join_holecards(self
, player
, asList
=False):
1263 """With asList = True it returns the set cards for a player including down cards if they aren't know"""
1264 # FIXME: This should actually return
1265 holecards
= [u
'0x']*20
1267 for i
, street
in enumerate(self
.holeStreets
):
1268 if player
in self
.holecards
[street
].keys():
1269 allhole
= self
.holecards
[street
][player
][1] + self
.holecards
[street
][player
][0]
1270 for c
in range(len(allhole
)):
1272 holecards
[idx
] = allhole
[c
]
1275 return " ".join(holecards
)
1280 def writeHand(self
, fh
=sys
.__stdout
__):
1281 # PokerStars format.
1282 # HH output should not be translated
1283 super(DrawHand
, self
).writeHand(fh
)
1285 players_who_act_ondeal
= set(([x
[0] for x
in self
.actions
['DEAL']]+[x
[0] for x
in self
.actions
['BLINDSANTES']]))
1287 for player
in [x
for x
in self
.players
if x
[1] in players_who_act_ondeal
]:
1288 #Only print stacks of players who do something on deal
1289 print >>fh
, (("Seat %s: %s (%s%s in chips) ") % (player
[0], player
[1], self
.sym
, player
[2]))
1291 if 'BLINDSANTES' in self
.actions
:
1292 for act
in self
.actions
['BLINDSANTES']:
1293 print >>fh
, ("%s: %s %s %s%s" % (act
[0], act
[1], act
[2], self
.sym
, act
[3]))
1295 if 'DEAL' in self
.actions
:
1296 print >>fh
, ("*** DEALING HANDS ***")
1297 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_act_ondeal
]:
1298 if 'DEAL' in self
.holecards
:
1299 if self
.holecards
['DEAL'].has_key(player
):
1300 (nc
,oc
) = self
.holecards
['DEAL'][player
]
1301 print >>fh
, ("Dealt to %s: [%s]") % (player
, " ".join(nc
))
1302 for act
in self
.actions
['DEAL']:
1303 print >>fh
, self
.actionString(act
, 'DEAL')
1305 if 'DRAWONE' in self
.actions
:
1306 print >>fh
, ("*** FIRST DRAW ***")
1307 for act
in self
.actions
['DRAWONE']:
1308 print >>fh
, self
.actionString(act
, 'DRAWONE')
1309 if act
[0] == self
.hero
and act
[1] == 'discards':
1310 (nc
,oc
) = self
.holecardsAsSet('DRAWONE', act
[0])
1311 dc
= self
.discards
['DRAWONE'][act
[0]]
1313 print >>fh
, (("Dealt to %s [%s] [%s]") % (act
[0], " ".join(kc
), " ".join(nc
)))
1315 if 'DRAWTWO' in self
.actions
:
1316 print >>fh
, ("*** SECOND DRAW ***")
1317 for act
in self
.actions
['DRAWTWO']:
1318 print >>fh
, self
.actionString(act
, 'DRAWTWO')
1319 if act
[0] == self
.hero
and act
[1] == 'discards':
1320 (nc
,oc
) = self
.holecardsAsSet('DRAWONE', act
[0])
1321 dc
= self
.discards
['DRAWTWO'][act
[0]]
1323 print >>fh
, (("Dealt to %s [%s] [%s]") % (act
[0], " ".join(kc
), " ".join(nc
)))
1325 if 'DRAWTHREE' in self
.actions
:
1326 print >>fh
, ("*** THIRD DRAW ***")
1327 for act
in self
.actions
['DRAWTHREE']:
1328 print >>fh
, self
.actionString(act
, 'DRAWTHREE')
1329 if act
[0] == self
.hero
and act
[1] == 'discards':
1330 (nc
,oc
) = self
.holecardsAsSet('DRAWONE', act
[0])
1331 dc
= self
.discards
['DRAWTHREE'][act
[0]]
1333 print >>fh
, (("Dealt to %s [%s] [%s]") % (act
[0], " ".join(kc
), " ".join(nc
)))
1335 if 'SHOWDOWN' in self
.actions
:
1336 print >>fh
, ("*** SHOW DOWN ***")
1337 #TODO: Complete SHOWDOWN
1339 # Current PS format has the lines:
1340 # Uncalled bet ($111.25) returned to s0rrow
1341 # s0rrow collected $5.15 from side pot
1342 # stervels: shows [Ks Qs] (two pair, Kings and Queens)
1343 # stervels collected $45.35 from main pot
1344 # Immediately before the summary.
1345 # The current importer uses those lines for importing winning rather than the summary
1346 for name
in self
.pot
.returned
:
1347 print >>fh
, ("Uncalled bet (%s%s) returned to %s" % (self
.sym
, self
.pot
.returned
[name
],name
))
1348 for entry
in self
.collected
:
1349 print >>fh
, ("%s collected %s%s from x pot" % (entry
[0], self
.sym
, entry
[1]))
1351 print >>fh
, ("*** SUMMARY ***")
1352 print >>fh
, "%s | Rake %s%.2f" % (self
.pot
, self
.sym
, self
.rake
)
1357 class StudHand(Hand
):
1358 def __init__(self
, config
, hhc
, sitename
, gametype
, handText
, builtFrom
= "HHC"):
1359 self
.config
= config
1360 if gametype
['base'] != 'stud':
1361 pass # or indeed don't pass and complain instead
1363 self
.allStreets
= ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
1364 self
.communityStreets
= []
1365 self
.actionStreets
= ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
1367 self
.streetList
= ['BLINDSANTES','THIRD','FOURTH','FIFTH','SIXTH','SEVENTH'] # a list of the observed street names in order
1368 self
.holeStreets
= ['THIRD','FOURTH','FIFTH','SIXTH','SEVENTH']
1369 Hand
.__init
__(self
, self
.config
, sitename
, gametype
, handText
)
1370 self
.sb
= gametype
['sb']
1371 self
.bb
= gametype
['bb']
1372 #Populate the StudHand
1373 #Generally, we call a 'read' method here, which gets the info according to the particular filter (hhc)
1374 # which then invokes a 'addXXX' callback
1375 if builtFrom
== "HHC":
1376 hhc
.readHandInfo(self
)
1377 if self
.gametype
['type'] == 'tour':
1378 self
.tablename
= "%s %s" % (self
.tourNo
, self
.tablename
)
1379 hhc
.readPlayerStacks(self
)
1380 hhc
.compilePlayerRegexs(self
)
1381 hhc
.markStreets(self
)
1383 hhc
.readBringIn(self
)
1384 hhc
.readHeroCards(self
)
1385 # Read actions in street order
1386 for street
in self
.actionStreets
:
1387 if street
== 'BLINDSANTES': continue # OMG--sometime someone folds in the ante round
1388 if self
.streets
[street
]:
1389 log
.debug(street
+ self
.streets
[street
])
1390 hhc
.readAction(self
, street
)
1391 self
.pot
.markTotal(street
)
1392 hhc
.readCollectPot(self
)
1393 hhc
.readShownCards(self
) # not done yet
1394 self
.totalPot() # finalise it (total the pot)
1396 if self
.maxseats
is None:
1397 self
.maxseats
= hhc
.guessMaxSeats(self
)
1399 elif builtFrom
== "DB":
1400 self
.select("dummy") # Will need a handId
1402 def addShownCards(self
, cards
, player
, shown
=True, mucked
=False, dealt
=False, string
=None):
1403 if player
== self
.hero
: # we have hero's cards just update shown/mucked
1404 if shown
: self
.shown
.add(player
)
1405 if mucked
: self
.mucked
.add(player
)
1407 self
.addHoleCards('THIRD', player
, open=[cards
[2]], closed
=cards
[0:2], shown
=shown
, mucked
=mucked
)
1408 self
.addHoleCards('FOURTH', player
, open=[cards
[3]], closed
=[cards
[2]], shown
=shown
, mucked
=mucked
)
1409 self
.addHoleCards('FIFTH', player
, open=[cards
[4]], closed
=cards
[2:4], shown
=shown
, mucked
=mucked
)
1410 self
.addHoleCards('SIXTH', player
, open=[cards
[5]], closed
=cards
[2:5], shown
=shown
, mucked
=mucked
)
1412 self
.addHoleCards('SEVENTH', player
, open=[], closed
=[cards
[6]], shown
=shown
, mucked
=mucked
)
1413 if string
is not None:
1414 self
.showdownStrings
[player
] = string
1417 def addPlayerCards(self
, player
, street
, open=[], closed
=[]):
1419 Assigns observed cards to a player.
1420 player (string) name of player
1421 street (string) the street name (in streetList)
1422 open list of card bigrams e.g. ['2h','Jc'], dealt face up
1423 closed likewise, but known only to player
1425 log
.debug("addPlayerCards %s, o%s x%s" % (player
, open, closed
))
1427 self
.checkPlayerExists(player
)
1428 self
.holecards
[street
][player
] = (open, closed
)
1429 except FpdbParseError
, e
:
1430 print _("[ERROR] Tried to add holecards for unknown player: %s") % (player
,)
1432 # TODO: def addComplete(self, player, amount):
1433 def addComplete(self
, street
, player
, amountTo
):
1434 # assert street=='THIRD'
1435 # This needs to be called instead of addRaiseTo, and it needs to take account of self.lastBet['THIRD'] to determine the raise-by size
1437 Add a complete on [street] by [player] to [amountTo]
1439 log
.debug(_("%s %s completes %s") % (street
, player
, amountTo
))
1440 amountTo
= amountTo
.replace(u
',', u
'') #some sites have commas
1441 self
.checkPlayerExists(player
)
1442 Bp
= self
.lastBet
['THIRD']
1443 Bc
= sum(self
.bets
[street
][player
])
1444 Rt
= Decimal(amountTo
)
1447 self
._addRaise
(street
, player
, C
, Rb
, Rt
, 'completes')
1448 #~ self.bets[street][player].append(C + Rb)
1449 #~ self.stacks[player] -= (C + Rb)
1450 #~ act = (player, 'raises', Rb, Rt, C, self.stacks[player]==0)
1451 #~ self.actions[street].append(act)
1452 #~ self.lastBet[street] = Rt # TODO check this is correct
1453 #~ self.pot.addMoney(player, C+Rb)
1455 def addBringIn(self
, player
, bringin
):
1456 if player
is not None:
1457 log
.debug(_("Bringin: %s, %s") % (player
, bringin
))
1458 bringin
= bringin
.replace(u
',', u
'') #some sites have commas
1459 bringin
= Decimal(bringin
)
1460 self
.bets
['THIRD'][player
].append(bringin
)
1461 self
.stacks
[player
] -= bringin
1462 act
= (player
, 'bringin', bringin
, self
.stacks
[player
]==0)
1463 self
.actions
['THIRD'].append(act
)
1464 self
.lastBet
['THIRD'] = bringin
1465 self
.pot
.addMoney(player
, bringin
)
1467 def getStreetTotals(self
):
1468 # street1Pot INT, /* pot size at flop/street4 */
1469 # street2Pot INT, /* pot size at turn/street5 */
1470 # street3Pot INT, /* pot size at river/street6 */
1471 # street4Pot INT, /* pot size at sd/street7 */
1472 # showdownPot INT, /* pot size at sd/street7 */
1476 def writeHand(self
, fh
=sys
.__stdout
__):
1477 # PokerStars format.
1478 # HH output should not be translated
1479 super(StudHand
, self
).writeHand(fh
)
1481 players_who_post_antes
= set([x
[0] for x
in self
.actions
['BLINDSANTES']])
1483 for player
in [x
for x
in self
.players
if x
[1] in players_who_post_antes
]:
1484 #Only print stacks of players who do something preflop
1485 print >>fh
, ("Seat %s: %s (%s%s in chips)" %(player
[0], player
[1], self
.sym
, player
[2]))
1487 if 'BLINDSANTES' in self
.actions
:
1488 for act
in self
.actions
['BLINDSANTES']:
1489 print >>fh
, ("%s: posts the ante %s%s" %(act
[0], self
.sym
, act
[3]))
1491 if 'THIRD' in self
.actions
:
1493 #~ print >>fh, ("*** 3RD STREET ***")
1494 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1495 if self
.holecards
['THIRD'].has_key(player
):
1496 (open, closed
) = self
.holecards
['THIRD'][player
]
1499 print >>fh
, ("*** 3RD STREET ***")
1500 # print >>fh, ("Dealt to %s:%s%s") % (player, " [" + " ".join(closed) + "] " if closed else " ", "[" + " ".join(open) + "]" if open else "")
1501 print >>fh
, self
.writeHoleCards('THIRD', player
)
1502 for act
in self
.actions
['THIRD']:
1503 #FIXME: Need some logic here for bringin vs completes
1504 print >>fh
, self
.actionString(act
)
1506 if 'FOURTH' in self
.actions
:
1508 #~ print >>fh, ("*** 4TH STREET ***")
1509 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1510 if player
in self
.holecards
['FOURTH']:
1513 print >>fh
, ("*** 4TH STREET ***")
1514 print >>fh
, self
.writeHoleCards('FOURTH', player
)
1515 for act
in self
.actions
['FOURTH']:
1516 print >>fh
, self
.actionString(act
)
1518 if 'FIFTH' in self
.actions
:
1520 #~ print >>fh, ("*** 5TH STREET ***")
1521 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1522 if self
.holecards
['FIFTH'].has_key(player
):
1525 print >>fh
, ("*** 5TH STREET ***")
1526 print >>fh
, self
.writeHoleCards('FIFTH', player
)
1527 for act
in self
.actions
['FIFTH']:
1528 print >>fh
, self
.actionString(act
)
1530 if 'SIXTH' in self
.actions
:
1532 #~ print >>fh, ("*** 6TH STREET ***")
1533 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1534 if self
.holecards
['SIXTH'].has_key(player
):
1537 print >>fh
, ("*** 6TH STREET ***")
1538 print >>fh
, self
.writeHoleCards('SIXTH', player
)
1539 for act
in self
.actions
['SIXTH']:
1540 print >>fh
, self
.actionString(act
)
1542 if 'SEVENTH' in self
.actions
:
1543 # OK. It's possible that they're all in at an earlier street, but only closed cards are dealt.
1544 # Then we have no 'dealt to' lines, no action lines, but still 7th street should appear.
1545 # The only way I can see to know whether to print this line is by knowing the state of the hand
1546 # i.e. are all but one players folded; is there an allin showdown; and all that.
1547 print >>fh
, ("*** RIVER ***")
1548 for player
in [x
[1] for x
in self
.players
if x
[1] in players_who_post_antes
]:
1549 if self
.holecards
['SEVENTH'].has_key(player
):
1550 if self
.writeHoleCards('SEVENTH', player
):
1551 print >>fh
, self
.writeHoleCards('SEVENTH', player
)
1552 for act
in self
.actions
['SEVENTH']:
1553 print >>fh
, self
.actionString(act
)
1555 #Some sites don't have a showdown section so we have to figure out if there should be one
1556 # The logic for a showdown is: at the end of river action there are at least two players in the hand
1557 # we probably don't need a showdown section in pseudo stars format for our filtering purposes
1558 if 'SHOWDOWN' in self
.actions
:
1559 print >>fh
, ("*** SHOW DOWN ***")
1560 # TODO: print showdown lines.
1562 # Current PS format has the lines:
1563 # Uncalled bet ($111.25) returned to s0rrow
1564 # s0rrow collected $5.15 from side pot
1565 # stervels: shows [Ks Qs] (two pair, Kings and Queens)
1566 # stervels collected $45.35 from main pot
1567 # Immediately before the summary.
1568 # The current importer uses those lines for importing winning rather than the summary
1569 for name
in self
.pot
.returned
:
1570 print >>fh
, ("Uncalled bet (%s%s) returned to %s" %(self
.sym
, self
.pot
.returned
[name
],name
))
1571 for entry
in self
.collected
:
1572 print >>fh
, ("%s collected %s%s from x pot" %(entry
[0], self
.sym
, entry
[1]))
1574 print >>fh
, ("*** SUMMARY ***")
1575 print >>fh
, "%s | Rake %s%.2f" % (self
.pot
, self
.sym
, self
.rake
)
1579 for s
in self
.board
.values():
1581 if board
: # sometimes hand ends preflop without a board
1582 print >>fh
, ("Board [%s]" % (" ".join(board
)))
1584 for player
in [x
for x
in self
.players
if x
[1] in players_who_post_antes
]:
1587 if name
in self
.collectees
and name
in self
.shown
:
1588 print >>fh
, ("Seat %d: %s showed [%s] and won (%s%s)" % (seatnum
, name
, self
.join_holecards(name
), self
.sym
, self
.collectees
[name
]))
1589 elif name
in self
.collectees
:
1590 print >>fh
, ("Seat %d: %s collected (%s%s)" % (seatnum
, name
, self
.sym
, self
.collectees
[name
]))
1591 elif name
in self
.shown
:
1592 print >>fh
, ("Seat %d: %s showed [%s]" % (seatnum
, name
, self
.join_holecards(name
)))
1593 elif name
in self
.mucked
:
1594 print >>fh
, ("Seat %d: %s mucked [%s]" % (seatnum
, name
, self
.join_holecards(name
)))
1595 elif name
in self
.folded
:
1596 print >>fh
, ("Seat %d: %s folded" % (seatnum
, name
))
1598 print >>fh
, ("Seat %d: %s mucked" % (seatnum
, name
))
1603 def writeHoleCards(self
, street
, player
):
1604 hc
= "Dealt to %s [" % player
1605 if street
== 'THIRD':
1606 if player
== self
.hero
:
1607 return hc
+ " ".join(self
.holecards
[street
][player
][1]) + " " + " ".join(self
.holecards
[street
][player
][0]) + ']'
1609 return hc
+ " ".join(self
.holecards
[street
][player
][0]) + ']'
1611 if street
== 'SEVENTH' and player
!= self
.hero
: return # only write 7th st line for hero, LDO
1612 return hc
+ " ".join(self
.holecards
[street
][player
][1]) + "] [" + " ".join(self
.holecards
[street
][player
][0]) + "]"
1614 def join_holecards(self
, player
, asList
=False):
1615 """Function returns a string for the stud writeHand method by default
1616 With asList = True it returns the set cards for a player including down cards if they aren't know"""
1618 for street
in self
.holeStreets
:
1619 if self
.holecards
[street
].has_key(player
):
1620 if street
== 'THIRD':
1621 holecards
= holecards
+ self
.holecards
[street
][player
][1] + self
.holecards
[street
][player
][0]
1622 elif street
== 'SEVENTH':
1623 if player
== self
.hero
:
1624 holecards
= holecards
+ self
.holecards
[street
][player
][0]
1626 holecards
= holecards
+ self
.holecards
[street
][player
][1]
1628 holecards
= holecards
+ self
.holecards
[street
][player
][0]
1631 return " ".join(holecards
)
1633 if player
== self
.hero
or len(holecards
) == 7:
1635 elif len(holecards
) <= 4:
1636 #Non hero folded before showdown, add first two downcards
1637 holecards
= [u
'0x', u
'0x'] + holecards
1639 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"))
1640 log
.warning(_("join_holcards: holecards(%s): %s") %(player
, holecards
))
1641 if holecards
== [u
'0x', u
'0x']:
1642 log
.warning(_("join_holecards: Player '%s' appears not to have been dealt a card"))
1643 # If a player is listed but not dealt a card in a cash game this can occur
1644 # Noticed in FTP Razz hand. Return 3 empty cards in this case
1645 holecards
= [u
'0x', u
'0x', u
'0x']
1653 self
.contenders
= set()
1655 self
.streettotals
= {}
1659 self
.sym
= u
'$' # this is the default currency symbol
1661 def setSym(self
, sym
):
1664 def addPlayer(self
,player
):
1665 self
.committed
[player
] = Decimal(0)
1666 self
.common
[player
] = Decimal(0)
1668 def addFold(self
, player
):
1669 # addFold must be called when a player folds
1670 self
.contenders
.discard(player
)
1672 def addCommonMoney(self
, player
, amount
):
1673 self
.common
[player
] += amount
1675 def addMoney(self
, player
, amount
):
1676 # addMoney must be called for any actions that put money in the pot, in the order they occur
1677 self
.contenders
.add(player
)
1678 self
.committed
[player
] += amount
1680 def markTotal(self
, street
):
1681 self
.streettotals
[street
] = sum(self
.committed
.values()) + sum(self
.common
.values())
1683 def getTotalAtStreet(self
, street
):
1684 if street
in self
.streettotals
:
1685 return self
.streettotals
[street
]
1689 self
.total
= sum(self
.committed
.values()) + sum(self
.common
.values())
1691 # Return any uncalled bet.
1692 committed
= sorted([ (v
,k
) for (k
,v
) in self
.committed
.items()])
1693 #print "DEBUG: committed: %s" % committed
1694 #ERROR below. lastbet is correct in most cases, but wrong when
1695 # additional money is committed to the pot in cash games
1696 # due to an additional sb being posted. (Speculate that
1697 # posting sb+bb is also potentially wrong)
1698 lastbet
= committed
[-1][0] - committed
[-2][0]
1699 if lastbet
> 0: # uncalled
1700 returnto
= committed
[-1][1]
1701 #print "DEBUG: returning %f to %s" % (lastbet, returnto)
1702 self
.total
-= lastbet
1703 self
.committed
[returnto
] -= lastbet
1704 self
.returned
[returnto
] = lastbet
1707 # Work out side pots
1708 commitsall
= sorted([(v
,k
) for (k
,v
) in self
.committed
.items() if v
>0])
1712 while len(commitsall
) > 0:
1713 commitslive
= [(v
,k
) for (v
,k
) in commitsall
if k
in self
.contenders
]
1714 v1
= commitslive
[0][0]
1715 self
.pots
+= [sum([min(v
,v1
) for (v
,k
) in commitsall
])]
1716 commitsall
= [((v
-v1
),k
) for (v
,k
) in commitsall
if v
-v1
>0]
1717 except IndexError, e
:
1718 log
.error(_("Pot.end(): Major failure while calculating pot: '%s'") % e
)
1719 raise FpdbParseError(_("Pot.end(): Major failure while calculating pot: '%s'") % e
)
1721 # TODO: I think rake gets taken out of the pots.
1723 # total pot x. main pot y, side pot z. | rake r
1726 # Total pot $124.30 Main pot $98.90. Side pot $23.40. | Rake $2
1729 if self
.sym
is None:
1731 if self
.total
is None:
1732 print (_("DEBUG:") + " " + _("call Pot.end() before printing pot total"))
1733 # NB if I'm sure end() is idempotent, call it here.
1734 raise FpdbParseError(_("Error in printing Hand object"))
1736 ret
= "Total pot %s%.2f" % (self
.sym
, self
.total
)
1737 if len(self
.pots
) < 2:
1739 ret
+= " Main pot %s%.2f" % (self
.sym
, self
.pots
[0])
1741 return ret
+ ''.join([ (" Side pot %s%.2f." % (self
.sym
, self
.pots
[x
]) ) for x
in xrange(1, len(self
.pots
)) ])