Use debian 2.7 only
[fpbd-bostik.git] / pyfpdb / DerivedStats.py
blob9be9222921fc49584feb6549afd3fea6a61b0ca0
1 #!/usr/bin/env python
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.
18 #fpdb modules
19 import L10n
20 _ = L10n.get_translation()
21 import Card
22 from decimal_wrapper import Decimal
24 import sys
25 import logging
26 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
27 log = logging.getLogger("parser")
29 try:
30 from pokereval import PokerEval
31 pokereval = PokerEval()
32 except:
33 pokereval = None
35 class DerivedStats():
36 def __init__(self, hand):
37 self.hand = hand
39 self.hands = {}
40 self.handsplayers = {}
41 self.handsactions = {}
42 self.handsstove = []
43 self._initStats = DerivedStats._buildStatsInitializer()
45 @staticmethod
46 def _buildStatsInitializer():
47 init = {}
48 #Init vars that may not be used, but still need to be inserted.
49 # All stud street4 need this when importing holdem
50 init['winnings'] = 0
51 init['rake'] = 0
52 init['totalProfit'] = 0
53 init['allInEV'] = 0
54 init['street4Aggr'] = False
55 init['wonWhenSeenStreet1'] = 0.0
56 init['sawShowdown'] = False
57 init['showed'] = False
58 init['wonAtSD'] = 0.0
59 init['startCards'] = 0
60 init['position'] = 2
61 init['street0CalledRaiseChance'] = 0
62 init['street0CalledRaiseDone'] = 0
63 init['street0_3BChance'] = False
64 init['street0_3BDone'] = False
65 init['street0_4BChance'] = False
66 init['street0_4BDone'] = False
67 init['street0_C4BChance'] = False
68 init['street0_C4BDone'] = False
69 init['street0_FoldTo3BChance']= False
70 init['street0_FoldTo3BDone']= False
71 init['street0_FoldTo4BChance']= False
72 init['street0_FoldTo4BDone']= False
73 init['street0_SqueezeChance']= False
74 init['street0_SqueezeDone'] = False
75 init['success_Steal'] = False
76 init['raiseToStealChance'] = False
77 init['raiseToStealDone'] = False
78 init['raiseFirstInChance'] = False
79 init['raisedFirstIn'] = False
80 init['foldBbToStealChance'] = False
81 init['foldSbToStealChance'] = False
82 init['foldedSbToSteal'] = False
83 init['foldedBbToSteal'] = False
84 init['tourneyTypeId'] = None
85 init['street1Seen'] = False
86 init['street2Seen'] = False
87 init['street3Seen'] = False
88 init['street4Seen'] = False
91 for i in range(5):
92 init['street%dCalls' % i] = 0
93 init['street%dBets' % i] = 0
94 init['street%dRaises' % i] = 0
95 for i in range(1,5):
96 init['street%dCBChance' %i] = False
97 init['street%dCBDone' %i] = False
98 init['street%dCheckCallRaiseChance' %i] = False
99 init['street%dCheckCallRaiseDone' %i] = False
100 init['otherRaisedStreet%d' %i] = False
101 init['foldToOtherRaisedStreet%d' %i] = False
103 #FIXME - Everything below this point is incomplete.
104 init['other3BStreet0'] = False
105 init['other4BStreet0'] = False
106 init['otherRaisedStreet0'] = False
107 init['foldToOtherRaisedStreet0'] = False
108 for i in range(1,5):
109 init['foldToStreet%dCBChance' %i] = False
110 init['foldToStreet%dCBDone' %i] = False
111 init['wonWhenSeenStreet2'] = 0.0
112 init['wonWhenSeenStreet3'] = 0.0
113 init['wonWhenSeenStreet4'] = 0.0
114 return init
116 def getStats(self, hand):
117 for player in hand.players:
118 self.handsplayers[player[1]] = self._initStats.copy()
120 self.assembleHands(self.hand)
121 self.assembleHandsPlayers(self.hand)
123 if self.hand.saveActions:
124 self.assembleHandsActions(self.hand)
126 if pokereval:
127 if self.hand.gametype['category'] in Card.games:
128 self.assembleHandsStove(self.hand)
130 def getHands(self):
131 return self.hands
133 def getHandsPlayers(self):
134 return self.handsplayers
136 def getHandsActions(self):
137 return self.handsactions
139 def getHandsStove(self):
140 return self.handsstove
142 def assembleHands(self, hand):
143 self.hands['tableName'] = hand.tablename
144 self.hands['siteHandNo'] = hand.handid
145 self.hands['gametypeId'] = None # Leave None, handled later after checking db
146 self.hands['sessionId'] = None # Leave None, added later if caching sessions
147 self.hands['gameId'] = None # Leave None, added later if caching sessions
148 self.hands['startTime'] = hand.startTime # format this!
149 self.hands['importTime'] = None
150 self.hands['seats'] = self.countPlayers(hand)
151 #self.hands['maxSeats'] = hand.maxseats
152 self.hands['texture'] = None # No calculation done for this yet.
153 self.hands['tourneyId'] = hand.tourneyId
155 # This (i think...) is correct for both stud and flop games, as hand.board['street'] disappears, and
156 # those values remain default in stud.
157 boardcards = []
158 for street in hand.communityStreets:
159 boardcards += hand.board[street]
160 boardcards += [u'0x', u'0x', u'0x', u'0x', u'0x']
161 cards = [Card.encodeCard(c) for c in boardcards[0:5]]
162 self.hands['boardcard1'] = cards[0]
163 self.hands['boardcard2'] = cards[1]
164 self.hands['boardcard3'] = cards[2]
165 self.hands['boardcard4'] = cards[3]
166 self.hands['boardcard5'] = cards[4]
168 self.hands['boards'] = []
169 self.hands['runItTwice'] = False
170 for i in range(hand.runItTimes):
171 self.hands['runItTwice'] = True
172 boardcards = []
173 for street in hand.communityStreets:
174 boardId = i+1
175 street_i = street + str(boardId)
176 if street_i in hand.board:
177 boardcards += hand.board[street_i]
178 boardcards = [u'0x', u'0x', u'0x', u'0x', u'0x'] + boardcards
179 cards = [Card.encodeCard(c) for c in boardcards[-5:]]
180 self.hands['boards'] += [[boardId] + cards]
182 #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % hand.getStreetTotals()
183 totals = hand.getStreetTotals()
184 totals = [int(100*i) for i in totals]
185 self.hands['street1Pot'] = totals[0]
186 self.hands['street2Pot'] = totals[1]
187 self.hands['street3Pot'] = totals[2]
188 self.hands['street4Pot'] = totals[3]
189 self.hands['showdownPot'] = totals[4]
191 self.vpip(hand) # Gives playersVpi (num of players vpip)
192 #print "DEBUG: vpip: %s" %(self.hands['playersVpi'])
193 self.playersAtStreetX(hand) # Gives playersAtStreet1..4 and Showdown
194 #print "DEBUG: playersAtStreet 1:'%s' 2:'%s' 3:'%s' 4:'%s'" %(self.hands['playersAtStreet1'],self.hands['playersAtStreet2'],self.hands['playersAtStreet3'],self.hands['playersAtStreet4'])
195 self.streetXRaises(hand)
197 def assembleHandsPlayers(self, hand):
198 #street0VPI/vpip already called in Hand
199 # sawShowdown is calculated in playersAtStreetX, as that calculation gives us a convenient list of names
201 #hand.players = [[seat, name, chips],[seat, name, chips]]
202 for player in hand.players:
203 self.handsplayers[player[1]]['seatNo'] = player[0]
204 self.handsplayers[player[1]]['startCash'] = int(100 * Decimal(player[2]))
205 self.handsplayers[player[1]]['sitout'] = False #TODO: implement actual sitout detection
206 if hand.gametype["type"]=="tour":
207 self.handsplayers[player[1]]['tourneyTypeId']=hand.tourneyTypeId
208 self.handsplayers[player[1]]['tourneysPlayersIds'] = hand.tourneysPlayersIds[player[1]]
209 else:
210 self.handsplayers[player[1]]['tourneysPlayersIds'] = None
211 if player[1] in hand.shown:
212 self.handsplayers[player[1]]['showed'] = True
214 #### seen now processed in playersAtStreetX()
215 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
216 #for i, street in enumerate(hand.actionStreets[2:], start=1):
217 #for i, street in enumerate(hand.actionStreets[2:]):
218 # self.seen(self.hand, i+1)
220 for i, street in enumerate(hand.actionStreets[1:]):
221 self.aggr(self.hand, i)
222 self.calls(self.hand, i)
223 self.bets(self.hand, i)
224 if i>0:
225 self.folds(self.hand, i)
227 # Winnings is a non-negative value of money collected from the pot, which already includes the
228 # rake taken out. hand.collectees is Decimal, database requires cents
229 for player in hand.collectees:
230 self.handsplayers[player]['winnings'] = int(100 * hand.collectees[player])
231 #FIXME: This is pretty dodgy, rake = hand.rake/#collectees
232 # You can really only pay rake when you collect money, but
233 # different sites calculate rake differently.
234 # Should be fine for split-pots, but won't be accurate for multi-way pots
235 self.handsplayers[player]['rake'] = int(100* hand.rake)/len(hand.collectees)
236 if self.handsplayers[player]['street1Seen'] == True:
237 self.handsplayers[player]['wonWhenSeenStreet1'] = 1.0
238 if self.handsplayers[player]['street2Seen'] == True:
239 self.handsplayers[player]['wonWhenSeenStreet2'] = 1.0
240 if self.handsplayers[player]['street3Seen'] == True:
241 self.handsplayers[player]['wonWhenSeenStreet3'] = 1.0
242 if self.handsplayers[player]['street4Seen'] == True:
243 self.handsplayers[player]['wonWhenSeenStreet4'] = 1.0
244 if self.handsplayers[player]['sawShowdown'] == True:
245 self.handsplayers[player]['wonAtSD'] = 1.0
247 for player in hand.pot.committed:
248 self.handsplayers[player]['totalProfit'] = int(self.handsplayers[player]['winnings'] - (100*hand.pot.committed[player])- (100*hand.pot.common[player]))
249 if hand.gametype['type'] == 'ring' and pokereval:
250 self.handsplayers[player]['allInEV'] = self.handsplayers[player]['totalProfit']
252 self.calcCBets(hand)
254 for player in hand.players:
255 hcs = hand.join_holecards(player[1], asList=True)
256 hcs = hcs + [u'0x']*18
257 #for i, card in enumerate(hcs[:20, 1): #Python 2.6 syntax
258 # self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card)
259 for i, card in enumerate(hcs[:20]):
260 self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card)
261 self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1])
263 self.setPositions(hand)
264 self.calcCheckCallRaise(hand)
265 self.calc34BetStreet0(hand)
266 self.calcSteals(hand)
267 self.calcCalledRaiseStreet0(hand)
268 # Additional stats
269 # 3betSB, 3betBB
270 # Squeeze, Ratchet?
272 def assembleHandsActions(self, hand):
273 k = 0
274 for i, street in enumerate(hand.actionStreets):
275 for j, act in enumerate(hand.actions[street]):
276 k += 1
277 self.handsactions[k] = {}
278 #default values
279 self.handsactions[k]['amount'] = 0
280 self.handsactions[k]['raiseTo'] = 0
281 self.handsactions[k]['amountCalled'] = 0
282 self.handsactions[k]['numDiscarded'] = 0
283 self.handsactions[k]['cardsDiscarded'] = None
284 self.handsactions[k]['allIn'] = False
285 #Insert values from hand.actions
286 self.handsactions[k]['player'] = act[0]
287 self.handsactions[k]['street'] = i-1
288 self.handsactions[k]['actionNo'] = k
289 self.handsactions[k]['streetActionNo'] = (j+1)
290 self.handsactions[k]['actionId'] = hand.ACTION[act[1]]
291 if act[1] not in ('discards') and len(act) > 2:
292 self.handsactions[k]['amount'] = int(100 * act[2])
293 if act[1] in ('raises', 'completes'):
294 self.handsactions[k]['raiseTo'] = int(100 * act[3])
295 self.handsactions[k]['amountCalled'] = int(100 * act[4])
296 if act[1] in ('discards'):
297 self.handsactions[k]['numDiscarded'] = int(act[2])
298 if act[1] in ('discards') and len(act) > 3:
299 self.handsactions[k]['cardsDiscarded'] = act[3]
300 if len(act) > 3 and act[1] not in ('discards'):
301 self.handsactions[k]['allIn'] = act[-1]
303 def assembleHandsStove(self, hand):
304 game = Card.games[hand.gametype['category']]
305 holecards, holeplayers, streets, boards, boardcards, inserts_temp, allInStreets = {}, [], {}, {}, [], [], []
306 defaultStreet = {'board': [[]], 'allin': False}
307 showdown = False
308 for player in hand.players:
309 if (self.handsplayers[player[1]]['sawShowdown']):
310 showdown = True
311 if game[0] == 'hold':
312 allInStreets = hand.communityStreets
313 boards['FLOP'] = {'board': [hand.board['FLOP']], 'allin': False}
314 boards['TURN'] = {'board': [hand.board['FLOP'] + hand.board['TURN']], 'allin': False}
315 boards['RIVER'] = {'board': [hand.board['FLOP'] + hand.board['TURN'] + hand.board['RIVER']], 'allin': False}
316 for street in hand.communityStreets:
317 boardcards += hand.board[street]
318 if not hand.actions[street] and showdown:
319 if street=='FLOP':
320 allInStreets = ['PREFLOP'] + allInStreets
321 boards['PREFLOP'] = {'board': [[]], 'allin': True}
322 else:
323 id = Card.streets[game[0]][street]
324 boards[hand.actionStreets[id]]['allin'] = True
325 boards[street]['allin'] = True
326 flop, turn, river = [], [], []
327 for i in range(hand.runItTimes):
328 runitcards = []
329 for street in hand.communityStreets:
330 street_i = street + str((i+1))
331 if street_i in hand.board:
332 runitcards += hand.board[street_i]
333 if len(boardcards + runitcards)==3: flop.append(boardcards + runitcards)
334 if len(boardcards + runitcards)==4: turn.append(boardcards + runitcards)
335 if len(boardcards + runitcards)==5: river.append(boardcards + runitcards)
336 if flop: boards['FLOP']['board'] = flop
337 if turn: boards['TURN']['board'] = turn
338 if river: boards['RIVER']['board'] = river
339 for player in hand.players:
340 hole, cards, bcards = [], [], []
341 best_lo, locards, lostring = None, None, None
342 best_hi, hicards, histring = None, None, None
343 if (self.handsplayers[player[1]]['sawShowdown']) or player[1]==hand.hero:
344 if (hand.gametype['category'] != 'badugi' and
345 hand.gametype['category'] != 'razz'):
346 hcs = hand.join_holecards(player[1], asList=True)
347 if game[0] == 'hold':
348 if 'omaha' in game[1]:
349 hole = hcs[:4]
350 else:
351 hole = hcs[:2]
352 last = 'RIVER'
353 elif game[0] == 'stud':
354 hole = hcs[:7]
355 boards['SEVENTH'] = defaultStreet
356 last = 'SEVENTH'
357 elif game[0] == 'draw':
358 if u'0x' in hcs[-5:]:
359 hole = hcs[5:10]
360 else:
361 hole = hcs[-5:]
362 if hand.gametype['category']=='27_1draw' or hand.gametype['category']=='fivedraw':
363 boards['DRAWONE'] = defaultStreet
364 last = 'DRAWONE'
365 else:
366 boards['DRAWTHREE'] = defaultStreet
367 last = 'DRAWTHREE'
369 holecards[player[1]] = {}
370 holecards[player[1]]['hole'] = [str(c) for c in hole]
371 holecards[player[1]]['cards'] = []
372 holecards[player[1]]['eq'] = 0
373 holecards[player[1]]['committed'] = 0
374 holeplayers.append(player[1])
376 for street, board in boards.iteritems():
377 streetId = Card.streets[game[0]][street]
378 if (board['allin'] or player[1]==hand.hero or self.handsplayers[player[1]]['sawShowdown']):
379 boardId = 0
380 for n in range(len(board['board'])):
381 if len(board['board']) > 1:
382 boardId = n + 1
383 else: boardId = n
384 cards = [str(c) for c in hole]
385 if board['board'][n]: bcards = [str(b) for b in board['board'][n]]
386 else : bcards = []
387 histring, lostring, histringold, lostringold, lostringvalue, histringvalue, winnings = None, None, None, None, 0, 0, 0
388 if 'omaha' not in game[1]:
389 if board['board'][n]:
390 cards = hole + board['board'][n]
391 cards = [str(c) for c in cards]
392 bcards = []
393 holecards[player[1]]['cards'] += [cards]
394 if (u'0x' not in cards and 'Nu' not in cards) and ((game[0] == 'hold' and len(board['board'][n])>=3) or
395 (game[0] == 'stud' and len(cards)==7) or (game[0] == 'draw' and len(cards)==5)):
396 if game[2] == 'h':
397 best_hi = pokereval.best_hand("hi", cards, bcards)
398 hicards = [pokereval.card2string(i) for i in best_hi[1:]]
399 histring = Card.hands['hi'][best_hi[0]]
400 elif game[2] == 'l':
401 best_lo = pokereval.best_hand("low", cards, bcards)
402 best_lo_r = pokereval.best_hand("hi", cards, bcards)
403 locards = [pokereval.card2string(i) for i in best_lo[1:]]
404 locards_r = [pokereval.card2string(i) for i in best_lo_r[1:]]
405 lostring = Card.hands['lo'][best_lo[0]]
406 elif game[2] == 's':
407 best_hi = pokereval.best_hand("hi", cards, bcards)
408 hicards = [pokereval.card2string(i) for i in best_hi[1:]]
409 histring = Card.hands['hi'][best_hi[0]]
410 best_lo = pokereval.best_hand("low", cards, bcards)
411 locards = [pokereval.card2string(i) for i in best_lo[1:]]
412 lostring = Card.hands['lo'][best_lo[0]]
413 elif game[2] == 'r':
414 best_lo = pokereval.best_hand("hi", cards, bcards)
415 locards = [pokereval.card2string(i) for i in best_lo[1:]]
416 if locards[4] in ('As', 'Ad', 'Ah', 'Ac'):
417 locards = [locards[4]] + locards[1:]
418 lostring = Card.hands['hi'][best_lo[0]]
420 if lostring:
421 lostring = self.getHandString('lo', lostring, locards, best_lo)
422 lostringvalue = pokereval.best_hand_value("lo", cards, bcards)
423 winnings = self.handsplayers[player[1]]['winnings']
424 lostringold = lostring
425 if street == last:
426 for j in range(len(inserts_temp)):
427 if ((boardId == inserts_temp[j][3]) and (lostring == inserts_temp[j][7]) and
428 (lostringvalue != inserts_temp[j][9]) and (lostring is not None) and (winnings>0) and
429 (streetId == inserts_temp[j][2]) and (hand.dbid_pids[player[1]] != inserts_temp[j][1])):
430 loappend = ' - lower kicker'
431 if lostringvalue < inserts_temp[j][9]:
432 lostring += loappend
433 else:
434 if loappend not in inserts_temp[j][5] and inserts_temp[k][10]>0:
435 inserts_temp[j][5] += loappend
436 if histring:
437 histring = self.getHandString('hi', histring, hicards, best_hi)
438 histringvalue = pokereval.best_hand_value("hi", cards, bcards)
439 winnings = self.handsplayers[player[1]]['winnings']
440 histringold = histring
441 if street == last:
442 for k in range(len(inserts_temp)):
443 if ((boardId == inserts_temp[k][3]) and (histring == inserts_temp[k][6]) and
444 (histringvalue != inserts_temp[k][8]) and (histring is not None) and (winnings>0) and
445 (streetId == inserts_temp[k][2]) and (hand.dbid_pids[player[1]] != inserts_temp[k][1])
446 and ('flush' not in histring) and ('straight' not in histring) and ('full house' not in histring)):
447 hiappend = ' - higher kicker'
448 if histringvalue > inserts_temp[k][8]:
449 histring += hiappend
450 else:
451 if hiappend not in inserts_temp[k][4] and inserts_temp[k][10]>0:
452 inserts_temp[k][4] += hiappend
453 inserts_temp.append( [hand.dbid_hands,
454 hand.dbid_pids[player[1]],
455 streetId,
456 boardId,
457 histring,
458 lostring,
459 histringold,
460 lostringold,
461 histringvalue,
462 lostringvalue,
463 winnings
465 elif (self.handsplayers[player[1]]['sawShowdown']):
466 if game[0]=='stud': streetId = 4
467 if game[0]=='draw': streetId = 3
468 if player[1] in hand.showdownStrings:
469 lostring = hand.showdownStrings[player[1]]
470 else:
471 lostring = ''
472 self.handsstove.append( [
473 hand.dbid_hands,
474 hand.dbid_pids[player[1]],
475 streetId,
477 histring,
478 lostring,
481 self.handsstove += [t[:6] + [0] for t in inserts_temp]
482 startstreet = None
483 for pot, players in hand.pot.pots:
484 players = [p for p in players]
485 for street in allInStreets:
486 board = boards[street]
487 tid = Card.streets[game[0]][street]
488 for n in range(len(board['board'])):
489 if len(board['board']) > 1:
490 bid = n + 1
491 portion = 2
492 else:
493 bid = n
494 portion = 1
495 if board['allin']:
496 if len([p for p in players
497 if p in holecards
498 and u'0x' not in holecards[p]['cards'][n]
499 and 'Nu' not in holecards[p]['cards'][n]]) > 0:
500 if not startstreet: startstreet = street
501 bcards = [str(b) for b in board['board'][n]]
502 b = bcards + (5 - len(board['board'][n])) * ['__']
503 holeshow = [holecards[p]['hole'] for p in players
504 if self.handsplayers[p]['sawShowdown']
505 and u'0x' not in holecards[p]['cards'][n]
506 and 'Nu' not in holecards[p]['cards'][n]]
507 if len(holeshow)> 1:
508 evs = pokereval.poker_eval(game = game[1]
509 ,iterations = Card.iter[tid]
510 ,pockets = holeshow
511 ,dead = []
512 ,board = b)
513 equities = [e['ev'] for e in evs['eval']]
514 else:
515 equities = [1000]
516 for i in range(len(equities)):
517 for j in self.handsstove:
518 p = players[i]
519 pid = hand.dbid_pids[p]
520 if ((j[1] == pid) and (j[2] == tid) and (j[3] == bid)):
521 if len(players) == len(hand.pot.contenders): j[6] = equities[i]
522 if street == startstreet and hand.gametype['type'] == 'ring':
523 rake = (hand.rake * (pot/hand.totalpot))
524 holecards[p]['eq'] += int(((100*pot - 100*rake) * Decimal(equities[i])/1000)/portion)
525 holecards[p]['committed'] = int(((100*hand.pot.committed[p]) + (100*hand.pot.common[p]))/portion)
527 for p in holeplayers:
528 if holecards[p]['committed'] != 0:
529 self.handsplayers[p]['allInEV'] = holecards[p]['eq'] - holecards[p]['committed']
531 def getHandString(self, type, string, cards, best):
532 if best[0] == 'Nothing':
533 string, cards = None, None
534 elif best[0] == 'NoPair':
535 if type == 'lo':
536 string = cards[0]+','+cards[1]+','+cards[2]+','+cards[3]+','+cards[4]
537 else:
538 highcard = Card.names[cards[0][0]][0]
539 string = string % highcard
540 elif best[0] == 'OnePair':
541 pair = Card.names[cards[0][0]][1]
542 string = string % pair
543 elif best[0] == 'TwoPair':
544 hipair = Card.names[cards[0][0]][1]
545 pair = Card.names[cards[2][0]][1]
546 pairs = _("%s and %s") % (hipair, pair)
547 string = string % pairs
548 elif best[0] == 'Trips':
549 threeoak = Card.names[cards[0][0]][1]
550 string = string % threeoak
551 elif best[0] == 'Straight':
552 straight = Card.names[cards[0][0]][0] + " " + _("high")
553 string = string % straight
554 elif best[0] == 'Flush':
555 flush = Card.names[cards[0][0]][0] + " " + _("high")
556 string = string % flush
557 elif best[0] == 'FlHouse':
558 threeoak = Card.names[cards[0][0]][1]
559 pair = Card.names[cards[3][0]][1]
560 full = _("%s full of %s") % (threeoak, pair)
561 string = string % full
562 elif best[0] == 'Quads':
563 four = Card.names[cards[0][0]][1]
564 string = string % four
565 elif best[0] == 'StFlush':
566 flush = Card.names[cards[0][0]][0] + " " + _("high")
567 string = string % flush
568 if string[0] in ('As', 'Ad', 'Ah', 'Ac'):
569 string = _('a Royal Flush')
570 return string
572 def setPositions(self, hand):
573 """Sets the position for each player in HandsPlayers
574 any blinds are negative values, and the last person to act on the
575 first betting round is 0
576 NOTE: HU, both values are negative for non-stud games
577 NOTE2: I've never seen a HU stud match"""
578 actions = hand.actions[hand.holeStreets[0]]
579 # Note: pfbao list may not include big blind if all others folded
580 players = self.pfbao(actions)
582 # set blinds first, then others from pfbao list, avoids problem if bb
583 # is missing from pfbao list or if there is no small blind
584 sb, bb, bi = False, False, False
585 if hand.gametype['base'] == 'stud':
586 # Stud position is determined after cards are dealt
587 bi = [x[0] for x in hand.actions[hand.actionStreets[1]] if x[1] == 'bringin']
588 else:
589 bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'big blind']
590 sb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'small blind']
592 # if there are > 1 sb or bb only the first is used!
593 if bb:
594 self.handsplayers[bb[0]]['position'] = 'B'
595 if bb[0] in players: players.remove(bb[0])
596 if sb:
597 self.handsplayers[sb[0]]['position'] = 'S'
598 if sb[0] in players: players.remove(sb[0])
599 if bi:
600 self.handsplayers[bi[0]]['position'] = 'S'
601 if bi[0] in players: players.remove(bi[0])
603 #print "DEBUG: bb: '%s' sb: '%s' bi: '%s' plyrs: '%s'" %(bb, sb, bi, players)
604 for i,player in enumerate(reversed(players)):
605 self.handsplayers[player]['position'] = i
607 def assembleHudCache(self, hand):
608 # No real work to be done - HandsPlayers data already contains the correct info
609 pass
611 def vpip(self, hand):
612 vpipers = set()
613 for act in hand.actions[hand.actionStreets[1]]:
614 if act[1] in ('calls','bets', 'raises', 'completes'):
615 vpipers.add(act[0])
617 self.hands['playersVpi'] = len(vpipers)
619 for player in hand.players:
620 if player[1] in vpipers:
621 self.handsplayers[player[1]]['street0VPI'] = True
622 else:
623 self.handsplayers[player[1]]['street0VPI'] = False
625 def playersAtStreetX(self, hand):
626 """ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */"""
627 # self.actions[street] is a list of all actions in a tuple, contining the player name first
628 # [ (player, action, ....), (player2, action, ...) ]
629 # The number of unique players in the list per street gives the value for playersAtStreetXXX
631 # FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
632 # ... new code below hopefully fixes this
633 # partly fixed, allins are now set as seeing streets because they never do a fold action
635 self.hands['playersAtStreet1'] = 0
636 self.hands['playersAtStreet2'] = 0
637 self.hands['playersAtStreet3'] = 0
638 self.hands['playersAtStreet4'] = 0
639 self.hands['playersAtShowdown'] = 0
641 # alliners = set()
642 # for (i, street) in enumerate(hand.actionStreets[2:]):
643 # actors = set()
644 # for action in hand.actions[street]:
645 # if len(action) > 2 and action[-1]: # allin
646 # alliners.add(action[0])
647 # actors.add(action[0])
648 # if len(actors)==0 and len(alliners)<2:
649 # alliners = set()
650 # self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors))
652 # actions = hand.actions[hand.actionStreets[-1]]
653 # print "p_actions:", self.pfba(actions), "p_folds:", self.pfba(actions, l=('folds',)), "alliners:", alliners
654 # pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners)
656 # hand.players includes people that are sitting out on some sites for cash games
657 # actionStreets[1] is 'DEAL', 'THIRD', 'PREFLOP', so any player dealt cards
658 # must act on this street if dealt cards. Almost certainly broken for the 'all-in blind' case
659 # and right now i don't care - CG
661 p_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]])
664 # discover who folded on each street and remove them from p_in
666 # i values as follows 0=BLINDSANTES 1=PREFLOP 2=FLOP 3=TURN 4=RIVER
667 # (for flop games)
669 # At the beginning of the loop p_in contains the players with cards
670 # at the start of that street.
671 # p_in is reduced each street to become a list of players still-in
672 # e.g. when i=1 (preflop) all players who folded during preflop
673 # are found by pfba() and eliminated from p_in.
674 # Therefore at the end of the loop, p_in contains players remaining
675 # at the end of the action on that street, and can therefore be set
676 # as the value for the number of players who saw the next street
678 # note that i is 1 in advance of the actual street numbers in the db
680 # if p_in reduces to 1 player, we must bomb-out immediately
681 # because the hand is over, this will ensure playersAtStreetx
682 # is accurate.
685 for (i, street) in enumerate(hand.actionStreets):
686 if (i-1) in (1,2,3,4):
687 # p_in stores players with cards at start of this street,
688 # so can set streetxSeen & playersAtStreetx with this information
689 # This hard-coded for i-1 =1,2,3,4 because those are the only columns
690 # in the db! this code section also replaces seen() - more info log 66
691 # nb i=2=flop=street1Seen, hence i-1 term needed
692 self.hands['playersAtStreet%d' % (i-1)] = len(p_in)
693 for player_with_cards in p_in:
694 self.handsplayers[player_with_cards]['street%sSeen' % (i-1)] = True
696 # find out who folded, and eliminate them from p_in
698 actions = hand.actions[street]
699 p_in = p_in - self.pfba(actions, l=('folds',))
701 # if everyone folded, we are done, so exit this method immediately
703 if len(p_in) == 1: return None
706 # The remaining players in p_in reached showdown (including all-ins
707 # because they never did a "fold" action in pfba() above)
709 self.hands['playersAtShowdown'] = len(p_in)
710 for showdown_player in p_in:
711 self.handsplayers[showdown_player]['sawShowdown'] = True
713 def streetXRaises(self, hand):
714 # self.actions[street] is a list of all actions in a tuple, contining the action as the second element
715 # [ (player, action, ....), (player2, action, ...) ]
716 # No idea what this value is actually supposed to be
717 # In theory its "num small bets paid to see flop/street4, including blind" which makes sense for limit. Not so useful for nl
718 # Leaving empty for the moment,
720 for i in range(5): self.hands['street%dRaises' % i] = 0
722 for (i, street) in enumerate(hand.actionStreets[1:]):
723 self.hands['street%dRaises' % i] = len(filter( lambda action: action[1] in ('raises','bets'), hand.actions[street]))
725 def calcSteals(self, hand):
726 """Fills raiseFirstInChance|raisedFirstIn, fold(Bb|Sb)ToSteal(Chance|)
728 Steal attempt - open raise on positions 1 0 S - i.e. CO, BU, SB
729 (note: I don't think PT2 counts SB steals in HU hands, maybe we shouldn't?)
730 Fold to steal - folding blind after steal attemp wo any other callers or raisers
732 steal_attempt = False
733 raised = False
734 steal_positions = (1, 0, 'S')
735 if hand.gametype['base'] == 'stud':
736 steal_positions = (2, 1, 0)
737 for action in hand.actions[hand.actionStreets[1]]:
738 pname, act = action[0], action[1]
739 posn = self.handsplayers[pname]['position']
740 #print "\naction:", action[0], posn, type(posn), steal_attempt, act
741 if posn == 'B':
742 #NOTE: Stud games will never hit this section
743 if steal_attempt:
744 self.handsplayers[pname]['foldBbToStealChance'] = True
745 self.handsplayers[pname]['raiseToStealChance'] = True
746 self.handsplayers[pname]['foldedBbToSteal'] = act == 'folds'
747 self.handsplayers[pname]['raiseToStealDone'] = act == 'raises'
748 self.handsplayers[stealer]['success_Steal'] = act == 'folds'
749 break
750 elif posn == 'S':
751 self.handsplayers[pname]['raiseToStealChance'] = steal_attempt
752 self.handsplayers[pname]['foldSbToStealChance'] = steal_attempt
753 self.handsplayers[pname]['foldedSbToSteal'] = steal_attempt and act == 'folds'
754 self.handsplayers[pname]['raiseToStealDone'] = steal_attempt and act == 'raises'
756 if steal_attempt and act != 'folds':
757 break
759 if not steal_attempt and not raised and not act in ('bringin'):
760 self.handsplayers[pname]['raiseFirstInChance'] = True
761 if act in ('bets', 'raises', 'completes'):
762 self.handsplayers[pname]['raisedFirstIn'] = True
763 raised = True
764 if posn in steal_positions:
765 steal_attempt = True
766 stealer = pname
767 if act == 'calls':
768 break
770 if posn not in steal_positions and act not in ('folds', 'bringin'):
771 break
773 def calc34BetStreet0(self, hand):
774 """Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0"""
775 bet_level = 1 # bet_level after 3-bet is equal to 3
776 squeeze_chance = False
777 for action in hand.actions[hand.actionStreets[1]]:
778 pname, act, aggr = action[0], action[1], action[1] in ('raises', 'bets')
779 if bet_level == 1:
780 if aggr:
781 first_agressor = pname
782 bet_level += 1
783 continue
784 elif bet_level == 2:
785 self.handsplayers[pname]['street0_3BChance'] = True
786 self.handsplayers[pname]['street0_SqueezeChance'] = squeeze_chance
787 if not squeeze_chance and act == 'calls':
788 squeeze_chance = True
789 continue
790 if aggr:
791 self.handsplayers[pname]['street0_3BDone'] = True
792 self.handsplayers[pname]['street0_SqueezeDone'] = squeeze_chance
793 second_agressor = pname
794 bet_level += 1
795 continue
796 elif bet_level == 3:
797 if pname == first_agressor:
798 self.handsplayers[pname]['street0_4BChance'] = True
799 self.handsplayers[pname]['street0_FoldTo3BChance'] = True
800 if aggr:
801 self.handsplayers[pname]['street0_4BDone'] = True
802 bet_level += 1
803 elif act == 'folds':
804 self.handsplayers[pname]['street0_FoldTo3BDone'] = True
805 break
806 else:
807 self.handsplayers[pname]['street0_C4BChance'] = True
808 if aggr:
809 self.handsplayers[pname]['street0_C4BDone'] = True
810 bet_level += 1
811 continue
812 elif bet_level == 4:
813 if pname != first_agressor:
814 self.handsplayers[pname]['street0_FoldTo4BChance'] = True
815 if act == 'folds':
816 self.handsplayers[pname]['street0_FoldTo4BDone'] = True
818 def calcCBets(self, hand):
819 """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance
821 Continuation Bet chance, action:
822 Had the last bet (initiative) on previous street, got called, close street action
823 Then no bets before the player with initiatives first action on current street
824 ie. if player on street-1 had initiative and no donkbets occurred
826 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
827 # came there
828 #for i, street in enumerate(hand.actionStreets[2:], start=1):
829 for i, street in enumerate(hand.actionStreets[2:]):
830 name = self.lastBetOrRaiser(hand.actionStreets[i+1])
831 if name:
832 chance = self.noBetsBefore(hand.actionStreets[i+2], name)
833 if chance == True:
834 self.handsplayers[name]['street%dCBChance' % (i+1)] = True
835 self.handsplayers[name]['street%dCBDone' % (i+1)] = self.betStreet(hand.actionStreets[i+2], name)
836 if self.handsplayers[name]['street%dCBDone' % (i+1)]:
837 for pname, folds in self.foldTofirstsBetOrRaiser(street, name).iteritems():
838 #print "DEBUG: hand.handid, pname.encode('utf8'), street, folds, '--', name, 'lastbet on ', hand.actionStreets[i+1]
839 self.handsplayers[pname]['foldToStreet%sCBChance' % (i+1)] = True
840 self.handsplayers[pname]['foldToStreet%sCBDone' % (i+1)] = folds
842 def calcCalledRaiseStreet0(self, hand):
844 Fill street0CalledRaiseChance, street0CalledRaiseDone
845 For flop games, go through the preflop actions:
846 skip through first raise
847 For each subsequent action:
848 if the next action is fold :
849 player chance + 1
850 if the next action is raise :
851 player chance + 1
852 if the next non-fold action is call :
853 player chance + 1
854 player done + 1
855 skip through list to the next raise action
858 if hand.gametype['base'] <> 'hold':
859 return
861 fast_forward = True
862 for tupleread in hand.actions[hand.actionStreets[1]]:
863 action = tupleread[1]
864 if fast_forward:
865 if action == 'raises':
866 fast_forward = False # raisefound, end fast-forward
867 else:
868 player = tupleread[0]
869 self.handsplayers[player]['street0CalledRaiseChance'] += 1
870 if action == 'calls':
871 self.handsplayers[player]['street0CalledRaiseDone'] += 1
872 fast_forward = True
874 def calcCheckCallRaise(self, hand):
875 """Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone
877 streetXCheckCallRaiseChance = got raise/bet after check
878 streetXCheckCallRaiseDone = checked. got raise/bet. didn't fold
880 CG: CheckCall would be a much better name for this.
882 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
883 #for i, street in enumerate(hand.actionStreets[2:], start=1):
884 for i, street in enumerate(hand.actionStreets[2:]):
885 actions = hand.actions[hand.actionStreets[i+1]]
886 checkers = set()
887 initial_raiser = None
888 for action in actions:
889 pname, act = action[0], action[1]
890 if act in ('bets', 'raises') and initial_raiser is None:
891 initial_raiser = pname
892 elif act == 'checks' and initial_raiser is None:
893 checkers.add(pname)
894 elif initial_raiser is not None and pname in checkers:
895 self.handsplayers[pname]['street%dCheckCallRaiseChance' % (i+1)] = True
896 self.handsplayers[pname]['street%dCheckCallRaiseDone' % (i+1)] = act!='folds'
898 def aggr(self, hand, i):
899 aggrers = set()
900 others = set()
901 # Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street
903 firstAggrMade=False
904 for act in hand.actions[hand.actionStreets[i+1]]:
905 if firstAggrMade:
906 others.add(act[0])
907 if act[1] in ('completes', 'bets', 'raises'):
908 aggrers.add(act[0])
909 firstAggrMade=True
911 for player in hand.players:
912 #print "DEBUG: actionStreet[%s]: %s" %(hand.actionStreets[i+1], i)
913 if player[1] in aggrers:
914 self.handsplayers[player[1]]['street%sAggr' % i] = True
915 else:
916 self.handsplayers[player[1]]['street%sAggr' % i] = False
918 if len(aggrers)>0 and i>0:
919 for playername in others:
920 self.handsplayers[playername]['otherRaisedStreet%s' % i] = True
921 #print "otherRaised detected on handid "+str(hand.handid)+" for "+playername+" on street "+str(i)
923 if i > 0 and len(aggrers) > 0:
924 for playername in others:
925 self.handsplayers[playername]['otherRaisedStreet%s' % i] = True
926 #print "DEBUG: otherRaised detected on handid %s for %s on actionStreet[%s]: %s"
927 # %(hand.handid, playername, hand.actionStreets[i+1], i)
929 def calls(self, hand, i):
930 callers = []
931 for act in hand.actions[hand.actionStreets[i+1]]:
932 if act[1] in ('calls'):
933 self.handsplayers[act[0]]['street%sCalls' % i] = 1 + self.handsplayers[act[0]]['street%sCalls' % i]
935 # CG - I'm sure this stat is wrong
936 # Best guess is that raise = 2 bets
937 def bets(self, hand, i):
938 for act in hand.actions[hand.actionStreets[i+1]]:
939 if act[1] in ('bets'):
940 self.handsplayers[act[0]]['street%sBets' % i] = 1 + self.handsplayers[act[0]]['street%sBets' % i]
942 def folds(self, hand, i):
943 for act in hand.actions[hand.actionStreets[i+1]]:
944 if act[1] in ('folds'):
945 if self.handsplayers[act[0]]['otherRaisedStreet%s' % i] == True:
946 self.handsplayers[act[0]]['foldToOtherRaisedStreet%s' % i] = True
947 #print "DEBUG: fold detected on handid %s for %s on actionStreet[%s]: %s"
948 # %(hand.handid, act[0],hand.actionStreets[i+1], i)
950 def countPlayers(self, hand):
951 pass
953 def pfba(self, actions, f=None, l=None):
954 """Helper method. Returns set of PlayersFilteredByActions
956 f - forbidden actions
957 l - limited to actions
959 players = set()
960 for action in actions:
961 if l is not None and action[1] not in l: continue
962 if f is not None and action[1] in f: continue
963 players.add(action[0])
964 return players
966 def pfbao(self, actions, f=None, l=None, unique=True):
967 """Helper method. Returns set of PlayersFilteredByActionsOrdered
969 f - forbidden actions
970 l - limited to actions
972 # Note, this is an adaptation of function 5 from:
973 # http://www.peterbe.com/plog/uniqifiers-benchmark
974 seen = {}
975 players = []
976 for action in actions:
977 if l is not None and action[1] not in l: continue
978 if f is not None and action[1] in f: continue
979 if action[0] in seen and unique: continue
980 seen[action[0]] = 1
981 players.append(action[0])
982 return players
984 def firstsBetOrRaiser(self, actions):
985 """Returns player name that placed the first bet or raise.
987 None if there were no bets or raises on that street
989 for act in actions:
990 if act[1] in ('bets', 'raises'):
991 return act[0]
992 return None
994 def foldTofirstsBetOrRaiser(self, street, aggressor):
995 """Returns player name that placed the first bet or raise.
997 None if there were no bets or raises on that street
999 i, players = 0, {}
1000 for act in self.hand.actions[street]:
1001 if i>1: break
1002 if act[0] != aggressor:
1003 if act[1] == 'folds':
1004 players[act[0]] = True
1005 else:
1006 players[act[0]] = False
1007 if act[1] == 'raises': break
1008 else:
1009 i+=1
1010 return players
1012 def lastBetOrRaiser(self, street):
1013 """Returns player name that placed the last bet or raise for that street.
1014 None if there were no bets or raises on that street"""
1015 lastbet = None
1016 for act in self.hand.actions[street]:
1017 if act[1] in ('bets', 'raises'):
1018 lastbet = act[0]
1019 return lastbet
1022 def noBetsBefore(self, street, player):
1023 """Returns true if there were no bets before the specified players turn, false otherwise"""
1024 betOrRaise = False
1025 for act in self.hand.actions[street]:
1026 #Must test for player first in case UTG
1027 if act[0] == player:
1028 betOrRaise = True
1029 break
1030 if act[1] in ('bets', 'raises'):
1031 break
1032 return betOrRaise
1035 def betStreet(self, street, player):
1036 """Returns true if player bet/raised the street as their first action"""
1037 betOrRaise = False
1038 for act in self.hand.actions[street]:
1039 if act[0] == player:
1040 if act[1] in ('bets', 'raises'):
1041 betOrRaise = True
1042 else:
1043 # player found but did not bet or raise as their first action
1044 pass
1045 break
1046 #else:
1047 # haven't found player's first action yet
1048 return betOrRaise