Coverage for DerivedStats.py: 13%

1010 statements  

« 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 -*- 

3 

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. 

17 

18# fpdb modules 

19from __future__ import division 

20 

21from past.utils import old_div 

22 

23# import L10n 

24# _ = L10n.get_translation() 

25import Card 

26from decimal import Decimal, ROUND_DOWN 

27 

28import logging 

29 

30try: 

31 from pokereval import PokerEval 

32 

33 pokereval = PokerEval() 

34except Exception: 

35 pokereval = None 

36 

37 

38# logging has been set up in fpdb.py or HUD_main.py, use their settings: 

39log = logging.getLogger("parser") 

40 

41 

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 

106 

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 

115 

116 for i in range(1, 4): 

117 init["street%dDiscards" % i] = 0 

118 

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 

131 

132 

133_INIT_STATS = _buildStatsInitializer() 

134 

135 

136class DerivedStats(object): 

137 def __init__(self): 

138 self.hands = {} 

139 self.handsplayers = {} 

140 self.handsactions = {} 

141 self.handsstove = [] 

142 self.handspots = [] 

143 

144 def getStats(self, hand): 

145 for player in hand.players: 

146 self.handsplayers[player[1]] = _INIT_STATS.copy() 

147 

148 self.assembleHands(hand) 

149 self.assembleHandsPlayers(hand) 

150 self.assembleHandsActions(hand) 

151 

152 if pokereval and hand.gametype["category"] in Card.games: 

153 self.assembleHandsStove(hand) 

154 self.assembleHandsPots(hand) 

155 

156 def getHands(self): 

157 return self.hands 

158 

159 def getHandsPlayers(self): 

160 return self.handsplayers 

161 

162 def getHandsActions(self): 

163 return self.handsactions 

164 

165 def getHandsStove(self): 

166 return self.handsstove 

167 

168 def getHandsPots(self): 

169 return self.handspots 

170 

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 

184 

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] 

203 

204 # print "cards: ",cards 

205 

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] 

223 

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] 

233 

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) 

239 

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 

243 

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 

266 

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) 

272 

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) 

280 

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 

314 

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 

330 

331 for i, player in enumerate(contributed): 

332 self.handsplayers[player]["rakeContributed"] = 100 * hand.rake / len(contributed) 

333 

334 self.calcCBets(hand) 

335 

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)) 

356 

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? 

366 

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 

401 

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]) 

490 

491 if base == "hold" and evalgame: 

492 self.getAllInEV(hand, evalgame, holeplayers, boards, streets, holecards) 

493 

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"] 

559 

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 

577 

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 

613 

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)) 

709 

710 def assembleHandsPots(self, hand): 

711 # init 

712 category = hand.gametype["category"] 

713 positions = [] 

714 playersPots = {} 

715 potFound = {} 

716 positionDict = {} 

717 showdown = False 

718 

719 # debug 

720 # print("start assembleHandsPots", hand.handid) 

721 

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"] 

727 

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 

732 

733 positionDict[str(position)] = p[1] 

734 positions.append(str(position)) 

735 

736 if self.handsplayers[p[1]]["sawShowdown"]: 

737 showdown = True 

738 

739 # debug 

740 # print("positionDict:", positionDict) 

741 # print("positions before sort:", positions) 

742 

743 # sort positions 

744 positions.sort(reverse=True) 

745 # print("positions after sort:", positions) 

746 

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 

757 

758 # Config game 

759 hiLoKey = {"h": ["hi"], "l": ["low"], "r": ["low"], "s": ["hi", "low"]} 

760 base, evalgame, hilo, streets, last, hrange = Card.games[category] 

761 

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 

774 

775 # boards, boardId, sumpot = self.getBoardsList(hand), 0, 0 

776 boards, boardId = self.getBoardsList(hand), 0 

777 

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 

784 

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 

842 

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) 

884 

885 # debug 

886 # print("end assembleHandsPots", hand.handid) 

887 

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) 

897 

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"] 

915 

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()) 

938 

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 

948 

949 def assembleHudCache(self, hand): 

950 # No real work to be done - HandsPlayers data already contains the correct info 

951 pass 

952 

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]) 

959 

960 self.hands["playersVpi"] = len(vpipers) 

961 

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 

970 

971 if len(vpipers) == 0 and bb: 

972 self.handsplayers[bb[0]]["street0VPIChance"] = False 

973 self.handsplayers[bb[0]]["street0AggrChance"] = False 

974 

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 

980 

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 

984 

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 

990 

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) 

1005 

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 

1010 

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]) 

1016 

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 # 

1038 

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 

1049 

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 

1069 

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 

1077 

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, 

1084 

1085 for i in range(5): 

1086 self.hands["street%dRaises" % i] = 0 

1087 

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 ) 

1092 

1093 def calcSteals(self, hand): 

1094 """Fills raiseFirstInChance|raisedFirstIn, fold(Bb|Sb)ToSteal(Chance|) 

1095 

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" 

1132 

1133 if steal_attempt and act != "folds": 

1134 break 

1135 

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 

1149 

1150 if posn not in steal_positions and act not in ("folds", "bringin"): 

1151 break 

1152 

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 

1229 

1230 def calcCBets(self, hand): 

1231 """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance 

1232 

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 

1256 

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 """ 

1272 

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 

1286 

1287 def calcCheckCallRaise(self, hand): 

1288 """Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone 

1289 

1290 streetXCheckCallRaiseChance = got raise/bet after check 

1291 streetXCheckCallRaiseDone = checked. got raise/bet. didn't fold 

1292 

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) 

1314 

1315 def aggr(self, hand, i): 

1316 aggrers = set() 

1317 others = set() 

1318 # Growl - actionStreets contains 'BLINDSANTES', which isn't actually an action street 

1319 

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 

1327 

1328 for player in hand.players: 

1329 if player[1] in aggrers: 

1330 self.handsplayers[player[1]]["street%sAggr" % i] = True 

1331 

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) 

1336 

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) 

1342 

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] 

1349 

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] 

1355 

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] 

1361 

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) 

1370 

1371 def countPlayers(self, hand): 

1372 pass 

1373 

1374 def pfba(self, actions, f=None, limit_actions=None): 

1375 """Helper method. Returns set of PlayersFilteredByActions 

1376 

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 

1388 

1389 def pfbao(self, actions, f=None, limit_actions=None, unique=True): 

1390 """Helper method. Returns set of PlayersFilteredByActionsOrdered 

1391 

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 

1409 

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 

1432 

1433 def firstsBetOrRaiser(self, actions): 

1434 """Returns player name that placed the first bet or raise. 

1435 

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 

1442 

1443 def foldTofirstsBetOrRaiser(self, actions, street, aggressor): 

1444 """Returns player name that placed the first bet or raise. 

1445 

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 

1462 

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 

1471 

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 

1483 

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