Coverage for DerivedStats.py: 13%
1010 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-14 11:07 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-14 11:07 +0000
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.
8#
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.
13#
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
19from __future__ import division
21from past.utils import old_div
23# import L10n
24# _ = L10n.get_translation()
25import Card
26from decimal import Decimal, ROUND_DOWN
28import logging
30try:
31 from pokereval import PokerEval
33 pokereval = PokerEval()
34except Exception:
35 pokereval = None
38# logging has been set up in fpdb.py or HUD_main.py, use their settings:
39log = logging.getLogger("parser")
42def _buildStatsInitializer():
43 init = {}
44 # Init vars that may not be used, but still need to be inserted.
45 # All stud street4 need this when importing holdem
46 init["effStack"] = 0
47 init["startBounty"] = None
48 init["endBounty"] = None
49 init["common"] = 0
50 init["committed"] = 0
51 init["winnings"] = 0
52 init["rake"] = 0
53 init["rakeDealt"] = 0
54 init["rakeContributed"] = 0
55 init["rakeWeighted"] = 0
56 init["totalProfit"] = 0
57 init["allInEV"] = 0
58 init["showdownWinnings"] = 0
59 init["nonShowdownWinnings"] = 0
60 init["sawShowdown"] = False
61 init["showed"] = False
62 init["wonAtSD"] = False
63 init["startCards"] = 170
64 init["handString"] = None
65 init["position"] = 9 # ANTE ALL IN
66 init["street0CalledRaiseChance"] = 0
67 init["street0CalledRaiseDone"] = 0
68 init["street0VPIChance"] = True
69 init["street0VPI"] = False
70 init["street0AggrChance"] = True
71 init["street0_2BChance"] = False
72 init["street0_2BDone"] = False
73 init["street0_3BChance"] = False
74 init["street0_3BDone"] = False
75 init["street0_4BChance"] = False
76 init["street0_4BDone"] = False
77 init["street0_C4BChance"] = False
78 init["street0_C4BDone"] = False
79 init["street0_FoldTo2BChance"] = False
80 init["street0_FoldTo2BDone"] = False
81 init["street0_FoldTo3BChance"] = False
82 init["street0_FoldTo3BDone"] = False
83 init["street0_FoldTo4BChance"] = False
84 init["street0_FoldTo4BDone"] = False
85 init["street0_SqueezeChance"] = False
86 init["street0_SqueezeDone"] = False
87 init["stealChance"] = False
88 init["stealDone"] = False
89 init["success_Steal"] = False
90 init["raiseToStealChance"] = False
91 init["raiseToStealDone"] = False
92 init["raiseFirstInChance"] = False
93 init["raisedFirstIn"] = False
94 init["foldBbToStealChance"] = False
95 init["foldSbToStealChance"] = False
96 init["foldedSbToSteal"] = False
97 init["foldedBbToSteal"] = False
98 init["tourneyTypeId"] = None
99 init["street1Seen"] = False
100 init["street2Seen"] = False
101 init["street3Seen"] = False
102 init["street4Seen"] = False
103 init["otherRaisedStreet0"] = False
104 init["foldToOtherRaisedStreet0"] = False
105 init["wentAllIn"] = False
107 for i in range(5):
108 init["street%dCalls" % i] = 0
109 init["street%dBets" % i] = 0
110 init["street%dRaises" % i] = 0
111 init["street%dAggr" % i] = False
112 init["street%dInPosition" % i] = False
113 init["street%dFirstToAct" % i] = False
114 init["street%dAllIn" % i] = False
116 for i in range(1, 4):
117 init["street%dDiscards" % i] = 0
119 for i in range(1, 5):
120 init["street%dCBChance" % i] = False
121 init["street%dCBDone" % i] = False
122 init["street%dCheckCallRaiseChance" % i] = False
123 init["street%dCheckCallDone" % i] = False
124 init["street%dCheckRaiseDone" % i] = False
125 init["otherRaisedStreet%d" % i] = False
126 init["foldToOtherRaisedStreet%d" % i] = False
127 init["foldToStreet%dCBChance" % i] = False
128 init["foldToStreet%dCBDone" % i] = False
129 init["wonWhenSeenStreet%d" % i] = False
130 return init
133_INIT_STATS = _buildStatsInitializer()
136class DerivedStats(object):
137 def __init__(self):
138 self.hands = {}
139 self.handsplayers = {}
140 self.handsactions = {}
141 self.handsstove = []
142 self.handspots = []
144 def getStats(self, hand):
145 for player in hand.players:
146 self.handsplayers[player[1]] = _INIT_STATS.copy()
148 self.assembleHands(hand)
149 self.assembleHandsPlayers(hand)
150 self.assembleHandsActions(hand)
152 if pokereval and hand.gametype["category"] in Card.games:
153 self.assembleHandsStove(hand)
154 self.assembleHandsPots(hand)
156 def getHands(self):
157 return self.hands
159 def getHandsPlayers(self):
160 return self.handsplayers
162 def getHandsActions(self):
163 return self.handsactions
165 def getHandsStove(self):
166 return self.handsstove
168 def getHandsPots(self):
169 return self.handspots
171 def assembleHands(self, hand):
172 self.hands["tableName"] = hand.tablename
173 self.hands["siteHandNo"] = hand.handid
174 self.hands["gametypeId"] = None # Leave None, handled later after checking db
175 self.hands["sessionId"] = None # Leave None, added later if caching sessions
176 self.hands["gameId"] = None # Leave None, added later if caching sessions
177 self.hands["startTime"] = hand.startTime # format this!
178 self.hands["importTime"] = None
179 self.hands["seats"] = self.countPlayers(hand)
180 self.hands["maxPosition"] = -1
181 # self.hands['maxSeats'] = hand.maxseats
182 self.hands["texture"] = None # No calculation done for this yet.
183 self.hands["tourneyId"] = hand.tourneyId
185 self.hands["heroSeat"] = 0
186 for player in hand.players:
187 if hand.hero == player[1]:
188 self.hands["heroSeat"] = player[0]
189 # This (i think...) is correct for both stud and flop games, as hand.board['street'] disappears, and
190 # those values remain default in stud.
191 boardcards = []
192 if hand.board.get("FLOPET") is not None:
193 boardcards += hand.board.get("FLOPET")
194 for street in hand.communityStreets:
195 boardcards += hand.board[street]
196 boardcards += ["0x", "0x", "0x", "0x", "0x"]
197 cards = [Card.encodeCard(c) for c in boardcards[0:5]]
198 self.hands["boardcard1"] = cards[0]
199 self.hands["boardcard2"] = cards[1]
200 self.hands["boardcard3"] = cards[2]
201 self.hands["boardcard4"] = cards[3]
202 self.hands["boardcard5"] = cards[4]
204 # print "cards: ",cards
206 self.hands["boards"] = []
207 self.hands["runItTwice"] = False
208 for i in range(hand.runItTimes):
209 boardcards = []
210 for street in hand.communityStreets:
211 boardId = i + 1
212 street_i = street + str(boardId)
213 if street_i in hand.board:
214 boardcards += hand.board[street_i]
215 if hand.gametype["split"]:
216 boardcards = boardcards + ["0x", "0x", "0x", "0x", "0x"]
217 cards = [Card.encodeCard(c) for c in boardcards[:5]]
218 else:
219 self.hands["runItTwice"] = True
220 boardcards = ["0x", "0x", "0x", "0x", "0x"] + boardcards
221 cards = [Card.encodeCard(c) for c in boardcards[-5:]]
222 self.hands["boards"] += [[boardId] + cards]
224 # print "DEBUG: %s self.getStreetTotals = (%s, %s, %s, %s, %s, %s)" % tuple([hand.handid] + list(hand.getStreetTotals()))
225 totals = hand.getStreetTotals()
226 totals = [int(100 * i) for i in totals]
227 self.hands["street0Pot"] = totals[0]
228 self.hands["street1Pot"] = totals[1]
229 self.hands["street2Pot"] = totals[2]
230 self.hands["street3Pot"] = totals[3]
231 self.hands["street4Pot"] = totals[4]
232 self.hands["finalPot"] = totals[5]
234 self.vpip(hand) # Gives playersVpi (num of players vpip)
235 # print "DEBUG: vpip: %s" %(self.hands['playersVpi'])
236 self.playersAtStreetX(hand) # Gives playersAtStreet1..4 and Showdown
237 # print "DEBUG: playersAtStreet 1:'%s' 2:'%s' 3:'%s' 4:'%s'" %(self.hands['playersAtStreet1'],self.hands['playersAtStreet2'],self.hands['playersAtStreet3'],self.hands['playersAtStreet4'])
238 self.streetXRaises(hand)
240 def assembleHandsPlayers(self, hand):
241 # street0VPI/vpip already called in Hand
242 # sawShowdown is calculated in playersAtStreetX, as that calculation gives us a convenient list of names
244 # hand.players = [[seat, name, chips],[seat, name, chips]]
245 for player in hand.players:
246 player_name = player[1]
247 player_stats = self.handsplayers.get(player_name)
248 player_stats["seatNo"] = player[0]
249 player_stats["startCash"] = int(100 * Decimal(player[2]))
250 if player[4] is not None:
251 player_stats["startBounty"] = int(100 * Decimal(player[4]))
252 player_stats["endBounty"] = int(100 * Decimal(player[4]))
253 if player_name in hand.endBounty:
254 player_stats["endBounty"] = int(hand.endBounty.get(player_name))
255 if player_name in hand.sitout:
256 player_stats["sitout"] = True
257 else:
258 player_stats["sitout"] = False
259 if hand.gametype["type"] == "tour":
260 player_stats["tourneyTypeId"] = hand.tourneyTypeId
261 player_stats["tourneysPlayersId"] = hand.tourneysPlayersIds[player[1]]
262 else:
263 player_stats["tourneysPlayersId"] = None
264 if player_name in hand.shown:
265 player_stats["showed"] = True
267 #### seen now processed in playersAtStreetX()
268 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
269 # for i, street in enumerate(hand.actionStreets[2:], start=1):
270 # for i, street in enumerate(hand.actionStreets[2:]):
271 # self.seen(self.hand, i+1)
273 for i, street in enumerate(hand.actionStreets[1:]):
274 self.aggr(hand, i)
275 self.calls(hand, i)
276 self.bets(hand, i)
277 self.raises(hand, i)
278 if i > 0:
279 self.folds(hand, i)
281 # Winnings is a non-negative value of money collected from the pot, which already includes the
282 # rake taken out. hand.collectees is Decimal, database requires cents
283 num_collectees, i = len(hand.collectees), 0
284 even_split = old_div(hand.totalpot, num_collectees) if num_collectees > 0 else 0
285 unraked = [c for c in list(hand.collectees.values()) if even_split == c]
286 for player, winnings in list(hand.collectees.items()):
287 collectee_stats = self.handsplayers.get(player)
288 collectee_stats["winnings"] = int(100 * winnings)
289 # Splits evenly on split pots and gives remainder to first player
290 # Gets overwritten when calculating multi-way pots in assembleHandsPots
291 if num_collectees == 0:
292 collectee_stats["rake"] = 0
293 elif len(unraked) == 0:
294 rake = old_div(int(100 * hand.rake), num_collectees)
295 remainder_1, remainder_2 = 0, 0
296 if rake > 0 and i == 0:
297 leftover = int(100 * hand.rake) - (rake * num_collectees)
298 remainder_1 = int(100 * hand.rake) % rake
299 remainder_2 = leftover if remainder_1 == 0 else 0
300 collectee_stats["rake"] = rake + remainder_1 + remainder_2
301 else:
302 collectee_stats["rake"] = int(100 * (even_split - winnings))
303 if collectee_stats["street1Seen"] is True:
304 collectee_stats["wonWhenSeenStreet1"] = True
305 if collectee_stats["street2Seen"] is True:
306 collectee_stats["wonWhenSeenStreet2"] = True
307 if collectee_stats["street3Seen"] is True:
308 collectee_stats["wonWhenSeenStreet3"] = True
309 if collectee_stats["street4Seen"] is True:
310 collectee_stats["wonWhenSeenStreet4"] = True
311 if collectee_stats["sawShowdown"] is True:
312 collectee_stats["wonAtSD"] = True
313 i += 1
315 contributed, i = [], 0
316 for player, money_committed in list(hand.pot.committed.items()):
317 committed_player_stats = self.handsplayers.get(player)
318 paid = (100 * money_committed) + (100 * hand.pot.common[player])
319 committed_player_stats["common"] = int(100 * hand.pot.common[player])
320 committed_player_stats["committed"] = int(100 * money_committed)
321 committed_player_stats["totalProfit"] = int(committed_player_stats["winnings"] - paid)
322 committed_player_stats["allInEV"] = committed_player_stats["totalProfit"]
323 committed_player_stats["rakeDealt"] = 100 * hand.rake / len(hand.players)
324 committed_player_stats["rakeWeighted"] = (
325 100 * hand.rake * paid / (100 * hand.totalpot) if hand.rake > 0 else 0
326 )
327 if paid > 0:
328 contributed.append(player)
329 i += 1
331 for i, player in enumerate(contributed):
332 self.handsplayers[player]["rakeContributed"] = 100 * hand.rake / len(contributed)
334 self.calcCBets(hand)
336 # More inner-loop speed hackery.
337 encodeCard = Card.encodeCard
338 calcStartCards = Card.calcStartCards
339 for player in hand.players:
340 player_name = player[1]
341 hcs = hand.join_holecards(player_name, asList=True)
342 hcs = hcs + ["0x"] * 18
343 # for i, card in enumerate(hcs[:20, 1): #Python 2.6 syntax
344 # self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card)
345 player_stats = self.handsplayers.get(player_name)
346 if player_stats["sawShowdown"]:
347 player_stats["showdownWinnings"] = player_stats["totalProfit"]
348 else:
349 player_stats["nonShowdownWinnings"] = player_stats["totalProfit"]
350 for i, card in enumerate(hcs[:20]):
351 player_stats["card%d" % (i + 1)] = encodeCard(card)
352 try:
353 player_stats["startCards"] = calcStartCards(hand, player_name)
354 except IndexError:
355 log.error("IndexError: string index out of range %s %s" % (hand.handid, hand.in_path))
357 self.setPositions(hand)
358 self.calcEffectiveStack(hand)
359 self.calcCheckCallRaise(hand)
360 self.calc34BetStreet0(hand)
361 self.calcSteals(hand)
362 self.calcCalledRaiseStreet0(hand)
363 # Additional stats
364 # 3betSB, 3betBB
365 # Squeeze, Ratchet?
367 def assembleHandsActions(self, hand):
368 k = 0
369 for i, street in enumerate(hand.actionStreets):
370 for j, act in enumerate(hand.actions[street]):
371 k += 1
372 self.handsactions[k] = {}
373 # default values
374 self.handsactions[k]["amount"] = 0
375 self.handsactions[k]["raiseTo"] = 0
376 self.handsactions[k]["amountCalled"] = 0
377 self.handsactions[k]["numDiscarded"] = 0
378 self.handsactions[k]["cardsDiscarded"] = None
379 self.handsactions[k]["allIn"] = False
380 # Insert values from hand.actions
381 self.handsactions[k]["player"] = act[0]
382 self.handsactions[k]["street"] = i - 1
383 self.handsactions[k]["actionNo"] = k
384 self.handsactions[k]["streetActionNo"] = j + 1
385 self.handsactions[k]["actionId"] = hand.ACTION[act[1]]
386 if act[1] not in ("discards") and len(act) > 2:
387 self.handsactions[k]["amount"] = int(100 * act[2])
388 if act[1] in ("raises", "completes"):
389 self.handsactions[k]["raiseTo"] = int(100 * act[3])
390 self.handsactions[k]["amountCalled"] = int(100 * act[4])
391 if act[1] in ("discards"):
392 self.handsactions[k]["numDiscarded"] = int(act[2])
393 self.handsplayers[act[0]]["street%dDiscards" % (i - 1)] = int(act[2])
394 if act[1] in ("discards") and len(act) > 3:
395 self.handsactions[k]["cardsDiscarded"] = act[3]
396 if len(act) > 3 and act[1] not in ("discards"):
397 self.handsactions[k]["allIn"] = act[-1]
398 if act[-1]:
399 self.handsplayers[act[0]]["wentAllIn"] = True
400 self.handsplayers[act[0]]["street%dAllIn" % (i - 1)] = True
402 def assembleHandsStove(self, hand):
403 category = hand.gametype["category"]
404 # holecards, holeplayers, allInStreets = {}, [], hand.allStreets[1:]
405 holecards, holeplayers = {}, []
406 base, evalgame, hilo, streets, last, hrange = Card.games[category]
407 hiLoKey = {"h": [("h", "hi")], "l": [("l", "low")], "s": [("h", "hi"), ("l", "low")], "r": [("l", "hi")]}
408 boards = self.getBoardsDict(hand, base, streets)
409 for player in hand.players:
410 pname = player[1]
411 hp = self.handsplayers.get(pname)
412 if evalgame:
413 hcs = hand.join_holecards(pname, asList=True)
414 holecards[pname] = {}
415 holecards[pname]["cards"] = []
416 holecards[pname]["eq"] = 0
417 holecards[pname]["committed"] = 0
418 holeplayers.append(pname)
419 for street, board in list(boards.items()):
420 streetId = streets[street]
421 if streetId > 0:
422 streetSeen = hp["street%sSeen" % str(streetId)]
423 else:
424 streetSeen = True
425 if (pname == hand.hero and streetSeen) or (hp["showed"] and streetSeen) or hp["sawShowdown"]:
426 boardId, hl, rankId, value, _cards = 0, "n", 1, 0, None
427 for n in range(len(board["board"])):
428 streetIdx = -1 if base == "hold" else streetId
429 cards = hcs[hrange[streetIdx][0] : hrange[streetIdx][1]]
430 boardId = (n + 1) if (len(board["board"]) > 1) else n
431 cards += board["board"][n] if (board["board"][n] and "omaha" not in evalgame) else []
432 bcards = board["board"][n] if (board["board"][n] and "omaha" in evalgame) else []
433 cards = [str(c) if Card.encodeCardList.get(c) else "0x" for c in cards]
434 bcards = [str(b) if Card.encodeCardList.get(b) else "0x" for b in bcards]
435 holecards[pname]["hole"] = cards[hrange[streetIdx][0] : hrange[streetIdx][1]]
436 holecards[pname]["cards"] += [cards]
437 notnull = ("0x" not in cards) and ("0x" not in bcards)
438 postflop = base == "hold" and len(board["board"][n]) >= 3
439 maxcards = base != "hold" and len(cards) >= 5
440 if notnull and (postflop or maxcards):
441 for hl, side in hiLoKey[hilo]:
442 try:
443 value, rank = pokereval.best(side, cards, bcards)
444 rankId = Card.hands[rank[0]][0]
445 if rank is not None and rank[0] != "Nothing":
446 _cards = "".join([pokereval.card2string(i)[0] for i in rank[1:]])
447 else:
448 _cards = None
449 self.handsstove.append(
450 [
451 hand.dbid_hands,
452 hand.dbid_pids[pname],
453 streetId,
454 boardId,
455 hl,
456 rankId,
457 value,
458 _cards,
459 0,
460 ]
461 )
462 except RuntimeError:
463 log.error(
464 "assembleHandsStove: error determining value and rank for hand %s %s"
465 % (hand.handid, hand.in_path)
466 )
467 self.handsstove.append(
468 [
469 hand.dbid_hands,
470 hand.dbid_pids[pname],
471 streetId,
472 boardId,
473 "n",
474 1,
475 0,
476 None,
477 0,
478 ]
479 )
480 else:
481 self.handsstove.append(
482 [hand.dbid_hands, hand.dbid_pids[pname], streetId, boardId, "n", 1, 0, None, 0]
483 )
484 else:
485 hl, streetId = hiLoKey[hilo][0][0], 0
486 if hp["sawShowdown"] or hp["showed"]:
487 hp["handString"] = hand.showdownStrings.get(pname)
488 streetId = streets[last]
489 self.handsstove.append([hand.dbid_hands, hand.dbid_pids[player[1]], streetId, 0, hl, 1, 0, None, 0])
491 if base == "hold" and evalgame:
492 self.getAllInEV(hand, evalgame, holeplayers, boards, streets, holecards)
494 def getAllInEV(self, hand, evalgame, holeplayers, boards, streets, holecards):
495 startstreet, potId, allInStreets, allplayers = None, 0, hand.allStreets[1:], []
496 for pot, players in hand.pot.pots:
497 if potId == 0:
498 pot += sum(hand.pot.common.values()) + hand.pot.stp
499 potId += 1
500 for street in allInStreets:
501 board = boards[street]
502 streetId = streets[street]
503 for n in range(len(board["board"])):
504 if len(board["board"]) > 1:
505 boardId = n + 1
506 else:
507 boardId = n
508 valid = [
509 p
510 for p in players
511 if self.handsplayers[p]["sawShowdown"] and "0x" not in holecards[p]["cards"][n]
512 ]
513 if potId == 1:
514 allplayers = valid
515 deadcards, deadplayers = [], []
516 else:
517 deadplayers = [d for d in allplayers if d not in valid]
518 _deadcards = [holecards[d]["hole"] for d in deadplayers]
519 deadcards = [item for sublist in _deadcards for item in sublist]
520 if len(players) == len(valid) and (board["allin"] or hand.publicDB):
521 if board["allin"] and not startstreet:
522 startstreet = street
523 if len(valid) > 1:
524 try:
525 evs = pokereval.poker_eval(
526 game=evalgame,
527 iterations=Card.iter[streetId],
528 pockets=[holecards[p]["hole"] for p in valid],
529 dead=deadcards,
530 board=[str(b) for b in board["board"][n]] + (5 - len(board["board"][n])) * ["__"],
531 )
532 equities = [e["ev"] for e in evs["eval"]]
533 except RuntimeError:
534 log.error(
535 "getAllInEV: error running poker_eval for hand %s %s" % (hand.handid, hand.in_path)
536 )
537 equities = [1000]
538 else:
539 equities = [1000]
540 remainder = old_div((1000 - sum(equities)), Decimal(len(equities)))
541 for i in range(len(equities)):
542 equities[i] += remainder
543 p = valid[i]
544 pid = hand.dbid_pids[p]
545 if street == startstreet:
546 rake = (
547 Decimal(0)
548 if hand.cashedOut
549 else (hand.rake * (old_div(Decimal(pot), Decimal(hand.totalpot))))
550 )
551 holecards[p]["eq"] += old_div(((pot - rake) * equities[i]), Decimal(10))
552 holecards[p]["committed"] = 100 * hand.pot.committed[p] + 100 * hand.pot.common[p]
553 for j in self.handsstove:
554 if [pid, streetId, boardId] == j[1:4] and len(valid) == len(hand.pot.contenders):
555 j[-1] = equities[i]
556 for p in holeplayers:
557 if holecards[p]["committed"] != 0:
558 self.handsplayers[p]["allInEV"] = holecards[p]["eq"] - holecards[p]["committed"]
560 def getBoardsList(self, hand):
561 boards, community = [], []
562 if hand.gametype["base"] == "hold":
563 for s in hand.communityStreets:
564 community += hand.board[s]
565 for i in range(hand.runItTimes):
566 boardcards = []
567 for street in hand.communityStreets:
568 boardId = i + 1
569 street_i = street + str(boardId)
570 if street_i in hand.board:
571 boardcards += hand.board[street_i]
572 cards = [str(c) for c in community + boardcards]
573 boards.append(cards)
574 if not boards:
575 boards = [community]
576 return boards
578 def getBoardsDict(self, hand, base, streets):
579 boards, boardcards, allInStreets, showdown = {}, [], hand.allStreets[1:], False
580 for player in hand.players:
581 if self.handsplayers[player[1]]["sawShowdown"]:
582 showdown = True
583 if base == "hold":
584 for s in allInStreets:
585 streetId = streets[s]
586 b = [x for sublist in [hand.board[k] for k in allInStreets[: streetId + 1]] for x in sublist]
587 boards[s] = {"board": [b], "allin": False}
588 boardcards += hand.board[s]
589 if not hand.actions[s] and showdown:
590 if streetId > 0:
591 boards[allInStreets[streetId - 1]]["allin"] = True
592 boards[s]["allin"] = True
593 boardStreets = [[], [], []]
594 for i in range(hand.runItTimes):
595 runitcards = []
596 for street in hand.communityStreets:
597 street_i = street + str((i + 1))
598 if street_i in hand.board:
599 runitcards += hand.board[street_i]
600 sId = len(boardcards + runitcards) - 3
601 boardStreets[sId].append(boardcards + runitcards)
602 for i in range(len(boardStreets)):
603 if boardStreets[i]:
604 boards[allInStreets[i + 1]]["board"] = boardStreets[i]
605 else:
606 for s in allInStreets:
607 if s in streets:
608 streetId = streets[s]
609 boards[s] = {}
610 boards[s]["board"] = [[]]
611 boards[s]["allin"] = False
612 return boards
614 def awardPots(self, hand):
615 holeshow = True
616 base, evalgame, hilo, streets, last, hrange = Card.games[hand.gametype["category"]]
617 for pot, players in hand.pot.pots:
618 for p in players:
619 hcs = hand.join_holecards(p, asList=True)
620 holes = [
621 str(c)
622 for c in hcs[hrange[-1][0] : hrange[-1][1]]
623 if Card.encodeCardList.get(c) is not None or c == "0x"
624 ]
625 # log.error((p, holes))
626 if "0x" in holes:
627 holeshow = False
628 factor = 100
629 if (
630 hand.gametype["type"] == "tour"
631 or (
632 hand.gametype["type"] == "ring"
633 and (hand.gametype["currency"] == "play" and (hand.sitename not in ("Winamax", "PacificPoker")))
634 )
635 ) and (not [n for (n, v) in hand.pot.pots if (n % Decimal("1.00")) != 0]):
636 factor = 1
637 hiLoKey = {"h": ["hi"], "l": ["low"], "r": ["low"], "s": ["hi", "low"]}
638 # log.error((len(hand.pot.pots)>1, evalgame, holeshow))
639 if pokereval and len(hand.pot.pots) > 1 and evalgame and holeshow: # hrange
640 hand.collected = [] # list of ?
641 hand.collectees = {} # dict from player names to amounts collected (?)
642 # rakes, totrake, potId = {}, 0, 0
643 potId = 0
644 totalrake = hand.rakes.get("rake")
645 if not totalrake:
646 totalpot = hand.rakes.get("pot")
647 if totalpot:
648 totalrake = hand.totalpot - totalpot
649 else:
650 totalrake = 0
651 for pot, players in hand.pot.pots:
652 if potId == 0:
653 pot += sum(hand.pot.common.values()) + hand.pot.stp
654 potId += 1
655 # boards, boardId, sumpot = self.getBoardsList(hand), 0, 0
656 boards, boardId = self.getBoardsList(hand), 0
657 for b in boards:
658 boardId += hand.runItTimes >= 2
659 potBoard = Decimal(int(pot / len(boards) * factor)) / factor
660 modBoard = pot - potBoard * len(boards)
661 if boardId == 1:
662 potBoard += modBoard
663 holeplayers, holecards = [], []
664 for p in players:
665 hcs = hand.join_holecards(p, asList=True)
666 holes = [
667 str(c)
668 for c in hcs[hrange[-1][0] : hrange[-1][1]]
669 if Card.encodeCardList.get(c) is not None or c == "0x"
670 ]
671 board = [str(c) for c in b if "omaha" in evalgame]
672 if "omaha" not in evalgame:
673 holes = holes + [str(c) for c in b if base == "hold"]
674 if "0x" not in holes and "0x" not in board:
675 holecards.append(holes)
676 holeplayers.append(p)
677 if len(holecards) > 1:
678 try:
679 win = pokereval.winners(game=evalgame, pockets=holecards, board=board)
680 except RuntimeError:
681 # log.error((evalgame, holecards, board))
682 log.error(
683 "awardPots: error evaluating winners for hand %s %s" % (hand.handid, hand.in_path)
684 )
685 win = {}
686 win[hiLoKey[hilo][0]] = [0]
687 else:
688 win = {}
689 win[hiLoKey[hilo][0]] = [0]
690 for hl in hiLoKey[hilo]:
691 if hl in win and len(win[hl]) > 0:
692 potHiLo = Decimal(int(potBoard / len(win) * factor)) / factor
693 modHiLo = potBoard - potHiLo * len(win)
694 if len(win) == 1 or hl == "hi":
695 potHiLo += modHiLo
696 potSplit = Decimal(int(potHiLo / len(win[hl]) * factor)) / factor
697 modSplit = potHiLo - potSplit * len(win[hl])
698 pnames = players if len(holeplayers) == 0 else [holeplayers[w] for w in win[hl]]
699 for p in pnames:
700 ppot = potSplit
701 if modSplit > 0:
702 cent = Decimal("0.01") * (100 / factor)
703 ppot += cent
704 modSplit -= cent
705 rake = (totalrake * (ppot / hand.totalpot)).quantize(
706 Decimal("0.01"), rounding=ROUND_DOWN
707 )
708 hand.addCollectPot(player=p, pot=(ppot - rake))
710 def assembleHandsPots(self, hand):
711 # init
712 category = hand.gametype["category"]
713 positions = []
714 playersPots = {}
715 potFound = {}
716 positionDict = {}
717 showdown = False
719 # debug
720 # print("start assembleHandsPots", hand.handid)
722 # Init player's pot dict
723 for p in hand.players:
724 playersPots[p[1]] = [0, []]
725 potFound[p[1]] = [0, 0]
726 position = self.handsplayers[p[1]]["position"]
728 # check if player has a position
729 if position is None or position == "":
730 # print(f"warning: player {p[1]} has no position")
731 position = "Unknown" # default
733 positionDict[str(position)] = p[1]
734 positions.append(str(position))
736 if self.handsplayers[p[1]]["sawShowdown"]:
737 showdown = True
739 # debug
740 # print("positionDict:", positionDict)
741 # print("positions before sort:", positions)
743 # sort positions
744 positions.sort(reverse=True)
745 # print("positions after sort:", positions)
747 # define factor
748 factor = 100
749 if (
750 hand.gametype["type"] == "tour"
751 or (
752 hand.gametype["type"] == "ring"
753 and (hand.gametype["currency"] == "play" and (hand.sitename not in ("Winamax", "PacificPoker")))
754 )
755 ) and (not [n for (n, v) in hand.pot.pots if (n % Decimal("1.00")) != 0]):
756 factor = 1
758 # Config game
759 hiLoKey = {"h": ["hi"], "l": ["low"], "r": ["low"], "s": ["hi", "low"]}
760 base, evalgame, hilo, streets, last, hrange = Card.games[category]
762 # process pots
763 if (
764 (hand.sitename != "KingsClub" or hand.adjustCollected)
765 and evalgame
766 and (len(hand.pot.pots) > 1 or (showdown and (hilo == "s" or hand.runItTimes >= 2)))
767 ):
768 # rakes, totrake, potId = {}, 0, 0
769 potId = 0
770 for pot, players in hand.pot.pots:
771 if potId == 0:
772 pot += sum(hand.pot.common.values()) + hand.pot.stp
773 potId += 1
775 # boards, boardId, sumpot = self.getBoardsList(hand), 0, 0
776 boards, boardId = self.getBoardsList(hand), 0
778 for b in boards:
779 boardId += hand.runItTimes >= 2
780 potBoard = Decimal(int(pot / len(boards) * factor)) / factor
781 modBoard = pot - potBoard * len(boards)
782 if boardId == 1:
783 potBoard += modBoard
785 holeplayers, holecards = [], []
786 for p in players:
787 hcs = hand.join_holecards(p, asList=True)
788 holes = [
789 str(c)
790 for c in hcs[hrange[-1][0] : hrange[-1][1]]
791 if Card.encodeCardList.get(c) is not None or c == "0x"
792 ]
793 board = [str(c) for c in b if "omaha" in evalgame]
794 if "omaha" not in evalgame:
795 holes = holes + [str(c) for c in b if base == "hold"]
796 if "0x" not in holes and "0x" not in board:
797 holecards.append(holes)
798 holeplayers.append(p)
799 if len(holecards) > 1:
800 try:
801 win = pokereval.winners(game=evalgame, pockets=holecards, board=board)
802 except RuntimeError:
803 log.error(
804 "assembleHandsPots: error evaluating winners for hand %s %s"
805 % (hand.handid, hand.in_path)
806 )
807 win = {}
808 win[hiLoKey[hilo][0]] = [0]
809 else:
810 win = {}
811 win[hiLoKey[hilo][0]] = [0]
812 for hl in hiLoKey[hilo]:
813 if hl in win and len(win[hl]) > 0:
814 potHiLo = old_div(Decimal(int(potBoard / len(win) * factor)), factor)
815 modHiLo = potBoard - potHiLo * len(win)
816 if len(win) == 1 or hl == "hi":
817 potHiLo += modHiLo
818 potSplit = old_div(Decimal(int(potHiLo / len(win[hl]) * factor)), factor)
819 modSplit = potHiLo - potSplit * len(win[hl])
820 pnames = players if len(holeplayers) == 0 else [holeplayers[w] for w in win[hl]]
821 for n in positions:
822 # print(f"check position position: {n}")
823 if positionDict[n] in pnames:
824 pname = positionDict[n]
825 ppot = potSplit
826 if modSplit > 0:
827 cent = Decimal("0.01") * (old_div(100, factor))
828 ppot += cent
829 modSplit -= cent
830 playersPots[pname][0] += ppot
831 potFound[pname][0] += ppot
832 data = {
833 "potId": potId,
834 "boardId": boardId,
835 "hiLo": hl,
836 "ppot": ppot,
837 "winners": [m for m in pnames if pname != n],
838 "mod": ppot > potSplit,
839 }
840 playersPots[pname][1].append(data)
841 self.handsplayers[pname]["rake"] = 0
843 for p, (total, info) in list(playersPots.items()):
844 # log.error((p, (total, info)))
845 if hand.collectees.get(p) and info:
846 potFound[p][1] = hand.collectees.get(p)
847 for item in info:
848 # log.error((str(hand.handid)," winners: ",item['winners']))
849 split = [
850 n
851 for n in item["winners"]
852 if len(playersPots[n][1]) == 1 and hand.collectees.get(n) is not None
853 ]
854 if len(info) == 1:
855 ppot = item["ppot"]
856 rake = ppot - hand.collectees[p]
857 collected = hand.collectees[p]
858 elif item == info[-1]:
859 ppot, collected = potFound[p]
860 rake = ppot - collected
861 elif len(split) > 0 and not item["mod"]:
862 ppot = item["ppot"]
863 collected = min([hand.collectees[s] for s in split] + [ppot])
864 rake = ppot - collected
865 else:
866 ppot = item["ppot"]
867 totalrake = total - hand.collectees[p]
868 rake = (totalrake * (old_div(ppot, total))).quantize(Decimal("0.01"))
869 collected = ppot - rake
870 potFound[p][0] -= ppot
871 potFound[p][1] -= collected
872 insert = [
873 None,
874 item["potId"],
875 item["boardId"],
876 item["hiLo"][0],
877 hand.dbid_pids[p],
878 int(item["ppot"] * 100),
879 int(collected * 100),
880 int(rake * 100),
881 ]
882 self.handspots.append(insert)
883 self.handsplayers[p]["rake"] += int(rake * 100)
885 # debug
886 # print("end assembleHandsPots", hand.handid)
888 def setPositions(self, hand):
889 """Sets the position for each player in HandsPlayers
890 any blinds are negative values, and the last person to act on the
891 first betting round is 0
892 NOTE: HU, both values are negative for non-stud games
893 NOTE2: I've never seen a HU stud match"""
894 actions = hand.actions[hand.holeStreets[0]]
895 # Note: pfbao list may not include big blind if all others folded
896 players = self.pfbao(actions)
898 # set blinds first, then others from pfbao list, avoids problem if bb
899 # is missing from pfbao list or if there is no small blind
900 sb, bb, bi, ub, st = False, False, False, False, False
901 if hand.gametype["base"] == "stud":
902 # Stud position is determined after cards are dealt
903 # First player to act is always the bring-in position in stud
904 # even if they decided to bet/completed
905 if len(hand.actions[hand.actionStreets[1]]) > 0:
906 bi = [hand.actions[hand.actionStreets[1]][0][0]]
907 # else:
908 # TODO fix: if ante all and no actions and no bring in
909 # bi = [hand.actions[hand.actionStreets[0]][0][0]]
910 else:
911 ub = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == "button blind"]
912 bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == "big blind"]
913 sb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == "small blind"]
914 st = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == "straddle"]
916 # if there are > 1 sb or bb only the first is used!
917 if ub:
918 self.handsplayers[ub[0]]["street0InPosition"] = True
919 if ub[0] not in players:
920 players.append(ub[0])
921 if bb:
922 self.handsplayers[bb[0]]["position"] = "B"
923 self.handsplayers[bb[0]]["street0InPosition"] = True
924 if bb[0] in players:
925 players.remove(bb[0])
926 if sb:
927 self.handsplayers[sb[0]]["position"] = "S"
928 self.handsplayers[sb[0]]["street0FirstToAct"] = True
929 if sb[0] in players:
930 players.remove(sb[0])
931 if bi:
932 self.handsplayers[bi[0]]["position"] = "S"
933 self.handsplayers[bi[0]]["street0FirstToAct"] = True
934 if bi[0] in players:
935 players.remove(bi[0])
936 if st and st[0] in players:
937 players.insert(0, players.pop())
939 # print "DEBUG: actions: '%s'" % actions
940 # print "DEBUG: ub: '%s' bb: '%s' sb: '%s' bi: '%s' plyrs: '%s'" %(ub, bb, sb, bi, players)
941 for i, player in enumerate(reversed(players)):
942 self.handsplayers[player]["position"] = i
943 self.hands["maxPosition"] = i
944 if i == 0 and hand.gametype["base"] == "stud":
945 self.handsplayers[player]["street0InPosition"] = True
946 elif (i - 1) == len(players):
947 self.handsplayers[player]["street0FirstToAct"] = True
949 def assembleHudCache(self, hand):
950 # No real work to be done - HandsPlayers data already contains the correct info
951 pass
953 def vpip(self, hand):
954 vpipers = set()
955 bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] in ("big blind", "button blind")]
956 for act in hand.actions[hand.actionStreets[1]]:
957 if act[1] in ("calls", "bets", "raises", "completes"):
958 vpipers.add(act[0])
960 self.hands["playersVpi"] = len(vpipers)
962 for player in hand.players:
963 pname = player[1]
964 player_stats = self.handsplayers.get(pname)
965 if pname in vpipers:
966 player_stats["street0VPI"] = True
967 elif pname in hand.sitout:
968 player_stats["street0VPIChance"] = False
969 player_stats["street0AggrChance"] = False
971 if len(vpipers) == 0 and bb:
972 self.handsplayers[bb[0]]["street0VPIChance"] = False
973 self.handsplayers[bb[0]]["street0AggrChance"] = False
975 def playersAtStreetX(self, hand):
976 """playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */"""
977 # self.actions[street] is a list of all actions in a tuple, contining the player name first
978 # [ (player, action, ....), (player2, action, ...) ]
979 # The number of unique players in the list per street gives the value for playersAtStreetXXX
981 # FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this
982 # ... new code below hopefully fixes this
983 # partly fixed, allins are now set as seeing streets because they never do a fold action
985 self.hands["playersAtStreet1"] = 0
986 self.hands["playersAtStreet2"] = 0
987 self.hands["playersAtStreet3"] = 0
988 self.hands["playersAtStreet4"] = 0
989 self.hands["playersAtShowdown"] = 0
991 # alliners = set()
992 # for (i, street) in enumerate(hand.actionStreets[2:]):
993 # actors = set()
994 # for action in hand.actions[street]:
995 # if len(action) > 2 and action[-1]: # allin
996 # alliners.add(action[0])
997 # actors.add(action[0])
998 # if len(actors)==0 and len(alliners)<2:
999 # alliners = set()
1000 # self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors))
1001 #
1002 # actions = hand.actions[hand.actionStreets[-1]]
1003 # print "p_actions:", self.pfba(actions), "p_folds:", self.pfba(actions, l=('folds',)), "alliners:", alliners
1004 # pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners)
1006 # hand.players includes people that are sitting out on some sites for cash games
1007 # actionStreets[1] is 'DEAL', 'THIRD', 'PREFLOP', so any player dealt cards
1008 # must act on this street if dealt cards. Almost certainly broken for the 'all-in blind' case
1009 # and right now i don't care - CG
1011 p_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]])
1012 # Add in players who were allin blind
1013 if hand.pot.pots:
1014 if len(hand.pot.pots[0][1]) > 1:
1015 p_in = p_in.union(hand.pot.pots[0][1])
1017 #
1018 # discover who folded on each street and remove them from p_in
1019 #
1020 # i values as follows 0=BLINDSANTES 1=PREFLOP 2=FLOP 3=TURN 4=RIVER
1021 # (for flop games)
1022 #
1023 # At the beginning of the loop p_in contains the players with cards
1024 # at the start of that street.
1025 # p_in is reduced each street to become a list of players still-in
1026 # e.g. when i=1 (preflop) all players who folded during preflop
1027 # are found by pfba() and eliminated from p_in.
1028 # Therefore at the end of the loop, p_in contains players remaining
1029 # at the end of the action on that street, and can therefore be set
1030 # as the value for the number of players who saw the next street
1031 #
1032 # note that i is 1 in advance of the actual street numbers in the db
1033 #
1034 # if p_in reduces to 1 player, we must bomb-out immediately
1035 # because the hand is over, this will ensure playersAtStreetx
1036 # is accurate.
1037 #
1039 for i, street in enumerate(hand.actionStreets):
1040 if (i - 1) in (1, 2, 3, 4):
1041 # p_in stores players with cards at start of this street,
1042 # so can set streetxSeen & playersAtStreetx with this information
1043 # This hard-coded for i-1 =1,2,3,4 because those are the only columns
1044 # in the db! this code section also replaces seen() - more info log 66
1045 # nb i=2=flop=street1Seen, hence i-1 term needed
1046 self.hands["playersAtStreet%d" % (i - 1)] = len(p_in)
1047 for player_with_cards in p_in:
1048 self.handsplayers[player_with_cards]["street%sSeen" % (i - 1)] = True
1050 players = self.pfbao(hand.actions[street], f=("discards", "stands pat"))
1051 if len(players) > 0:
1052 self.handsplayers[players[0]]["street%dFirstToAct" % (i - 1)] = True
1053 self.handsplayers[players[-1]]["street%dInPosition" % (i - 1)] = True
1054 #
1055 # find out who folded, and eliminate them from p_in
1056 #
1057 actions = hand.actions[street]
1058 p_in = p_in - self.pfba(actions, ("folds",))
1059 #
1060 # if everyone folded, we are done, so exit this method
1061 #
1062 if len(p_in) == 1:
1063 if (i - 1) in (1, 2, 3, 4) and len(players) > 0 and list(p_in)[0] not in players:
1064 # corrects which player is "in position"
1065 # if everyone folds before the last player could act
1066 self.handsplayers[players[-1]]["street%dInPosition" % (i - 1)] = False
1067 self.handsplayers[list(p_in)[0]]["street%dInPosition" % (i - 1)] = True
1068 return None
1070 #
1071 # The remaining players in p_in reached showdown (including all-ins
1072 # because they never did a "fold" action in pfba() above)
1073 #
1074 self.hands["playersAtShowdown"] = len(p_in)
1075 for showdown_player in p_in:
1076 self.handsplayers[showdown_player]["sawShowdown"] = True
1078 def streetXRaises(self, hand):
1079 # self.actions[street] is a list of all actions in a tuple, contining the action as the second element
1080 # [ (player, action, ....), (player2, action, ...) ]
1081 # No idea what this value is actually supposed to be
1082 # In theory its "num small bets paid to see flop/street4, including blind" which makes sense for limit. Not so useful for nl
1083 # Leaving empty for the moment,
1085 for i in range(5):
1086 self.hands["street%dRaises" % i] = 0
1088 for i, street in enumerate(hand.actionStreets[1:]):
1089 self.hands["street%dRaises" % i] = len(
1090 [action for action in hand.actions[street] if action[1] in ("raises", "bets", "completes")]
1091 )
1093 def calcSteals(self, hand):
1094 """Fills raiseFirstInChance|raisedFirstIn, fold(Bb|Sb)ToSteal(Chance|)
1096 Steal attempt - open raise on positions 1 0 S - i.e. CO, BU, SB
1097 (note: I don't think PT2 counts SB steals in HU hands, maybe we shouldn't?)
1098 Fold to steal - folding blind after steal attemp wo any other callers or raisers
1099 """
1100 steal_attempt = False
1101 raised = False
1102 stealer = ""
1103 if hand.gametype["base"] == "stud":
1104 steal_positions = (2, 1, 0)
1105 elif len([x for x in hand.actions[hand.actionStreets[0]] if x[1] == "button blind"]) > 0:
1106 steal_positions = (3, 2, 1)
1107 else:
1108 steal_positions = (1, 0, "S")
1109 for action in hand.actions[hand.actionStreets[1]]:
1110 pname, act = action[0], action[1]
1111 player_stats = self.handsplayers.get(pname)
1112 if player_stats["sitout"]:
1113 continue
1114 posn = player_stats["position"]
1115 # print "\naction:", action[0], posn, type(posn), steal_attempt, act
1116 if posn == "B":
1117 # NOTE: Stud games will never hit this section
1118 if steal_attempt:
1119 player_stats["foldBbToStealChance"] = True
1120 player_stats["raiseToStealChance"] = True
1121 player_stats["foldedBbToSteal"] = act == "folds"
1122 player_stats["raiseToStealDone"] = act == "raises"
1123 self.handsplayers[stealer]["success_Steal"] = act == "folds"
1124 break
1125 elif posn == "S":
1126 player_stats["raiseToStealChance"] = steal_attempt
1127 player_stats["foldSbToStealChance"] = steal_attempt
1128 player_stats["foldedSbToSteal"] = steal_attempt and act == "folds"
1129 player_stats["raiseToStealDone"] = steal_attempt and act == "raises"
1130 if steal_attempt:
1131 self.handsplayers[stealer]["success_Steal"] = act == "folds" and hand.gametype["base"] == "stud"
1133 if steal_attempt and act != "folds":
1134 break
1136 if not steal_attempt and not raised and not act in ("bringin"):
1137 player_stats["raiseFirstInChance"] = True
1138 if posn in steal_positions:
1139 player_stats["stealChance"] = True
1140 if act in ("bets", "raises", "completes"):
1141 player_stats["raisedFirstIn"] = True
1142 raised = True
1143 if posn in steal_positions:
1144 player_stats["stealDone"] = True
1145 steal_attempt = True
1146 stealer = pname
1147 if act == "calls":
1148 break
1150 if posn not in steal_positions and act not in ("folds", "bringin"):
1151 break
1153 def calc34BetStreet0(self, hand):
1154 """Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0"""
1155 if hand.gametype["base"] == "stud":
1156 bet_level = 0 # bet_level after 3-bet is equal to 3
1157 else:
1158 bet_level = 1 # bet_level after 3-bet is equal to 3
1159 squeeze_chance, raise_chance, action_cnt, first_agressor = False, True, {}, None
1160 p0_in = set([x[0] for x in hand.actions[hand.actionStreets[0]] if not x[-1]])
1161 p1_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]])
1162 p_in = p1_in.union(p0_in)
1163 for p in p_in:
1164 action_cnt[p] = 0
1165 for action in hand.actions[hand.actionStreets[1]]:
1166 pname, act, aggr, allin = action[0], action[1], action[1] in ("raises", "bets", "completes"), False
1167 player_stats = self.handsplayers.get(pname)
1168 action_cnt[pname] += 1
1169 if len(action) > 3 and act != "discards":
1170 allin = action[-1]
1171 if len(p_in) == 1 and action_cnt[pname] == 1:
1172 raise_chance = False
1173 player_stats["street0AggrChance"] = raise_chance
1174 if act == "folds" or allin or player_stats["sitout"]:
1175 p_in.discard(pname)
1176 if player_stats["sitout"]:
1177 continue
1178 if bet_level == 0:
1179 if aggr:
1180 if first_agressor is None:
1181 first_agressor = pname
1182 bet_level += 1
1183 continue
1184 elif bet_level == 1:
1185 player_stats["street0_2BChance"] = raise_chance
1186 if aggr:
1187 if first_agressor is None:
1188 first_agressor = pname
1189 player_stats["street0_2BDone"] = True
1190 bet_level += 1
1191 continue
1192 elif bet_level == 2:
1193 player_stats["street0_3BChance"] = raise_chance
1194 player_stats["street0_SqueezeChance"] = squeeze_chance
1195 if pname == first_agressor:
1196 player_stats["street0_FoldTo2BChance"] = True
1197 if act == "folds":
1198 player_stats["street0_FoldTo2BDone"] = True
1199 if not squeeze_chance and act == "calls":
1200 squeeze_chance = True
1201 continue
1202 if aggr:
1203 player_stats["street0_3BDone"] = True
1204 player_stats["street0_SqueezeDone"] = squeeze_chance
1205 # second_agressor = pname
1206 bet_level += 1
1207 continue
1208 elif bet_level == 3:
1209 if pname == first_agressor:
1210 player_stats["street0_4BChance"] = raise_chance
1211 player_stats["street0_FoldTo3BChance"] = True
1212 if aggr:
1213 player_stats["street0_4BDone"] = raise_chance
1214 bet_level += 1
1215 elif act == "folds":
1216 player_stats["street0_FoldTo3BDone"] = True
1217 break
1218 else:
1219 player_stats["street0_C4BChance"] = raise_chance
1220 if aggr:
1221 player_stats["street0_C4BDone"] = raise_chance
1222 bet_level += 1
1223 continue
1224 elif bet_level == 4:
1225 if pname != first_agressor:
1226 player_stats["street0_FoldTo4BChance"] = True
1227 if act == "folds":
1228 player_stats["street0_FoldTo4BDone"] = True
1230 def calcCBets(self, hand):
1231 """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance
1233 Continuation Bet chance, action:
1234 Had the last bet (initiative) on previous street, got called, close street action
1235 Then no bets before the player with initiatives first action on current street
1236 ie. if player on street-1 had initiative and no donkbets occurred
1237 """
1238 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
1239 # came there
1240 # for i, street in enumerate(hand.actionStreets[2:], start=1):
1241 for i, street in enumerate(hand.actionStreets[2:]):
1242 name = self.lastBetOrRaiser(hand.actions, hand.actionStreets[i + 1]) # previous street
1243 if name:
1244 chance = self.noBetsBefore(hand.actions, hand.actionStreets[i + 2], name) # this street
1245 if chance is True:
1246 player_stats = self.handsplayers.get(name)
1247 player_stats["street%dCBChance" % (i + 1)] = True
1248 player_stats["street%dCBDone" % (i + 1)] = self.betStreet(
1249 hand.actions, hand.actionStreets[i + 2], name
1250 )
1251 if player_stats["street%dCBDone" % (i + 1)]:
1252 for pname, folds in list(self.foldTofirstsBetOrRaiser(hand.actions, street, name).items()):
1253 # print "DEBUG:", hand.handid, pname.encode('utf8'), street, folds, '--', name, 'lastbet on ', hand.actionStreets[i+1]
1254 self.handsplayers[pname]["foldToStreet%sCBChance" % (i + 1)] = True
1255 self.handsplayers[pname]["foldToStreet%sCBDone" % (i + 1)] = folds
1257 def calcCalledRaiseStreet0(self, hand):
1258 """
1259 Fill street0CalledRaiseChance, street0CalledRaiseDone
1260 For flop games, go through the preflop actions:
1261 skip through first raise
1262 For each subsequent action:
1263 if the next action is fold :
1264 player chance + 1
1265 if the next action is raise :
1266 player chance + 1
1267 if the next non-fold action is call :
1268 player chance + 1
1269 player done + 1
1270 skip through list to the next raise action
1271 """
1273 fast_forward = True
1274 for tupleread in hand.actions[hand.actionStreets[1]]:
1275 action = tupleread[1]
1276 if fast_forward:
1277 if action in ("raises", "completes"):
1278 fast_forward = False # raisefound, end fast-forward
1279 else:
1280 player = tupleread[0]
1281 player_stats = self.handsplayers.get(player)
1282 player_stats["street0CalledRaiseChance"] += 1
1283 if action == "calls":
1284 player_stats["street0CalledRaiseDone"] += 1
1285 fast_forward = True
1287 def calcCheckCallRaise(self, hand):
1288 """Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone
1290 streetXCheckCallRaiseChance = got raise/bet after check
1291 streetXCheckCallRaiseDone = checked. got raise/bet. didn't fold
1293 CG: CheckCall would be a much better name for this.
1294 """
1295 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start'
1296 # for i, street in enumerate(hand.actionStreets[2:], start=1):
1297 for i, street in enumerate(hand.actionStreets[2:]):
1298 actions = hand.actions[street]
1299 checkers = set()
1300 acted = set()
1301 initial_raiser = None
1302 for action in actions:
1303 pname, act = action[0], action[1]
1304 if act in ("bets", "raises") and initial_raiser is None:
1305 initial_raiser = pname
1306 elif act == "checks" and initial_raiser is None:
1307 checkers.add(pname)
1308 elif initial_raiser is not None and pname in checkers and pname not in acted:
1309 player_stats = self.handsplayers.get(pname)
1310 player_stats["street%dCheckCallRaiseChance" % (i + 1)] = True
1311 player_stats["street%dCheckCallDone" % (i + 1)] = act == "calls"
1312 player_stats["street%dCheckRaiseDone" % (i + 1)] = act == "raises"
1313 acted.add(pname)
1315 def aggr(self, hand, i):
1316 aggrers = set()
1317 others = set()
1318 # Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street
1320 firstAggrMade = False
1321 for act in hand.actions[hand.actionStreets[i + 1]]:
1322 if firstAggrMade:
1323 others.add(act[0])
1324 if act[1] in ("completes", "bets", "raises"):
1325 aggrers.add(act[0])
1326 firstAggrMade = True
1328 for player in hand.players:
1329 if player[1] in aggrers:
1330 self.handsplayers[player[1]]["street%sAggr" % i] = True
1332 if len(aggrers) > 0 and i > 0:
1333 for playername in others:
1334 self.handsplayers[playername]["otherRaisedStreet%s" % i] = True
1335 # print "otherRaised detected on handid "+str(hand.handid)+" for "+playername+" on street "+str(i)
1337 if i > 0 and len(aggrers) > 0:
1338 for playername in others:
1339 self.handsplayers[playername]["otherRaisedStreet%s" % i] = True
1340 # print "DEBUG: otherRaised detected on handid %s for %s on actionStreet[%s]: %s"
1341 # %(hand.handid, playername, hand.actionStreets[i+1], i)
1343 def calls(self, hand, i):
1344 # callers = []
1345 for act in hand.actions[hand.actionStreets[i + 1]]:
1346 if act[1] in ("calls"):
1347 player_stats = self.handsplayers.get(act[0])
1348 player_stats["street%sCalls" % i] = 1 + player_stats["street%sCalls" % i]
1350 def bets(self, hand, i):
1351 for act in hand.actions[hand.actionStreets[i + 1]]:
1352 if act[1] in ("bets"):
1353 player_stats = self.handsplayers.get(act[0])
1354 player_stats["street%sBets" % i] = 1 + player_stats["street%sBets" % i]
1356 def raises(self, hand, i):
1357 for act in hand.actions[hand.actionStreets[i + 1]]:
1358 if act[1] in ("completes", "raises"):
1359 player_stats = self.handsplayers.get(act[0])
1360 player_stats["street%sRaises" % i] = 1 + player_stats["street%sRaises" % i]
1362 def folds(self, hand, i):
1363 for act in hand.actions[hand.actionStreets[i + 1]]:
1364 if act[1] in ("folds"):
1365 player_stats = self.handsplayers.get(act[0])
1366 if player_stats["otherRaisedStreet%s" % i] is True:
1367 player_stats["foldToOtherRaisedStreet%s" % i] = True
1368 # print "DEBUG: fold detected on handid %s for %s on actionStreet[%s]: %s"
1369 # %(hand.handid, act[0],hand.actionStreets[i+1], i)
1371 def countPlayers(self, hand):
1372 pass
1374 def pfba(self, actions, f=None, limit_actions=None):
1375 """Helper method. Returns set of PlayersFilteredByActions
1377 f - forbidden actions
1378 limit_actions - limited to actions
1379 """
1380 players = set()
1381 for action in actions:
1382 if limit_actions is not None and action[1] not in limit_actions:
1383 continue
1384 if f is not None and action[1] in f:
1385 continue
1386 players.add(action[0])
1387 return players
1389 def pfbao(self, actions, f=None, limit_actions=None, unique=True):
1390 """Helper method. Returns set of PlayersFilteredByActionsOrdered
1392 f - forbidden actions
1393 limit_actions - limited to actions
1394 """
1395 # Note, this is an adaptation of function 5 from:
1396 # http://www.peterbe.com/plog/uniqifiers-benchmark
1397 seen = {}
1398 players = []
1399 for action in actions:
1400 if limit_actions is not None and action[1] not in limit_actions:
1401 continue
1402 if f is not None and action[1] in f:
1403 continue
1404 if action[0] in seen and unique:
1405 continue
1406 seen[action[0]] = 1
1407 players.append(action[0])
1408 return players
1410 def calcEffectiveStack(self, hand):
1411 """Calculates the effective stack for each player on street 1"""
1412 seen = {}
1413 pstacks = {}
1414 actions = hand.actions[hand.holeStreets[0]]
1415 for p in hand.players:
1416 if p[1] not in hand.sitout:
1417 pstacks[p[1]] = int(100 * Decimal(p[2]))
1418 for action in actions:
1419 if action[0] in seen:
1420 continue
1421 if action[0] not in pstacks:
1422 continue
1423 seen[action[0]] = 1
1424 oppstacks = [v for (k, v) in list(pstacks.items()) if k != action[0]]
1425 if oppstacks:
1426 if pstacks[action[0]] > max(oppstacks):
1427 self.handsplayers[action[0]]["effStack"] = max(oppstacks)
1428 else:
1429 self.handsplayers[action[0]]["effStack"] = pstacks[action[0]]
1430 if action[1] == "folds":
1431 pstacks[action[0]] = 0
1433 def firstsBetOrRaiser(self, actions):
1434 """Returns player name that placed the first bet or raise.
1436 None if there were no bets or raises on that street
1437 """
1438 for act in actions:
1439 if act[1] in ("bets", "raises", "completes"):
1440 return act[0]
1441 return None
1443 def foldTofirstsBetOrRaiser(self, actions, street, aggressor):
1444 """Returns player name that placed the first bet or raise.
1446 None if there were no bets or raises on that street
1447 """
1448 i, players = 0, {}
1449 for act in actions[street]:
1450 if i > 1:
1451 break
1452 if act[0] != aggressor:
1453 if act[1] == "folds":
1454 players[act[0]] = True
1455 else:
1456 players[act[0]] = False
1457 if act[1] == "raises" or act[1] == "completes":
1458 break
1459 elif act[1] not in ("discards", "stands pat"):
1460 i += 1
1461 return players
1463 def lastBetOrRaiser(self, actions, street):
1464 """Returns player name that placed the last bet or raise for that street.
1465 None if there were no bets or raises on that street"""
1466 lastbet = None
1467 for act in actions[street]:
1468 if act[1] in ("bets", "raises", "completes"):
1469 lastbet = act[0]
1470 return lastbet
1472 def noBetsBefore(self, actions, street, player):
1473 """Returns true if there were no bets before the specified players turn, false otherwise"""
1474 noBetsBefore = False
1475 for act in actions[street]:
1476 # Must test for player first in case UTG
1477 if act[0] == player:
1478 noBetsBefore = True
1479 break
1480 if act[1] in ("bets", "raises", "completes"):
1481 break
1482 return noBetsBefore
1484 def betStreet(self, actions, street, player):
1485 """Returns true if player bet/raised the street as their first action"""
1486 betOrRaise = False
1487 for act in actions[street]:
1488 if act[0] == player and act[1] not in ("discards", "stands pat"):
1489 if act[1] in ("bets", "raises", "completes"):
1490 betOrRaise = True
1491 else:
1492 # player found but did not bet or raise as their first action
1493 pass
1494 break
1495 # else:
1496 # haven't found player's first action yet
1497 return betOrRaise