Don't allow the start date to be later than the end date. If it is, modify whichever...
[fpdb-dooglus.git] / pyfpdb / DerivedStats.py
blob96d332eb1276ff0ed0411765e75f4a678de04b16
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 Card
20 from decimal_wrapper import Decimal
22 import logging
23 # logging has been set up in fpdb.py or HUD_main.py, use their settings:
24 log = logging.getLogger("parser")
26 class DerivedStats():
27 def __init__(self, hand):
28 self.hand = hand
30 self.hands = {}
31 self.handsplayers = {}
32 self.handsactions = {}
33 self._initStats = DerivedStats._buildStatsInitializer()
35 @staticmethod
36 def _buildStatsInitializer():
37 init = {}
38 #Init vars that may not be used, but still need to be inserted.
39 # All stud street4 need this when importing holdem
40 init['winnings'] = 0
41 init['rake'] = 0
42 init['totalProfit'] = 0
43 init['street4Aggr'] = False
44 init['wonWhenSeenStreet1'] = 0.0
45 init['sawShowdown'] = False
46 init['showed'] = False
47 init['wonAtSD'] = 0.0
48 init['startCards'] = 0
49 init['position'] = 2
50 init['street0_3BChance'] = False
51 init['street0_3BDone'] = False
52 init['street0_4BChance'] = False
53 init['street0_4BDone'] = False
54 init['street0_C4BChance'] = False
55 init['street0_C4BDone'] = False
56 init['street0_FoldTo3BChance']= False
57 init['street0_FoldTo3BDone']= False
58 init['street0_FoldTo4BChance']= False
59 init['street0_FoldTo4BDone']= False
60 init['street0_SqueezeChance']= False
61 init['street0_SqueezeDone'] = False
62 init['success_Steal'] = False
63 init['raiseToStealChance'] = False
64 init['raiseToStealDone'] = False
65 init['raiseFirstInChance'] = False
66 init['raisedFirstIn'] = False
67 init['foldBbToStealChance'] = False
68 init['foldSbToStealChance'] = False
69 init['foldedSbToSteal'] = False
70 init['foldedBbToSteal'] = False
71 init['tourneyTypeId'] = None
72 init['street1Seen'] = False
73 init['street2Seen'] = False
74 init['street3Seen'] = False
75 init['street4Seen'] = False
78 for i in range(5):
79 init['street%dCalls' % i] = 0
80 init['street%dBets' % i] = 0
81 init['street%dRaises' % i] = 0
82 for i in range(1,5):
83 init['street%dCBChance' %i] = False
84 init['street%dCBDone' %i] = False
85 init['street%dCheckCallRaiseChance' %i] = False
86 init['street%dCheckCallRaiseDone' %i] = False
87 init['otherRaisedStreet%d' %i] = False
88 init['foldToOtherRaisedStreet%d' %i] = False
90 #FIXME - Everything below this point is incomplete.
91 init['other3BStreet0'] = False
92 init['other4BStreet0'] = False
93 init['otherRaisedStreet0'] = False
94 init['foldToOtherRaisedStreet0'] = False
95 for i in range(1,5):
96 init['foldToStreet%dCBChance' %i] = False
97 init['foldToStreet%dCBDone' %i] = False
98 init['wonWhenSeenStreet2'] = 0.0
99 init['wonWhenSeenStreet3'] = 0.0
100 init['wonWhenSeenStreet4'] = 0.0
101 return init
103 def getStats(self, hand):
104 for player in hand.players:
105 self.handsplayers[player[1]] = self._initStats.copy()
107 self.assembleHands(self.hand)
108 self.assembleHandsPlayers(self.hand)
110 if self.hand.saveActions:
111 self.assembleHandsActions(self.hand)
113 def getHands(self):
114 return self.hands
116 def getHandsPlayers(self):
117 return self.handsplayers
119 def getHandsActions(self):
120 return self.handsactions
122 def assembleHands(self, hand):
123 self.hands['tableName'] = hand.tablename
124 self.hands['siteHandNo'] = hand.handid
125 self.hands['gametypeId'] = None # Leave None, handled later after checking db
126 self.hands['sessionId'] = None # Leave None, added later if caching sessions
127 self.hands['gameSessionId'] = None # Leave None, added later if caching sessions
128 self.hands['startTime'] = hand.startTime # format this!
129 self.hands['importTime'] = None
130 self.hands['seats'] = self.countPlayers(hand)
131 #self.hands['maxSeats'] = hand.maxseats
132 self.hands['texture'] = None # No calculation done for this yet.
133 self.hands['tourneyId'] = hand.tourneyId
135 # This (i think...) is correct for both stud and flop games, as hand.board['street'] disappears, and
136 # those values remain default in stud.
137 boardcards = []
138 for street in hand.communityStreets:
139 boardcards += hand.board[street]
140 boardcards += [u'0x', u'0x', u'0x', u'0x', u'0x']
141 cards = [Card.encodeCard(c) for c in boardcards[0:5]]
142 self.hands['boardcard1'] = cards[0]
143 self.hands['boardcard2'] = cards[1]
144 self.hands['boardcard3'] = cards[2]
145 self.hands['boardcard4'] = cards[3]
146 self.hands['boardcard5'] = cards[4]
148 self.hands['boards'] = []
149 self.hands['runItTwice'] = False
150 for i in range(hand.runItTimes):
151 self.hands['runItTwice'] = True
152 boardcards = []
153 for street in hand.communityStreets:
154 boardId = i+1
155 street_i = street + str(boardId)
156 if street_i in hand.board:
157 boardcards += hand.board[street_i]
158 boardcards = [u'0x', u'0x', u'0x', u'0x', u'0x'] + boardcards
159 cards = [Card.encodeCard(c) for c in boardcards[-5:]]
160 self.hands['boards'] += [[boardId] + cards]
162 #print "DEBUG: self.getStreetTotals = (%s, %s, %s, %s, %s)" % hand.getStreetTotals()
163 totals = hand.getStreetTotals()
164 totals = [int(100*i) for i in totals]
165 self.hands['street1Pot'] = totals[0]
166 self.hands['street2Pot'] = totals[1]
167 self.hands['street3Pot'] = totals[2]
168 self.hands['street4Pot'] = totals[3]
169 self.hands['showdownPot'] = totals[4]
171 self.vpip(hand) # Gives playersVpi (num of players vpip)
172 #print "DEBUG: vpip: %s" %(self.hands['playersVpi'])
173 self.playersAtStreetX(hand) # Gives playersAtStreet1..4 and Showdown
174 #print "DEBUG: playersAtStreet 1:'%s' 2:'%s' 3:'%s' 4:'%s'" %(self.hands['playersAtStreet1'],self.hands['playersAtStreet2'],self.hands['playersAtStreet3'],self.hands['playersAtStreet4'])
175 self.streetXRaises(hand)
177 def assembleHandsPlayers(self, hand):
178 #street0VPI/vpip already called in Hand
179 # sawShowdown is calculated in playersAtStreetX, as that calculation gives us a convenient list of names
181 #hand.players = [[seat, name, chips],[seat, name, chips]]
182 for player in hand.players:
183 self.handsplayers[player[1]]['seatNo'] = player[0]
184 self.handsplayers[player[1]]['startCash'] = int(100 * Decimal(player[2]))
185 self.handsplayers[player[1]]['sitout'] = False #TODO: implement actual sitout detection
186 if hand.gametype["type"]=="tour":
187 self.handsplayers[player[1]]['tourneyTypeId']=hand.tourneyTypeId
188 self.handsplayers[player[1]]['tourneysPlayersIds'] = hand.tourneysPlayersIds[player[1]]
189 else:
190 self.handsplayers[player[1]]['tourneysPlayersIds'] = None
191 if player[1] in hand.shown:
192 self.handsplayers[player[1]]['showed'] = True
194 #### seen now processed in playersAtStreetX()
195 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
196 #for i, street in enumerate(hand.actionStreets[2:], start=1):
197 #for i, street in enumerate(hand.actionStreets[2:]):
198 # self.seen(self.hand, i+1)
200 for i, street in enumerate(hand.actionStreets[1:]):
201 self.aggr(self.hand, i)
202 self.calls(self.hand, i)
203 self.bets(self.hand, i)
204 if i>0:
205 self.folds(self.hand, i)
207 # Winnings is a non-negative value of money collected from the pot, which already includes the
208 # rake taken out. hand.collectees is Decimal, database requires cents
209 for player in hand.collectees:
210 self.handsplayers[player]['winnings'] = int(100 * hand.collectees[player])
211 #FIXME: This is pretty dodgy, rake = hand.rake/#collectees
212 # You can really only pay rake when you collect money, but
213 # different sites calculate rake differently.
214 # Should be fine for split-pots, but won't be accurate for multi-way pots
215 self.handsplayers[player]['rake'] = int(100* hand.rake)/len(hand.collectees)
216 if self.handsplayers[player]['street1Seen'] == True:
217 self.handsplayers[player]['wonWhenSeenStreet1'] = 1.0
218 if self.handsplayers[player]['street2Seen'] == True:
219 self.handsplayers[player]['wonWhenSeenStreet2'] = 1.0
220 if self.handsplayers[player]['street3Seen'] == True:
221 self.handsplayers[player]['wonWhenSeenStreet3'] = 1.0
222 if self.handsplayers[player]['street4Seen'] == True:
223 self.handsplayers[player]['wonWhenSeenStreet4'] = 1.0
224 if self.handsplayers[player]['sawShowdown'] == True:
225 self.handsplayers[player]['wonAtSD'] = 1.0
227 for player in hand.pot.committed:
228 self.handsplayers[player]['totalProfit'] = int(self.handsplayers[player]['winnings'] - (100*hand.pot.committed[player])- (100*hand.pot.common[player]))
230 self.calcCBets(hand)
232 for player in hand.players:
233 hcs = hand.join_holecards(player[1], asList=True)
234 hcs = hcs + [u'0x']*18
235 #for i, card in enumerate(hcs[:20, 1): #Python 2.6 syntax
236 # self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card)
237 for i, card in enumerate(hcs[:20]):
238 self.handsplayers[player[1]]['card%s' % (i+1)] = Card.encodeCard(card)
239 self.handsplayers[player[1]]['startCards'] = Card.calcStartCards(hand, player[1])
241 self.setPositions(hand)
242 self.calcCheckCallRaise(hand)
243 self.calc34BetStreet0(hand)
244 self.calcSteals(hand)
245 # Additional stats
246 # 3betSB, 3betBB
247 # Squeeze, Ratchet?
249 def assembleHandsActions(self, hand):
250 k = 0
251 for i, street in enumerate(hand.actionStreets):
252 for j, act in enumerate(hand.actions[street]):
253 k += 1
254 self.handsactions[k] = {}
255 #default values
256 self.handsactions[k]['amount'] = 0
257 self.handsactions[k]['raiseTo'] = 0
258 self.handsactions[k]['amountCalled'] = 0
259 self.handsactions[k]['numDiscarded'] = 0
260 self.handsactions[k]['cardsDiscarded'] = None
261 self.handsactions[k]['allIn'] = False
262 #Insert values from hand.actions
263 self.handsactions[k]['player'] = act[0]
264 self.handsactions[k]['street'] = i-1
265 self.handsactions[k]['actionNo'] = k
266 self.handsactions[k]['streetActionNo'] = (j+1)
267 self.handsactions[k]['actionId'] = hand.ACTION[act[1]]
268 if act[1] not in ('discards') and len(act) > 2:
269 self.handsactions[k]['amount'] = int(100 * act[2])
270 if act[1] in ('raises', 'completes'):
271 self.handsactions[k]['raiseTo'] = int(100 * act[3])
272 self.handsactions[k]['amountCalled'] = int(100 * act[4])
273 if act[1] in ('discards'):
274 self.handsactions[k]['numDiscarded'] = int(act[2])
275 if act[1] in ('discards') and len(act) > 3:
276 self.handsactions[k]['cardsDiscarded'] = act[3]
277 if len(act) > 3 and act[1] not in ('discards'):
278 self.handsactions[k]['allIn'] = act[-1]
280 def setPositions(self, hand):
281 """Sets the position for each player in HandsPlayers
282 any blinds are negative values, and the last person to act on the
283 first betting round is 0
284 NOTE: HU, both values are negative for non-stud games
285 NOTE2: I've never seen a HU stud match"""
286 actions = hand.actions[hand.holeStreets[0]]
287 # Note: pfbao list may not include big blind if all others folded
288 players = self.pfbao(actions)
290 # set blinds first, then others from pfbao list, avoids problem if bb
291 # is missing from pfbao list or if there is no small blind
292 sb, bb, bi = False, False, False
293 if hand.gametype['base'] == 'stud':
294 # Stud position is determined after cards are dealt
295 bi = [x[0] for x in hand.actions[hand.actionStreets[1]] if x[1] == 'bringin']
296 else:
297 bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'big blind']
298 sb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'small blind']
300 # if there are > 1 sb or bb only the first is used!
301 if bb:
302 self.handsplayers[bb[0]]['position'] = 'B'
303 if bb[0] in players: players.remove(bb[0])
304 if sb:
305 self.handsplayers[sb[0]]['position'] = 'S'
306 if sb[0] in players: players.remove(sb[0])
307 if bi:
308 self.handsplayers[bi[0]]['position'] = 'S'
309 if bi[0] in players: players.remove(bi[0])
311 #print "DEBUG: bb: '%s' sb: '%s' bi: '%s' plyrs: '%s'" %(bb, sb, bi, players)
312 for i,player in enumerate(reversed(players)):
313 self.handsplayers[player]['position'] = i
315 def assembleHudCache(self, hand):
316 # No real work to be done - HandsPlayers data already contains the correct info
317 pass
319 def vpip(self, hand):
320 vpipers = set()
321 for act in hand.actions[hand.actionStreets[1]]:
322 if act[1] in ('calls','bets', 'raises', 'completes'):
323 vpipers.add(act[0])
325 self.hands['playersVpi'] = len(vpipers)
327 for player in hand.players:
328 if player[1] in vpipers:
329 self.handsplayers[player[1]]['street0VPI'] = True
330 else:
331 self.handsplayers[player[1]]['street0VPI'] = False
333 def playersAtStreetX(self, hand):
334 """ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */"""
335 # self.actions[street] is a list of all actions in a tuple, contining the player name first
336 # [ (player, action, ....), (player2, action, ...) ]
337 # The number of unique players in the list per street gives the value for playersAtStreetXXX
339 # FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
340 # ... new code below hopefully fixes this
341 # partly fixed, allins are now set as seeing streets because they never do a fold action
343 self.hands['playersAtStreet1'] = 0
344 self.hands['playersAtStreet2'] = 0
345 self.hands['playersAtStreet3'] = 0
346 self.hands['playersAtStreet4'] = 0
347 self.hands['playersAtShowdown'] = 0
349 # alliners = set()
350 # for (i, street) in enumerate(hand.actionStreets[2:]):
351 # actors = set()
352 # for action in hand.actions[street]:
353 # if len(action) > 2 and action[-1]: # allin
354 # alliners.add(action[0])
355 # actors.add(action[0])
356 # if len(actors)==0 and len(alliners)<2:
357 # alliners = set()
358 # self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors))
360 # actions = hand.actions[hand.actionStreets[-1]]
361 # print "p_actions:", self.pfba(actions), "p_folds:", self.pfba(actions, l=('folds',)), "alliners:", alliners
362 # pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners)
364 # hand.players includes people that are sitting out on some sites for cash games
365 # actionStreets[1] is 'DEAL', 'THIRD', 'PREFLOP', so any player dealt cards
366 # must act on this street if dealt cards. Almost certainly broken for the 'all-in blind' case
367 # and right now i don't care - CG
369 p_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]])
372 # discover who folded on each street and remove them from p_in
374 # i values as follows 0=BLINDSANTES 1=PREFLOP 2=FLOP 3=TURN 4=RIVER
375 # (for flop games)
377 # At the beginning of the loop p_in contains the players with cards
378 # at the start of that street.
379 # p_in is reduced each street to become a list of players still-in
380 # e.g. when i=1 (preflop) all players who folded during preflop
381 # are found by pfba() and eliminated from p_in.
382 # Therefore at the end of the loop, p_in contains players remaining
383 # at the end of the action on that street, and can therefore be set
384 # as the value for the number of players who saw the next street
386 # note that i is 1 in advance of the actual street numbers in the db
388 # if p_in reduces to 1 player, we must bomb-out immediately
389 # because the hand is over, this will ensure playersAtStreetx
390 # is accurate.
393 for (i, street) in enumerate(hand.actionStreets):
394 if (i-1) in (1,2,3,4):
395 # p_in stores players with cards at start of this street,
396 # so can set streetxSeen & playersAtStreetx with this information
397 # This hard-coded for i-1 =1,2,3,4 because those are the only columns
398 # in the db! this code section also replaces seen() - more info log 66
399 # nb i=2=flop=street1Seen, hence i-1 term needed
400 self.hands['playersAtStreet%d' % (i-1)] = len(p_in)
401 for player_with_cards in p_in:
402 self.handsplayers[player_with_cards]['street%sSeen' % (i-1)] = True
404 # find out who folded, and eliminate them from p_in
406 actions = hand.actions[street]
407 p_in = p_in - self.pfba(actions, l=('folds',))
409 # if everyone folded, we are done, so exit this method immediately
411 if len(p_in) == 1: return None
414 # The remaining players in p_in reached showdown (including all-ins
415 # because they never did a "fold" action in pfba() above)
417 self.hands['playersAtShowdown'] = len(p_in)
418 for showdown_player in p_in:
419 self.handsplayers[showdown_player]['sawShowdown'] = True
421 def streetXRaises(self, hand):
422 # self.actions[street] is a list of all actions in a tuple, contining the action as the second element
423 # [ (player, action, ....), (player2, action, ...) ]
424 # No idea what this value is actually supposed to be
425 # In theory its "num small bets paid to see flop/street4, including blind" which makes sense for limit. Not so useful for nl
426 # Leaving empty for the moment,
428 for i in range(5): self.hands['street%dRaises' % i] = 0
430 for (i, street) in enumerate(hand.actionStreets[1:]):
431 self.hands['street%dRaises' % i] = len(filter( lambda action: action[1] in ('raises','bets'), hand.actions[street]))
433 def calcSteals(self, hand):
434 """Fills raiseFirstInChance|raisedFirstIn, fold(Bb|Sb)ToSteal(Chance|)
436 Steal attempt - open raise on positions 1 0 S - i.e. CO, BU, SB
437 (note: I don't think PT2 counts SB steals in HU hands, maybe we shouldn't?)
438 Fold to steal - folding blind after steal attemp wo any other callers or raisers
440 steal_attempt = False
441 raised = False
442 steal_positions = (1, 0, 'S')
443 if hand.gametype['base'] == 'stud':
444 steal_positions = (2, 1, 0)
445 for action in hand.actions[hand.actionStreets[1]]:
446 pname, act = action[0], action[1]
447 posn = self.handsplayers[pname]['position']
448 #print "\naction:", action[0], posn, type(posn), steal_attempt, act
449 if posn == 'B':
450 #NOTE: Stud games will never hit this section
451 if steal_attempt:
452 self.handsplayers[pname]['foldBbToStealChance'] = True
453 self.handsplayers[pname]['raiseToStealChance'] = True
454 self.handsplayers[pname]['foldedBbToSteal'] = act == 'folds'
455 self.handsplayers[pname]['raiseToStealDone'] = act == 'raises'
456 self.handsplayers[stealer]['success_Steal'] = act == 'folds'
457 break
458 elif posn == 'S':
459 self.handsplayers[pname]['raiseToStealChance'] = steal_attempt
460 self.handsplayers[pname]['foldSbToStealChance'] = steal_attempt
461 self.handsplayers[pname]['foldedSbToSteal'] = steal_attempt and act == 'folds'
462 self.handsplayers[pname]['raiseToStealDone'] = steal_attempt and act == 'raises'
464 if steal_attempt and act != 'folds':
465 break
467 if not steal_attempt and not raised and not act in ('bringin'):
468 self.handsplayers[pname]['raiseFirstInChance'] = True
469 if act in ('bets', 'raises', 'completes'):
470 self.handsplayers[pname]['raisedFirstIn'] = True
471 raised = True
472 if posn in steal_positions:
473 steal_attempt = True
474 stealer = pname
475 if act == 'calls':
476 break
478 if posn not in steal_positions and act not in ('folds', 'bringin'):
479 break
481 def calc34BetStreet0(self, hand):
482 """Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0"""
483 bet_level = 1 # bet_level after 3-bet is equal to 3
484 squeeze_chance = False
485 for action in hand.actions[hand.actionStreets[1]]:
486 pname, act, aggr = action[0], action[1], action[1] in ('raises', 'bets')
487 if bet_level == 1:
488 if aggr:
489 first_agressor = pname
490 bet_level += 1
491 continue
492 elif bet_level == 2:
493 self.handsplayers[pname]['street0_3BChance'] = True
494 self.handsplayers[pname]['street0_SqueezeChance'] = squeeze_chance
495 if not squeeze_chance and act == 'calls':
496 squeeze_chance = True
497 continue
498 if aggr:
499 self.handsplayers[pname]['street0_3BDone'] = True
500 self.handsplayers[pname]['street0_SqueezeDone'] = squeeze_chance
501 second_agressor = pname
502 bet_level += 1
503 continue
504 elif bet_level == 3:
505 if pname == first_agressor:
506 self.handsplayers[pname]['street0_4BChance'] = True
507 self.handsplayers[pname]['street0_FoldTo3BChance'] = True
508 if aggr:
509 self.handsplayers[pname]['street0_4BDone'] = True
510 bet_level += 1
511 elif act == 'folds':
512 self.handsplayers[pname]['street0_FoldTo3BDone'] = True
513 break
514 else:
515 self.handsplayers[pname]['street0_C4BChance'] = True
516 if aggr:
517 self.handsplayers[pname]['street0_C4BDone'] = True
518 bet_level += 1
519 continue
520 elif bet_level == 4:
521 if pname != first_agressor:
522 self.handsplayers[pname]['street0_FoldTo4BChance'] = True
523 if act == 'folds':
524 self.handsplayers[pname]['street0_FoldTo4BDone'] = True
526 def calcCBets(self, hand):
527 """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance
529 Continuation Bet chance, action:
530 Had the last bet (initiative) on previous street, got called, close street action
531 Then no bets before the player with initiatives first action on current street
532 ie. if player on street-1 had initiative and no donkbets occurred
534 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
535 # came there
536 #for i, street in enumerate(hand.actionStreets[2:], start=1):
537 for i, street in enumerate(hand.actionStreets[2:]):
538 name = self.lastBetOrRaiser(hand.actionStreets[i+1])
539 if name:
540 chance = self.noBetsBefore(hand.actionStreets[i+2], name)
541 if chance == True:
542 self.handsplayers[name]['street%dCBChance' % (i+1)] = True
543 self.handsplayers[name]['street%dCBDone' % (i+1)] = self.betStreet(hand.actionStreets[i+2], name)
545 def calcCheckCallRaise(self, hand):
546 """Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone
548 streetXCheckCallRaiseChance = got raise/bet after check
549 streetXCheckCallRaiseDone = checked. got raise/bet. didn't fold
551 CG: CheckCall would be a much better name for this.
553 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
554 #for i, street in enumerate(hand.actionStreets[2:], start=1):
555 for i, street in enumerate(hand.actionStreets[2:]):
556 actions = hand.actions[hand.actionStreets[i+1]]
557 checkers = set()
558 initial_raiser = None
559 for action in actions:
560 pname, act = action[0], action[1]
561 if act in ('bets', 'raises') and initial_raiser is None:
562 initial_raiser = pname
563 elif act == 'checks' and initial_raiser is None:
564 checkers.add(pname)
565 elif initial_raiser is not None and pname in checkers:
566 self.handsplayers[pname]['street%dCheckCallRaiseChance' % (i+1)] = True
567 self.handsplayers[pname]['street%dCheckCallRaiseDone' % (i+1)] = act!='folds'
569 def aggr(self, hand, i):
570 aggrers = set()
571 others = set()
572 # Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street
574 firstAggrMade=False
575 for act in hand.actions[hand.actionStreets[i+1]]:
576 if firstAggrMade:
577 others.add(act[0])
578 if act[1] in ('completes', 'bets', 'raises'):
579 aggrers.add(act[0])
580 firstAggrMade=True
582 for player in hand.players:
583 #print "DEBUG: actionStreet[%s]: %s" %(hand.actionStreets[i+1], i)
584 if player[1] in aggrers:
585 self.handsplayers[player[1]]['street%sAggr' % i] = True
586 else:
587 self.handsplayers[player[1]]['street%sAggr' % i] = False
589 if len(aggrers)>0 and i>0:
590 for playername in others:
591 self.handsplayers[playername]['otherRaisedStreet%s' % i] = True
592 #print "otherRaised detected on handid "+str(hand.handid)+" for "+playername+" on street "+str(i)
594 if i > 0 and len(aggrers) > 0:
595 for playername in others:
596 self.handsplayers[playername]['otherRaisedStreet%s' % i] = True
597 #print "DEBUG: otherRaised detected on handid %s for %s on actionStreet[%s]: %s"
598 # %(hand.handid, playername, hand.actionStreets[i+1], i)
600 def calls(self, hand, i):
601 callers = []
602 for act in hand.actions[hand.actionStreets[i+1]]:
603 if act[1] in ('calls'):
604 self.handsplayers[act[0]]['street%sCalls' % i] = 1 + self.handsplayers[act[0]]['street%sCalls' % i]
606 # CG - I'm sure this stat is wrong
607 # Best guess is that raise = 2 bets
608 def bets(self, hand, i):
609 for act in hand.actions[hand.actionStreets[i+1]]:
610 if act[1] in ('bets'):
611 self.handsplayers[act[0]]['street%sBets' % i] = 1 + self.handsplayers[act[0]]['street%sBets' % i]
613 def folds(self, hand, i):
614 for act in hand.actions[hand.actionStreets[i+1]]:
615 if act[1] in ('folds'):
616 if self.handsplayers[act[0]]['otherRaisedStreet%s' % i] == True:
617 self.handsplayers[act[0]]['foldToOtherRaisedStreet%s' % i] = True
618 #print "DEBUG: fold detected on handid %s for %s on actionStreet[%s]: %s"
619 # %(hand.handid, act[0],hand.actionStreets[i+1], i)
621 def countPlayers(self, hand):
622 pass
624 def pfba(self, actions, f=None, l=None):
625 """Helper method. Returns set of PlayersFilteredByActions
627 f - forbidden actions
628 l - limited to actions
630 players = set()
631 for action in actions:
632 if l is not None and action[1] not in l: continue
633 if f is not None and action[1] in f: continue
634 players.add(action[0])
635 return players
637 def pfbao(self, actions, f=None, l=None, unique=True):
638 """Helper method. Returns set of PlayersFilteredByActionsOrdered
640 f - forbidden actions
641 l - limited to actions
643 # Note, this is an adaptation of function 5 from:
644 # http://www.peterbe.com/plog/uniqifiers-benchmark
645 seen = {}
646 players = []
647 for action in actions:
648 if l is not None and action[1] not in l: continue
649 if f is not None and action[1] in f: continue
650 if action[0] in seen and unique: continue
651 seen[action[0]] = 1
652 players.append(action[0])
653 return players
655 def firstsBetOrRaiser(self, actions):
656 """Returns player name that placed the first bet or raise.
658 None if there were no bets or raises on that street
660 for act in actions:
661 if act[1] in ('bets', 'raises'):
662 return act[0]
663 return None
665 def lastBetOrRaiser(self, street):
666 """Returns player name that placed the last bet or raise for that street.
667 None if there were no bets or raises on that street"""
668 lastbet = None
669 for act in self.hand.actions[street]:
670 if act[1] in ('bets', 'raises'):
671 lastbet = act[0]
672 return lastbet
675 def noBetsBefore(self, street, player):
676 """Returns true if there were no bets before the specified players turn, false otherwise"""
677 betOrRaise = False
678 for act in self.hand.actions[street]:
679 #Must test for player first in case UTG
680 if act[0] == player:
681 betOrRaise = True
682 break
683 if act[1] in ('bets', 'raises'):
684 break
685 return betOrRaise
688 def betStreet(self, street, player):
689 """Returns true if player bet/raised the street as their first action"""
690 betOrRaise = False
691 for act in self.hand.actions[street]:
692 if act[0] == player:
693 if act[1] in ('bets', 'raises'):
694 betOrRaise = True
695 else:
696 # player found but did not bet or raise as their first action
697 pass
698 break
699 #else:
700 # haven't found player's first action yet
701 return betOrRaise