Coverage for DerivedStats.py: 14%

989 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 18:50 +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#import L10n 

23#_ = L10n.get_translation() 

24import Card 

25from decimal_wrapper import Decimal, ROUND_DOWN 

26 

27import sys 

28import logging 

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

30log = logging.getLogger("parser") 

31 

32try: 

33 from pokereval import PokerEval 

34 pokereval = PokerEval() 

35except: 

36 pokereval = None 

37 

38def _buildStatsInitializer(): 

39 init = {} 

40 #Init vars that may not be used, but still need to be inserted. 

41 # All stud street4 need this when importing holdem 

42 init['effStack'] = 0 

43 init['startBounty'] = None 

44 init['endBounty'] = None 

45 init['common'] = 0 

46 init['committed'] = 0 

47 init['winnings'] = 0 

48 init['rake'] = 0 

49 init['rakeDealt'] = 0 

50 init['rakeContributed'] = 0 

51 init['rakeWeighted'] = 0 

52 init['totalProfit'] = 0 

53 init['allInEV'] = 0 

54 init['showdownWinnings'] = 0 

55 init['nonShowdownWinnings'] = 0 

56 init['sawShowdown'] = False 

57 init['showed'] = False 

58 init['wonAtSD'] = False 

59 init['startCards'] = 170 

60 init['handString'] = None 

61 init['position'] = 9 #ANTE ALL IN 

62 init['street0CalledRaiseChance'] = 0 

63 init['street0CalledRaiseDone'] = 0 

64 init['street0VPIChance'] = True 

65 init['street0VPI'] = False 

66 init['street0AggrChance'] = True 

67 init['street0_2BChance'] = False 

68 init['street0_2BDone'] = False 

69 init['street0_3BChance'] = False 

70 init['street0_3BDone'] = False 

71 init['street0_4BChance'] = False 

72 init['street0_4BDone'] = False 

73 init['street0_C4BChance'] = False 

74 init['street0_C4BDone'] = False 

75 init['street0_FoldTo2BChance']= False 

76 init['street0_FoldTo2BDone']= False 

77 init['street0_FoldTo3BChance']= False 

78 init['street0_FoldTo3BDone']= False 

79 init['street0_FoldTo4BChance']= False 

80 init['street0_FoldTo4BDone']= False 

81 init['street0_SqueezeChance']= False 

82 init['street0_SqueezeDone'] = False 

83 init['stealChance'] = False 

84 init['stealDone'] = False 

85 init['success_Steal'] = False 

86 init['raiseToStealChance'] = False 

87 init['raiseToStealDone'] = False 

88 init['raiseFirstInChance'] = False 

89 init['raisedFirstIn'] = False 

90 init['foldBbToStealChance'] = False 

91 init['foldSbToStealChance'] = False 

92 init['foldedSbToSteal'] = False 

93 init['foldedBbToSteal'] = False 

94 init['tourneyTypeId'] = None 

95 init['street1Seen'] = False 

96 init['street2Seen'] = False 

97 init['street3Seen'] = False 

98 init['street4Seen'] = False 

99 init['otherRaisedStreet0'] = False 

100 init['foldToOtherRaisedStreet0'] = False 

101 init['wentAllIn'] = False 

102 

103 for i in range(5): 

104 init['street%dCalls' % i] = 0 

105 init['street%dBets' % i] = 0 

106 init['street%dRaises' % i] = 0 

107 init['street%dAggr' % i] = False 

108 init['street%dInPosition' % i] = False 

109 init['street%dFirstToAct' % i] = False 

110 init['street%dAllIn' % i] = False 

111 

112 for i in range(1,4): 

113 init['street%dDiscards' % i] = 0 

114 

115 for i in range(1,5): 

116 init['street%dCBChance' %i] = False 

117 init['street%dCBDone' %i] = False 

118 init['street%dCheckCallRaiseChance' %i] = False 

119 init['street%dCheckCallDone' %i] = False 

120 init['street%dCheckRaiseDone' %i] = False 

121 init['otherRaisedStreet%d' %i] = False 

122 init['foldToOtherRaisedStreet%d' %i] = False 

123 init['foldToStreet%dCBChance' %i] = False 

124 init['foldToStreet%dCBDone' %i] = False 

125 init['wonWhenSeenStreet%d' %i] = False 

126 return init 

127 

128_INIT_STATS = _buildStatsInitializer() 

129 

130class DerivedStats(object): 

131 def __init__(self): 

132 self.hands = {} 

133 self.handsplayers = {} 

134 self.handsactions = {} 

135 self.handsstove = [] 

136 self.handspots = [] 

137 

138 def getStats(self, hand): 

139 for player in hand.players: 

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

141 

142 self.assembleHands(hand) 

143 self.assembleHandsPlayers(hand) 

144 self.assembleHandsActions(hand) 

145 

146 if pokereval and hand.gametype['category'] in Card.games: 

147 self.assembleHandsStove(hand) 

148 self.assembleHandsPots(hand) 

149 

150 def getHands(self): 

151 return self.hands 

152 

153 def getHandsPlayers(self): 

154 return self.handsplayers 

155 

156 def getHandsActions(self): 

157 return self.handsactions 

158 

159 def getHandsStove(self): 

160 return self.handsstove 

161 

162 def getHandsPots(self): 

163 return self.handspots 

164 

165 def assembleHands(self, hand): 

166 self.hands['tableName'] = hand.tablename 

167 self.hands['siteHandNo'] = hand.handid 

168 self.hands['gametypeId'] = None # Leave None, handled later after checking db 

169 self.hands['sessionId'] = None # Leave None, added later if caching sessions 

170 self.hands['gameId'] = None # Leave None, added later if caching sessions 

171 self.hands['startTime'] = hand.startTime # format this! 

172 self.hands['importTime'] = None 

173 self.hands['seats'] = self.countPlayers(hand) 

174 self.hands['maxPosition'] = -1 

175 #self.hands['maxSeats'] = hand.maxseats 

176 self.hands['texture'] = None # No calculation done for this yet. 

177 self.hands['tourneyId'] = hand.tourneyId 

178 

179 self.hands['heroSeat'] = 0 

180 for player in hand.players: 

181 if hand.hero==player[1]: 

182 self.hands['heroSeat'] = player[0] 

183 # This (i think...) is correct for both stud and flop games, as hand.board['street'] disappears, and 

184 # those values remain default in stud. 

185 boardcards = [] 

186 if hand.board.get('FLOPET')!=None: 

187 boardcards += hand.board.get('FLOPET') 

188 for street in hand.communityStreets: 

189 boardcards += hand.board[street] 

190 boardcards += [u'0x', u'0x', u'0x', u'0x', u'0x'] 

191 cards = [Card.encodeCard(c) for c in boardcards[0:5]] 

192 self.hands['boardcard1'] = cards[0] 

193 self.hands['boardcard2'] = cards[1] 

194 self.hands['boardcard3'] = cards[2] 

195 self.hands['boardcard4'] = cards[3] 

196 self.hands['boardcard5'] = cards[4] 

197 

198 #print "cards: ",cards 

199 

200 self.hands['boards'] = [] 

201 self.hands['runItTwice'] = False 

202 for i in range(hand.runItTimes): 

203 boardcards = [] 

204 for street in hand.communityStreets: 

205 boardId = i+1 

206 street_i = street + str(boardId) 

207 if street_i in hand.board: 

208 boardcards += hand.board[street_i] 

209 if hand.gametype['split']: 

210 boardcards = boardcards + [u'0x', u'0x', u'0x', u'0x', u'0x'] 

211 cards = [Card.encodeCard(c) for c in boardcards[:5]] 

212 else: 

213 self.hands['runItTwice'] = True 

214 boardcards = [u'0x', u'0x', u'0x', u'0x', u'0x'] + boardcards 

215 cards = [Card.encodeCard(c) for c in boardcards[-5:]] 

216 self.hands['boards'] += [[boardId] + cards] 

217 

218 #print "DEBUG: %s self.getStreetTotals = (%s, %s, %s, %s, %s, %s)" % tuple([hand.handid] + list(hand.getStreetTotals())) 

219 totals = hand.getStreetTotals() 

220 totals = [int(100*i) for i in totals] 

221 self.hands['street0Pot'] = totals[0] 

222 self.hands['street1Pot'] = totals[1] 

223 self.hands['street2Pot'] = totals[2] 

224 self.hands['street3Pot'] = totals[3] 

225 self.hands['street4Pot'] = totals[4] 

226 self.hands['finalPot'] = totals[5] 

227 

228 self.vpip(hand) # Gives playersVpi (num of players vpip) 

229 #print "DEBUG: vpip: %s" %(self.hands['playersVpi']) 

230 self.playersAtStreetX(hand) # Gives playersAtStreet1..4 and Showdown 

231 #print "DEBUG: playersAtStreet 1:'%s' 2:'%s' 3:'%s' 4:'%s'" %(self.hands['playersAtStreet1'],self.hands['playersAtStreet2'],self.hands['playersAtStreet3'],self.hands['playersAtStreet4']) 

232 self.streetXRaises(hand) 

233 

234 def assembleHandsPlayers(self, hand): 

235 #street0VPI/vpip already called in Hand 

236 # sawShowdown is calculated in playersAtStreetX, as that calculation gives us a convenient list of names 

237 

238 #hand.players = [[seat, name, chips],[seat, name, chips]] 

239 for player in hand.players: 

240 player_name = player[1] 

241 player_stats = self.handsplayers.get(player_name) 

242 player_stats['seatNo'] = player[0] 

243 player_stats['startCash'] = int(100 * Decimal(player[2])) 

244 if player[4] != None: 

245 player_stats['startBounty'] = int(100 * Decimal(player[4])) 

246 player_stats['endBounty'] = int(100 * Decimal(player[4])) 

247 if player_name in hand.endBounty: 

248 player_stats['endBounty'] = int(hand.endBounty.get(player_name)) 

249 if player_name in hand.sitout: 

250 player_stats['sitout'] = True 

251 else: 

252 player_stats['sitout'] = False 

253 if hand.gametype["type"]=="tour": 

254 player_stats['tourneyTypeId']=hand.tourneyTypeId 

255 player_stats['tourneysPlayersId'] = hand.tourneysPlayersIds[player[1]] 

256 else: 

257 player_stats['tourneysPlayersId'] = None 

258 if player_name in hand.shown: 

259 player_stats['showed'] = True 

260 

261 #### seen now processed in playersAtStreetX() 

262 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start' 

263 #for i, street in enumerate(hand.actionStreets[2:], start=1): 

264 #for i, street in enumerate(hand.actionStreets[2:]): 

265 # self.seen(self.hand, i+1) 

266 

267 for i, street in enumerate(hand.actionStreets[1:]): 

268 self.aggr(hand, i) 

269 self.calls(hand, i) 

270 self.bets(hand, i) 

271 self.raises(hand, i) 

272 if i>0: 

273 self.folds(hand, i) 

274 

275 # Winnings is a non-negative value of money collected from the pot, which already includes the 

276 # rake taken out. hand.collectees is Decimal, database requires cents 

277 num_collectees, i = len(hand.collectees), 0 

278 even_split = old_div(hand.totalpot, num_collectees) if num_collectees > 0 else 0 

279 unraked = [c for c in list(hand.collectees.values()) if even_split == c] 

280 for player, winnings in list(hand.collectees.items()): 

281 collectee_stats = self.handsplayers.get(player) 

282 collectee_stats['winnings'] = int(100 * winnings) 

283 # Splits evenly on split pots and gives remainder to first player 

284 # Gets overwritten when calculating multi-way pots in assembleHandsPots 

285 if num_collectees == 0: 

286 collectee_stats['rake'] = 0 

287 elif len(unraked)==0: 

288 rake = old_div(int(100 * hand.rake),num_collectees) 

289 remainder_1, remainder_2 = 0, 0 

290 if rake > 0 and i==0: 

291 leftover = int(100 * hand.rake) - (rake * num_collectees) 

292 remainder_1 = int(100 * hand.rake) % rake 

293 remainder_2 = leftover if remainder_1 == 0 else 0 

294 collectee_stats['rake'] = rake + remainder_1 + remainder_2 

295 else: 

296 collectee_stats['rake'] = int(100 *(even_split - winnings)) 

297 if collectee_stats['street1Seen'] == True: 

298 collectee_stats['wonWhenSeenStreet1'] = True 

299 if collectee_stats['street2Seen'] == True: 

300 collectee_stats['wonWhenSeenStreet2'] = True 

301 if collectee_stats['street3Seen'] == True: 

302 collectee_stats['wonWhenSeenStreet3'] = True 

303 if collectee_stats['street4Seen'] == True: 

304 collectee_stats['wonWhenSeenStreet4'] = True 

305 if collectee_stats['sawShowdown'] == True: 

306 collectee_stats['wonAtSD'] = True 

307 i+=1 

308 

309 contributed, i = [], 0 

310 for player, money_committed in list(hand.pot.committed.items()): 

311 committed_player_stats = self.handsplayers.get(player) 

312 paid = (100 * money_committed) + (100*hand.pot.common[player]) 

313 committed_player_stats['common'] = int(100 * hand.pot.common[player]) 

314 committed_player_stats['committed'] = int(100 * money_committed) 

315 committed_player_stats['totalProfit'] = int(committed_player_stats['winnings'] - paid) 

316 committed_player_stats['allInEV'] = committed_player_stats['totalProfit'] 

317 committed_player_stats['rakeDealt'] = 100 * hand.rake/len(hand.players) 

318 committed_player_stats['rakeWeighted'] = 100 * hand.rake * paid/(100*hand.totalpot) if hand.rake>0 else 0 

319 if paid > 0: contributed.append(player) 

320 i+=1 

321 

322 for i, player in enumerate(contributed): 

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

324 

325 self.calcCBets(hand) 

326 

327 # More inner-loop speed hackery. 

328 encodeCard = Card.encodeCard 

329 calcStartCards = Card.calcStartCards 

330 for player in hand.players: 

331 player_name = player[1] 

332 hcs = hand.join_holecards(player_name, asList=True) 

333 hcs = hcs + [u'0x']*18 

334 #for i, card in enumerate(hcs[:20, 1): #Python 2.6 syntax 

335 # self.handsplayers[player[1]]['card%s' % i] = Card.encodeCard(card) 

336 player_stats = self.handsplayers.get(player_name) 

337 if player_stats['sawShowdown']: 

338 player_stats['showdownWinnings'] = player_stats['totalProfit'] 

339 else: 

340 player_stats['nonShowdownWinnings'] = player_stats['totalProfit'] 

341 for i, card in enumerate(hcs[:20]): 

342 player_stats['card%d' % (i+1)] = encodeCard(card) 

343 try: 

344 player_stats['startCards'] = calcStartCards(hand, player_name) 

345 except IndexError: 

346 log.error("IndexError: string index out of range %s %s" % (hand.handid, hand.in_path)) 

347 

348 self.setPositions(hand) 

349 self.calcEffectiveStack(hand) 

350 self.calcCheckCallRaise(hand) 

351 self.calc34BetStreet0(hand) 

352 self.calcSteals(hand) 

353 self.calcCalledRaiseStreet0(hand) 

354 # Additional stats 

355 # 3betSB, 3betBB 

356 # Squeeze, Ratchet? 

357 

358 def assembleHandsActions(self, hand): 

359 k = 0 

360 for i, street in enumerate(hand.actionStreets): 

361 for j, act in enumerate(hand.actions[street]): 

362 k += 1 

363 self.handsactions[k] = {} 

364 #default values 

365 self.handsactions[k]['amount'] = 0 

366 self.handsactions[k]['raiseTo'] = 0 

367 self.handsactions[k]['amountCalled'] = 0 

368 self.handsactions[k]['numDiscarded'] = 0 

369 self.handsactions[k]['cardsDiscarded'] = None 

370 self.handsactions[k]['allIn'] = False 

371 #Insert values from hand.actions 

372 self.handsactions[k]['player'] = act[0] 

373 self.handsactions[k]['street'] = i-1 

374 self.handsactions[k]['actionNo'] = k 

375 self.handsactions[k]['streetActionNo'] = (j+1) 

376 self.handsactions[k]['actionId'] = hand.ACTION[act[1]] 

377 if act[1] not in ('discards') and len(act) > 2: 

378 self.handsactions[k]['amount'] = int(100 * act[2]) 

379 if act[1] in ('raises', 'completes'): 

380 self.handsactions[k]['raiseTo'] = int(100 * act[3]) 

381 self.handsactions[k]['amountCalled'] = int(100 * act[4]) 

382 if act[1] in ('discards'): 

383 self.handsactions[k]['numDiscarded'] = int(act[2]) 

384 self.handsplayers[act[0]]['street%dDiscards' %(i-1)] = int(act[2]) 

385 if act[1] in ('discards') and len(act) > 3: 

386 self.handsactions[k]['cardsDiscarded'] = act[3] 

387 if len(act) > 3 and act[1] not in ('discards'): 

388 self.handsactions[k]['allIn'] = act[-1] 

389 if act[-1]: 

390 self.handsplayers[act[0]]['wentAllIn'] = True 

391 self.handsplayers[act[0]]['street%dAllIn' %(i-1)] = True 

392 

393 def assembleHandsStove(self, hand): 

394 category = hand.gametype['category'] 

395 holecards, holeplayers, allInStreets = {}, [], hand.allStreets[1:] 

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

397 hiLoKey = {'h': [('h', 'hi')], 'l': [('l', 'low')], 's': [('h', 'hi'),('l', 'low')], 'r': [('l', 'hi')]} 

398 boards = self.getBoardsDict(hand, base, streets) 

399 for player in hand.players: 

400 pname = player[1] 

401 hp = self.handsplayers.get(pname) 

402 if evalgame: 

403 hcs = hand.join_holecards(pname, asList=True) 

404 holecards[pname] = {} 

405 holecards[pname]['cards'] = [] 

406 holecards[pname]['eq'] = 0 

407 holecards[pname]['committed'] = 0 

408 holeplayers.append(pname) 

409 for street, board in list(boards.items()): 

410 streetId = streets[street] 

411 if streetId > 0: 

412 streetSeen = hp['street%sSeen' % str(streetId)] 

413 else: streetSeen = True 

414 if ((pname==hand.hero and streetSeen) or (hp['showed'] and streetSeen) or hp['sawShowdown']): 

415 boardId, hl, rankId, value, _cards = 0, 'n', 1, 0, None 

416 for n in range(len(board['board'])): 

417 streetIdx = -1 if base=='hold' else streetId 

418 cards = hcs[hrange[streetIdx][0]:hrange[streetIdx][1]] 

419 boardId = (n + 1) if (len(board['board']) > 1) else n 

420 cards += board['board'][n] if (board['board'][n] and 'omaha' not in evalgame) else [] 

421 bcards = board['board'][n] if (board['board'][n] and 'omaha' in evalgame) else [] 

422 cards = [str(c) if Card.encodeCardList.get(c) else '0x' for c in cards] 

423 bcards = [str(b) if Card.encodeCardList.get(b) else '0x' for b in bcards] 

424 holecards[pname]['hole'] = cards[hrange[streetIdx][0]:hrange[streetIdx][1]] 

425 holecards[pname]['cards'] += [cards] 

426 notnull = ('0x' not in cards) and ('0x' not in bcards) 

427 postflop = (base=='hold' and len(board['board'][n])>=3) 

428 maxcards = (base!='hold' and len(cards)>=5) 

429 if notnull and (postflop or maxcards): 

430 for hl, side in hiLoKey[hilo]: 

431 try: 

432 value, rank = pokereval.best(side, cards, bcards) 

433 rankId = Card.hands[rank[0]][0] 

434 if rank!=None and rank[0] != 'Nothing': 

435 _cards = ''.join([pokereval.card2string(i)[0] for i in rank[1:]]) 

436 else: 

437 _cards = None 

438 self.handsstove.append( [hand.dbid_hands, hand.dbid_pids[pname], streetId, boardId, hl, rankId, value, _cards, 0] ) 

439 except RuntimeError: 

440 log.error("assembleHandsStove: error determining value and rank for hand %s %s" % (hand.handid, hand.in_path)) 

441 self.handsstove.append( [hand.dbid_hands, hand.dbid_pids[pname], streetId, boardId, 'n', 1, 0, None, 0] ) 

442 else: 

443 self.handsstove.append( [hand.dbid_hands, hand.dbid_pids[pname], streetId, boardId, 'n', 1, 0, None, 0] ) 

444 else: 

445 hl, streetId = hiLoKey[hilo][0][0], 0 

446 if (hp['sawShowdown'] or hp['showed']): 

447 hp['handString'] = hand.showdownStrings.get(pname) 

448 streetId = streets[last] 

449 self.handsstove.append( [hand.dbid_hands, hand.dbid_pids[player[1]], streetId, 0, hl, 1, 0, None, 0] ) 

450 

451 if base=='hold' and evalgame: 

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

453 

454 def getAllInEV(self, hand, evalgame, holeplayers, boards, streets, holecards): 

455 startstreet, potId, allInStreets, allplayers = None, 0, hand.allStreets[1:], [] 

456 for pot, players in hand.pot.pots: 

457 if potId ==0: pot += (sum(hand.pot.common.values()) + hand.pot.stp) 

458 potId+=1 

459 for street in allInStreets: 

460 board = boards[street] 

461 streetId = streets[street] 

462 for n in range(len(board['board'])): 

463 if len(board['board']) > 1: 

464 boardId = n + 1 

465 else: boardId = n 

466 valid = [p for p in players if self.handsplayers[p]['sawShowdown'] and u'0x' not in holecards[p]['cards'][n]] 

467 if potId == 1: 

468 allplayers = valid 

469 deadcards, deadplayers = [], [] 

470 else: 

471 deadplayers = [d for d in allplayers if d not in valid] 

472 _deadcards = [holecards[d]['hole'] for d in deadplayers] 

473 deadcards = [item for sublist in _deadcards for item in sublist] 

474 if len(players) == len(valid) and (board['allin'] or hand.publicDB): 

475 if board['allin'] and not startstreet: startstreet = street 

476 if len(valid) > 1: 

477 try: 

478 evs = pokereval.poker_eval( 

479 game = evalgame, 

480 iterations = Card.iter[streetId], 

481 pockets = [holecards[p]['hole'] for p in valid], 

482 dead = deadcards, 

483 board = [str(b) for b in board['board'][n]] + (5 - len(board['board'][n])) * ['__'] 

484 ) 

485 equities = [e['ev'] for e in evs['eval']] 

486 except RuntimeError: 

487 log.error("getAllInEV: error running poker_eval for hand %s %s" % (hand.handid, hand.in_path)) 

488 equities = [1000] 

489 else: 

490 equities = [1000] 

491 remainder = old_div((1000 - sum(equities)), Decimal(len(equities))) 

492 for i in range(len(equities)): 

493 equities[i] += remainder 

494 p = valid[i] 

495 pid = hand.dbid_pids[p] 

496 if street == startstreet: 

497 rake = Decimal(0) if hand.cashedOut else (hand.rake * (old_div(Decimal(pot),Decimal(hand.totalpot)))) 

498 holecards[p]['eq'] += old_div(((pot - rake) * equities[i]),Decimal(10)) 

499 holecards[p]['committed'] = 100*hand.pot.committed[p] + 100*hand.pot.common[p] 

500 for j in self.handsstove: 

501 if [pid, streetId, boardId] == j[1:4] and len(valid) == len(hand.pot.contenders): 

502 j[-1] = equities[i] 

503 for p in holeplayers: 

504 if holecards[p]['committed'] != 0: 

505 self.handsplayers[p]['allInEV'] = holecards[p]['eq'] - holecards[p]['committed'] 

506 

507 def getBoardsList(self, hand): 

508 boards, community = [], [] 

509 if hand.gametype['base']=='hold': 

510 for s in hand.communityStreets: 

511 community += hand.board[s] 

512 for i in range(hand.runItTimes): 

513 boardcards = [] 

514 for street in hand.communityStreets: 

515 boardId = i+1 

516 street_i = street + str(boardId) 

517 if street_i in hand.board: 

518 boardcards += hand.board[street_i] 

519 cards = [str(c) for c in community + boardcards] 

520 boards.append(cards) 

521 if not boards: 

522 boards = [community] 

523 return boards 

524 

525 def getBoardsDict(self, hand, base, streets): 

526 boards, boardcards, allInStreets, showdown = {}, [], hand.allStreets[1:], False 

527 for player in hand.players: 

528 if (self.handsplayers[player[1]]['sawShowdown']): 

529 showdown = True 

530 if base == 'hold': 

531 for s in allInStreets: 

532 streetId = streets[s] 

533 b = [x for sublist in [hand.board[k] for k in allInStreets[:streetId+1]] for x in sublist] 

534 boards[s] = {'board': [b], 'allin': False} 

535 boardcards += hand.board[s] 

536 if not hand.actions[s] and showdown: 

537 if streetId>0: boards[allInStreets[streetId-1]]['allin'] = True 

538 boards[s]['allin'] = True 

539 boardStreets = [[], [], []] 

540 for i in range(hand.runItTimes): 

541 runitcards = [] 

542 for street in hand.communityStreets: 

543 street_i = street + str((i+1)) 

544 if street_i in hand.board: 

545 runitcards += hand.board[street_i] 

546 sId = len(boardcards + runitcards) - 3 

547 boardStreets[sId].append(boardcards + runitcards) 

548 for i in range(len(boardStreets)): 

549 if boardStreets[i]: 

550 boards[allInStreets[i+1]]['board'] = boardStreets[i] 

551 else: 

552 for s in allInStreets: 

553 if s in streets: 

554 streetId = streets[s] 

555 boards[s] = {} 

556 boards[s]['board'] = [[]] 

557 boards[s]['allin'] = False 

558 return boards 

559 

560 def awardPots(self, hand): 

561 holeshow = True 

562 base, evalgame, hilo, streets, last, hrange = Card.games[hand.gametype['category']] 

563 for pot, players in hand.pot.pots: 

564 for p in players: 

565 hcs = hand.join_holecards(p, asList=True) 

566 holes = [str(c) for c in hcs[hrange[-1][0]:hrange[-1][1]] if Card.encodeCardList.get(c)!=None or c=='0x'] 

567 #log.error((p, holes)) 

568 if '0x' in holes: holeshow = False 

569 factor = 100 

570 if ((hand.gametype["type"]=="tour" or 

571 (hand.gametype["type"]=="ring" and 

572 (hand.gametype["currency"]=="play" and 

573 (hand.sitename not in ('Winamax', 'PacificPoker'))))) and 

574 (not [n for (n,v) in hand.pot.pots if (n % Decimal('1.00'))!=0])): 

575 factor = 1 

576 hiLoKey = {'h':['hi'],'l':['low'],'r':['low'],'s':['hi','low']} 

577 #log.error((len(hand.pot.pots)>1, evalgame, holeshow)) 

578 if pokereval and len(hand.pot.pots)>1 and evalgame and holeshow: #hrange 

579 hand.collected = [] #list of ? 

580 hand.collectees = {} # dict from player names to amounts collected (?) 

581 rakes, totrake, potId = {}, 0, 0 

582 totalrake = hand.rakes.get('rake') 

583 if not totalrake: 

584 totalpot = hand.rakes.get('pot') 

585 if totalpot: 

586 totalrake = hand.totalpot - totalpot 

587 else: 

588 totalrake = 0 

589 for pot, players in hand.pot.pots: 

590 if potId ==0: pot += (sum(hand.pot.common.values()) + hand.pot.stp) 

591 potId+=1 

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

593 for b in boards: 

594 boardId += (hand.runItTimes>=2) 

595 potBoard = Decimal(int(pot/len(boards)*factor))/factor 

596 modBoard = pot - potBoard*len(boards) 

597 if boardId==1: 

598 potBoard+=modBoard 

599 holeplayers, holecards = [], [] 

600 for p in players: 

601 hcs = hand.join_holecards(p, asList=True) 

602 holes = [str(c) for c in hcs[hrange[-1][0]:hrange[-1][1]] if Card.encodeCardList.get(c)!=None or c=='0x'] 

603 board = [str(c) for c in b if 'omaha' in evalgame] 

604 if 'omaha' not in evalgame: 

605 holes = holes + [str(c) for c in b if base=='hold'] 

606 if '0x' not in holes and '0x' not in board: 

607 holecards.append(holes) 

608 holeplayers.append(p) 

609 if len(holecards)>1: 

610 try: 

611 win = pokereval.winners(game = evalgame, pockets = holecards, board = board) 

612 except RuntimeError: 

613 #log.error((evalgame, holecards, board)) 

614 log.error("awardPots: error evaluating winners for hand %s %s" % (hand.handid, hand.in_path)) 

615 win = {} 

616 win[hiLoKey[hilo][0]] = [0] 

617 else: 

618 win = {} 

619 win[hiLoKey[hilo][0]] = [0] 

620 for hl in hiLoKey[hilo]: 

621 if hl in win and len(win[hl])>0: 

622 potHiLo = Decimal(int(potBoard/len(win)*factor))/factor 

623 modHiLo = potBoard - potHiLo*len(win) 

624 if len(win)==1 or hl=='hi': 

625 potHiLo+=modHiLo 

626 potSplit = Decimal(int(potHiLo/len(win[hl])*factor))/factor 

627 modSplit = potHiLo - potSplit*len(win[hl]) 

628 pnames = players if len(holeplayers)==0 else [holeplayers[w] for w in win[hl]] 

629 for p in pnames: 

630 ppot = potSplit 

631 if modSplit>0: 

632 cent = (Decimal('0.01') * (100/factor)) 

633 ppot += cent 

634 modSplit -= cent 

635 rake = (totalrake * (ppot/hand.totalpot)).quantize(Decimal("0.01"), rounding=ROUND_DOWN) 

636 hand.addCollectPot(player=p,pot=(ppot-rake)) 

637 

638 def assembleHandsPots(self, hand): 

639 # init  

640 category = hand.gametype['category'] 

641 positions = [] 

642 playersPots = {} 

643 potFound = {} 

644 positionDict = {} 

645 showdown = False 

646 

647 #debug 

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

649 

650 # Init player's pot dict 

651 for p in hand.players: 

652 playersPots[p[1]] = [0, []] 

653 potFound[p[1]] = [0, 0] 

654 position = self.handsplayers[p[1]]['position'] 

655 

656 # check if player has a position 

657 if position is None or position == '': 

658 #print(f"warning: player {p[1]} has no position") 

659 position = 'Unknown' # default 

660 

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

662 positions.append(str(position)) 

663 

664 if self.handsplayers[p[1]]['sawShowdown']: 

665 showdown = True 

666 

667 #debug 

668 #print("positionDict:", positionDict) 

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

670 

671 # sort positions 

672 positions.sort(reverse=True) 

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

674 

675 # define factor 

676 factor = 100 

677 if ((hand.gametype["type"] == "tour" or 

678 (hand.gametype["type"] == "ring" and 

679 (hand.gametype["currency"] == "play" and 

680 (hand.sitename not in ('Winamax', 'PacificPoker'))))) and 

681 (not [n for (n, v) in hand.pot.pots if (n % Decimal('1.00')) != 0])): 

682 factor = 1 

683 

684 # Config game 

685 hiLoKey = {'h': ['hi'], 'l': ['low'], 'r': ['low'], 's': ['hi', 'low']} 

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

687 

688 # process pots 

689 if ((hand.sitename != 'KingsClub' or hand.adjustCollected) and 

690 evalgame and 

691 (len(hand.pot.pots) > 1 or (showdown and (hilo == 's' or hand.runItTimes >= 2)))): 

692 

693 rakes, totrake, potId = {}, 0, 0 

694 

695 for pot, players in hand.pot.pots: 

696 if potId == 0: 

697 pot += (sum(hand.pot.common.values()) + hand.pot.stp) 

698 potId += 1 

699 

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

701 

702 for b in boards: 

703 boardId += (hand.runItTimes >= 2) 

704 potBoard = Decimal(int(pot / len(boards) * factor)) / factor 

705 modBoard = pot - potBoard * len(boards) 

706 if boardId == 1: 

707 potBoard += modBoard 

708 

709 holeplayers, holecards = [], [] 

710 for p in players: 

711 hcs = hand.join_holecards(p, asList=True) 

712 holes = [str(c) for c in hcs[hrange[-1][0]:hrange[-1][1]] if Card.encodeCardList.get(c)!=None or c=='0x'] 

713 board = [str(c) for c in b if 'omaha' in evalgame] 

714 if 'omaha' not in evalgame: 

715 holes = holes + [str(c) for c in b if base=='hold'] 

716 if '0x' not in holes and '0x' not in board: 

717 holecards.append(holes) 

718 holeplayers.append(p) 

719 if len(holecards)>1: 

720 try: 

721 win = pokereval.winners(game = evalgame, pockets = holecards, board = board) 

722 except RuntimeError: 

723 log.error("assembleHandsPots: error evaluating winners for hand %s %s" % (hand.handid, hand.in_path)) 

724 win = {} 

725 win[hiLoKey[hilo][0]] = [0] 

726 else: 

727 win = {} 

728 win[hiLoKey[hilo][0]] = [0] 

729 for hl in hiLoKey[hilo]: 

730 if hl in win and len(win[hl])>0: 

731 potHiLo = old_div(Decimal(int(potBoard/len(win)*factor)),factor) 

732 modHiLo = potBoard - potHiLo*len(win) 

733 if len(win)==1 or hl=='hi': 

734 potHiLo+=modHiLo 

735 potSplit = old_div(Decimal(int(potHiLo/len(win[hl])*factor)),factor) 

736 modSplit = potHiLo - potSplit*len(win[hl]) 

737 pnames = players if len(holeplayers)==0 else [holeplayers[w] for w in win[hl]] 

738 for n in positions: 

739 #print(f"check position position: {n}") 

740 if positionDict[n] in pnames: 

741 pname = positionDict[n] 

742 ppot = potSplit 

743 if modSplit>0: 

744 cent = (Decimal('0.01') * (old_div(100,factor))) 

745 ppot += cent 

746 modSplit -= cent 

747 playersPots[pname][0] += ppot 

748 potFound[pname][0] += ppot 

749 data = {'potId': potId, 'boardId': boardId, 'hiLo': hl,'ppot':ppot, 'winners': [m for m in pnames if pname!=n], 'mod': ppot>potSplit} 

750 playersPots[pname][1].append(data) 

751 self.handsplayers[pname]['rake'] = 0 

752 

753 for p, (total, info) in list(playersPots.items()): 

754 #log.error((p, (total, info))) 

755 if hand.collectees.get(p) and info: 

756 potFound[p][1] = hand.collectees.get(p) 

757 for item in info: 

758 #log.error((str(hand.handid)," winners: ",item['winners'])) 

759 split = [n for n in item['winners'] if len(playersPots[n][1])==1 and hand.collectees.get(n)!=None] 

760 if len(info)==1: 

761 ppot = item['ppot'] 

762 rake = ppot - hand.collectees[p] 

763 collected = hand.collectees[p] 

764 elif item==info[-1]: 

765 ppot, collected = potFound[p] 

766 rake = ppot - collected 

767 elif len(split)>0 and not item['mod']: 

768 ppot = item['ppot'] 

769 collected = min([hand.collectees[s] for s in split] + [ppot]) 

770 rake = ppot - collected 

771 else: 

772 ppot = item['ppot'] 

773 totalrake = total - hand.collectees[p] 

774 rake = (totalrake * (old_div(ppot,total))).quantize(Decimal("0.01")) 

775 collected = ppot - rake 

776 potFound[p][0] -= ppot 

777 potFound[p][1] -= collected 

778 insert = [None, item['potId'], item['boardId'], item['hiLo'][0], hand.dbid_pids[p], int(item['ppot']*100), int(collected*100), int(rake*100)] 

779 self.handspots.append(insert) 

780 self.handsplayers[p]['rake'] += int(rake*100) 

781 

782 #debug  

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

784 

785 def setPositions(self, hand): 

786 """Sets the position for each player in HandsPlayers 

787 any blinds are negative values, and the last person to act on the 

788 first betting round is 0 

789 NOTE: HU, both values are negative for non-stud games 

790 NOTE2: I've never seen a HU stud match""" 

791 actions = hand.actions[hand.holeStreets[0]] 

792 # Note: pfbao list may not include big blind if all others folded 

793 players = self.pfbao(actions) 

794 

795 # set blinds first, then others from pfbao list, avoids problem if bb 

796 # is missing from pfbao list or if there is no small blind 

797 sb, bb, bi, ub, st = False, False, False, False, False 

798 if hand.gametype['base'] == 'stud': 

799 # Stud position is determined after cards are dealt 

800 # First player to act is always the bring-in position in stud 

801 # even if they decided to bet/completed 

802 if len(hand.actions[hand.actionStreets[1]])>0: 

803 bi = [hand.actions[hand.actionStreets[1]][0][0]] 

804 #else: 

805 # TODO fix: if ante all and no actions and no bring in 

806 # bi = [hand.actions[hand.actionStreets[0]][0][0]] 

807 else: 

808 ub = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'button blind'] 

809 bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'big blind'] 

810 sb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'small blind'] 

811 st = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] == 'straddle'] 

812 

813 # if there are > 1 sb or bb only the first is used! 

814 if ub: 

815 self.handsplayers[ub[0]]['street0InPosition'] = True 

816 if ub[0] not in players: players.append(ub[0]) 

817 if bb: 

818 self.handsplayers[bb[0]]['position'] = 'B' 

819 self.handsplayers[bb[0]]['street0InPosition'] = True 

820 if bb[0] in players: players.remove(bb[0]) 

821 if sb: 

822 self.handsplayers[sb[0]]['position'] = 'S' 

823 self.handsplayers[sb[0]]['street0FirstToAct'] = True 

824 if sb[0] in players: players.remove(sb[0]) 

825 if bi: 

826 self.handsplayers[bi[0]]['position'] = 'S' 

827 self.handsplayers[bi[0]]['street0FirstToAct'] = True 

828 if bi[0] in players: players.remove(bi[0]) 

829 if st and st[0] in players: 

830 players.insert(0, players.pop()) 

831 

832 #print "DEBUG: actions: '%s'" % actions 

833 #print "DEBUG: ub: '%s' bb: '%s' sb: '%s' bi: '%s' plyrs: '%s'" %(ub, bb, sb, bi, players) 

834 for i,player in enumerate(reversed(players)): 

835 self.handsplayers[player]['position'] = i 

836 self.hands['maxPosition'] = i 

837 if i==0 and hand.gametype['base'] == 'stud': 

838 self.handsplayers[player]['street0InPosition'] = True 

839 elif (i-1)==len(players): 

840 self.handsplayers[player]['street0FirstToAct'] = True 

841 

842 

843 def assembleHudCache(self, hand): 

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

845 pass 

846 

847 def vpip(self, hand): 

848 vpipers = set() 

849 bb = [x[0] for x in hand.actions[hand.actionStreets[0]] if x[1] in ('big blind', 'button blind')] 

850 for act in hand.actions[hand.actionStreets[1]]: 

851 if act[1] in ('calls','bets', 'raises', 'completes'): 

852 vpipers.add(act[0]) 

853 

854 self.hands['playersVpi'] = len(vpipers) 

855 

856 for player in hand.players: 

857 pname = player[1] 

858 player_stats = self.handsplayers.get(pname) 

859 if pname in vpipers: 

860 player_stats['street0VPI'] = True 

861 elif pname in hand.sitout: 

862 player_stats['street0VPIChance'] = False 

863 player_stats['street0AggrChance'] = False 

864 

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

866 self.handsplayers[bb[0]]['street0VPIChance'] = False 

867 self.handsplayers[bb[0]]['street0AggrChance'] = False 

868 

869 def playersAtStreetX(self, hand): 

870 """ playersAtStreet1 SMALLINT NOT NULL, /* num of players seeing flop/street4/draw1 */""" 

871 # self.actions[street] is a list of all actions in a tuple, contining the player name first 

872 # [ (player, action, ....), (player2, action, ...) ] 

873 # The number of unique players in the list per street gives the value for playersAtStreetXXX 

874 

875 # FIXME?? - This isn't couting people that are all in - at least showdown needs to reflect this 

876 # ... new code below hopefully fixes this 

877 # partly fixed, allins are now set as seeing streets because they never do a fold action 

878 

879 self.hands['playersAtStreet1'] = 0 

880 self.hands['playersAtStreet2'] = 0 

881 self.hands['playersAtStreet3'] = 0 

882 self.hands['playersAtStreet4'] = 0 

883 self.hands['playersAtShowdown'] = 0 

884 

885# alliners = set() 

886# for (i, street) in enumerate(hand.actionStreets[2:]): 

887# actors = set() 

888# for action in hand.actions[street]: 

889# if len(action) > 2 and action[-1]: # allin 

890# alliners.add(action[0]) 

891# actors.add(action[0]) 

892# if len(actors)==0 and len(alliners)<2: 

893# alliners = set() 

894# self.hands['playersAtStreet%d' % (i+1)] = len(set.union(alliners, actors)) 

895# 

896# actions = hand.actions[hand.actionStreets[-1]] 

897# print "p_actions:", self.pfba(actions), "p_folds:", self.pfba(actions, l=('folds',)), "alliners:", alliners 

898# pas = set.union(self.pfba(actions) - self.pfba(actions, l=('folds',)), alliners) 

899 

900 # hand.players includes people that are sitting out on some sites for cash games 

901 # actionStreets[1] is 'DEAL', 'THIRD', 'PREFLOP', so any player dealt cards 

902 # must act on this street if dealt cards. Almost certainly broken for the 'all-in blind' case 

903 # and right now i don't care - CG 

904 

905 p_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]]) 

906 #Add in players who were allin blind 

907 if hand.pot.pots: 

908 if len(hand.pot.pots[0][1])>1: p_in = p_in.union(hand.pot.pots[0][1]) 

909 

910 # 

911 # discover who folded on each street and remove them from p_in 

912 # 

913 # i values as follows 0=BLINDSANTES 1=PREFLOP 2=FLOP 3=TURN 4=RIVER 

914 # (for flop games) 

915 # 

916 # At the beginning of the loop p_in contains the players with cards 

917 # at the start of that street. 

918 # p_in is reduced each street to become a list of players still-in 

919 # e.g. when i=1 (preflop) all players who folded during preflop 

920 # are found by pfba() and eliminated from p_in. 

921 # Therefore at the end of the loop, p_in contains players remaining 

922 # at the end of the action on that street, and can therefore be set 

923 # as the value for the number of players who saw the next street 

924 # 

925 # note that i is 1 in advance of the actual street numbers in the db 

926 # 

927 # if p_in reduces to 1 player, we must bomb-out immediately 

928 # because the hand is over, this will ensure playersAtStreetx 

929 # is accurate. 

930 # 

931 

932 for (i, street) in enumerate(hand.actionStreets): 

933 

934 if (i-1) in (1,2,3,4): 

935 # p_in stores players with cards at start of this street, 

936 # so can set streetxSeen & playersAtStreetx with this information 

937 # This hard-coded for i-1 =1,2,3,4 because those are the only columns 

938 # in the db! this code section also replaces seen() - more info log 66 

939 # nb i=2=flop=street1Seen, hence i-1 term needed 

940 self.hands['playersAtStreet%d' % (i-1)] = len(p_in) 

941 for player_with_cards in p_in: 

942 self.handsplayers[player_with_cards]['street%sSeen' % (i-1)] = True 

943 

944 players = self.pfbao(hand.actions[street], f=('discards','stands pat')) 

945 if len(players)>0: 

946 self.handsplayers[players[0]]['street%dFirstToAct' % (i-1)] = True 

947 self.handsplayers[players[-1]]['street%dInPosition' % (i-1)] = True 

948 # 

949 # find out who folded, and eliminate them from p_in 

950 # 

951 actions = hand.actions[street] 

952 p_in = p_in - self.pfba(actions, l=('folds',)) 

953 # 

954 # if everyone folded, we are done, so exit this method 

955 # 

956 if len(p_in) == 1: 

957 if (i-1) in (1,2,3,4) and len(players)>0 and list(p_in)[0] not in players: 

958 # corrects which player is "in position" 

959 # if everyone folds before the last player could act 

960 self.handsplayers[players[-1]]['street%dInPosition' % (i-1)] = False 

961 self.handsplayers[list(p_in)[0]]['street%dInPosition' % (i-1)] = True 

962 return None 

963 

964 # 

965 # The remaining players in p_in reached showdown (including all-ins 

966 # because they never did a "fold" action in pfba() above) 

967 # 

968 self.hands['playersAtShowdown'] = len(p_in) 

969 for showdown_player in p_in: 

970 self.handsplayers[showdown_player]['sawShowdown'] = True 

971 

972 def streetXRaises(self, hand): 

973 # self.actions[street] is a list of all actions in a tuple, contining the action as the second element 

974 # [ (player, action, ....), (player2, action, ...) ] 

975 # No idea what this value is actually supposed to be 

976 # In theory its "num small bets paid to see flop/street4, including blind" which makes sense for limit. Not so useful for nl 

977 # Leaving empty for the moment, 

978 

979 for i in range(5): self.hands['street%dRaises' % i] = 0 

980 

981 for (i, street) in enumerate(hand.actionStreets[1:]): 

982 self.hands['street%dRaises' % i] = len([action for action in hand.actions[street] if action[1] in ('raises','bets','completes')]) 

983 

984 def calcSteals(self, hand): 

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

986 

987 Steal attempt - open raise on positions 1 0 S - i.e. CO, BU, SB 

988 (note: I don't think PT2 counts SB steals in HU hands, maybe we shouldn't?) 

989 Fold to steal - folding blind after steal attemp wo any other callers or raisers 

990 """ 

991 steal_attempt = False 

992 raised = False 

993 if hand.gametype['base'] == 'stud': 

994 steal_positions = (2, 1, 0) 

995 elif len([x for x in hand.actions[hand.actionStreets[0]] if x[1] == 'button blind']) > 0: 

996 steal_positions = (3, 2, 1) 

997 else: 

998 steal_positions = (1, 0, 'S') 

999 for action in hand.actions[hand.actionStreets[1]]: 

1000 pname, act = action[0], action[1] 

1001 player_stats = self.handsplayers.get(pname) 

1002 if player_stats['sitout']: continue 

1003 posn = player_stats['position'] 

1004 #print "\naction:", action[0], posn, type(posn), steal_attempt, act 

1005 if posn == 'B': 

1006 #NOTE: Stud games will never hit this section 

1007 if steal_attempt: 

1008 player_stats['foldBbToStealChance'] = True 

1009 player_stats['raiseToStealChance'] = True 

1010 player_stats['foldedBbToSteal'] = act == 'folds' 

1011 player_stats['raiseToStealDone'] = act == 'raises' 

1012 self.handsplayers[stealer]['success_Steal'] = act == 'folds' 

1013 break 

1014 elif posn == 'S': 

1015 player_stats['raiseToStealChance'] = steal_attempt 

1016 player_stats['foldSbToStealChance'] = steal_attempt 

1017 player_stats['foldedSbToSteal'] = steal_attempt and act == 'folds' 

1018 player_stats['raiseToStealDone'] = steal_attempt and act == 'raises' 

1019 if steal_attempt: 

1020 self.handsplayers[stealer]['success_Steal'] = act == 'folds' and hand.gametype['base'] == 'stud' 

1021 

1022 if steal_attempt and act != 'folds': 

1023 break 

1024 

1025 if not steal_attempt and not raised and not act in ('bringin'): 

1026 player_stats['raiseFirstInChance'] = True 

1027 if posn in steal_positions: 

1028 player_stats['stealChance'] = True 

1029 if act in ('bets', 'raises', 'completes'): 

1030 player_stats['raisedFirstIn'] = True 

1031 raised = True 

1032 if posn in steal_positions: 

1033 player_stats['stealDone'] = True 

1034 steal_attempt = True 

1035 stealer = pname 

1036 if act == 'calls': 

1037 break 

1038 

1039 if posn not in steal_positions and act not in ('folds', 'bringin'): 

1040 break 

1041 

1042 def calc34BetStreet0(self, hand): 

1043 """Fills street0_(3|4)B(Chance|Done), other(3|4)BStreet0""" 

1044 if hand.gametype['base'] == 'stud': 

1045 bet_level = 0 # bet_level after 3-bet is equal to 3 

1046 else: 

1047 bet_level = 1 # bet_level after 3-bet is equal to 3 

1048 squeeze_chance, raise_chance, action_cnt, first_agressor = False, True, {}, None 

1049 p0_in = set([x[0] for x in hand.actions[hand.actionStreets[0]] if not x[-1]]) 

1050 p1_in = set([x[0] for x in hand.actions[hand.actionStreets[1]]]) 

1051 p_in = p1_in.union(p0_in) 

1052 for p in p_in: action_cnt[p] = 0 

1053 for action in hand.actions[hand.actionStreets[1]]: 

1054 pname, act, aggr, allin = action[0], action[1], action[1] in ('raises', 'bets', 'completes'), False 

1055 player_stats = self.handsplayers.get(pname) 

1056 action_cnt[pname] += 1 

1057 if len(action) > 3 and act != 'discards': 

1058 allin = action[-1] 

1059 if len(p_in)==1 and action_cnt[pname]==1: 

1060 raise_chance = False 

1061 player_stats['street0AggrChance'] = raise_chance 

1062 if act == 'folds' or allin or player_stats['sitout']: 

1063 p_in.discard(pname) 

1064 if player_stats['sitout']: continue 

1065 if bet_level == 0: 

1066 if aggr: 

1067 if first_agressor == None: 

1068 first_agressor = pname 

1069 bet_level += 1 

1070 continue 

1071 elif bet_level == 1: 

1072 player_stats['street0_2BChance'] = raise_chance 

1073 if aggr: 

1074 if first_agressor == None: 

1075 first_agressor = pname 

1076 player_stats['street0_2BDone'] = True 

1077 bet_level += 1 

1078 continue 

1079 elif bet_level == 2: 

1080 player_stats['street0_3BChance'] = raise_chance 

1081 player_stats['street0_SqueezeChance'] = squeeze_chance 

1082 if pname == first_agressor: 

1083 player_stats['street0_FoldTo2BChance'] = True 

1084 if act == 'folds': 

1085 player_stats['street0_FoldTo2BDone'] = True 

1086 if not squeeze_chance and act == 'calls': 

1087 squeeze_chance = True 

1088 continue 

1089 if aggr: 

1090 player_stats['street0_3BDone'] = True 

1091 player_stats['street0_SqueezeDone'] = squeeze_chance 

1092 second_agressor = pname 

1093 bet_level += 1 

1094 continue 

1095 elif bet_level == 3: 

1096 if pname == first_agressor: 

1097 player_stats['street0_4BChance'] = raise_chance 

1098 player_stats['street0_FoldTo3BChance'] = True 

1099 if aggr: 

1100 player_stats['street0_4BDone'] = raise_chance 

1101 bet_level += 1 

1102 elif act == 'folds': 

1103 player_stats['street0_FoldTo3BDone'] = True 

1104 break 

1105 else: 

1106 player_stats['street0_C4BChance'] = raise_chance 

1107 if aggr: 

1108 player_stats['street0_C4BDone'] = raise_chance 

1109 bet_level += 1 

1110 continue 

1111 elif bet_level == 4: 

1112 if pname != first_agressor: 

1113 player_stats['street0_FoldTo4BChance'] = True 

1114 if act == 'folds': 

1115 player_stats['street0_FoldTo4BDone'] = True 

1116 

1117 def calcCBets(self, hand): 

1118 """Fill streetXCBChance, streetXCBDone, foldToStreetXCBDone, foldToStreetXCBChance 

1119 

1120 Continuation Bet chance, action: 

1121 Had the last bet (initiative) on previous street, got called, close street action 

1122 Then no bets before the player with initiatives first action on current street 

1123 ie. if player on street-1 had initiative and no donkbets occurred 

1124 """ 

1125 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start' 

1126 # came there 

1127 #for i, street in enumerate(hand.actionStreets[2:], start=1): 

1128 for i, street in enumerate(hand.actionStreets[2:]): 

1129 name = self.lastBetOrRaiser(hand.actions, hand.actionStreets[i+1]) # previous street 

1130 if name: 

1131 chance = self.noBetsBefore(hand.actions, hand.actionStreets[i+2], name) # this street 

1132 if chance == True: 

1133 player_stats = self.handsplayers.get(name) 

1134 player_stats['street%dCBChance' % (i+1)] = True 

1135 player_stats['street%dCBDone' % (i+1)] = self.betStreet(hand.actions, hand.actionStreets[i+2], name) 

1136 if player_stats['street%dCBDone' % (i+1)]: 

1137 for pname, folds in list(self.foldTofirstsBetOrRaiser(hand.actions, street, name).items()): 

1138 #print "DEBUG:", hand.handid, pname.encode('utf8'), street, folds, '--', name, 'lastbet on ', hand.actionStreets[i+1] 

1139 self.handsplayers[pname]['foldToStreet%sCBChance' % (i+1)] = True 

1140 self.handsplayers[pname]['foldToStreet%sCBDone' % (i+1)] = folds 

1141 

1142 def calcCalledRaiseStreet0(self, hand): 

1143 """ 

1144 Fill street0CalledRaiseChance, street0CalledRaiseDone 

1145 For flop games, go through the preflop actions: 

1146 skip through first raise 

1147 For each subsequent action: 

1148 if the next action is fold : 

1149 player chance + 1 

1150 if the next action is raise : 

1151 player chance + 1 

1152 if the next non-fold action is call : 

1153 player chance + 1 

1154 player done + 1 

1155 skip through list to the next raise action 

1156 """ 

1157 

1158 fast_forward = True 

1159 for tupleread in hand.actions[hand.actionStreets[1]]: 

1160 action = tupleread[1] 

1161 if fast_forward: 

1162 if action in ('raises', 'completes'): 

1163 fast_forward = False # raisefound, end fast-forward 

1164 else: 

1165 player = tupleread[0] 

1166 player_stats = self.handsplayers.get(player) 

1167 player_stats['street0CalledRaiseChance'] += 1 

1168 if action == 'calls': 

1169 player_stats['street0CalledRaiseDone'] += 1 

1170 fast_forward = True 

1171 

1172 def calcCheckCallRaise(self, hand): 

1173 """Fill streetXCheckCallRaiseChance, streetXCheckCallRaiseDone 

1174 

1175 streetXCheckCallRaiseChance = got raise/bet after check 

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

1177 

1178 CG: CheckCall would be a much better name for this. 

1179 """ 

1180 # XXX: enumerate(list, start=x) is python 2.6 syntax; 'start' 

1181 #for i, street in enumerate(hand.actionStreets[2:], start=1): 

1182 for i, street in enumerate(hand.actionStreets[2:]): 

1183 actions = hand.actions[street] 

1184 checkers = set() 

1185 acted = set() 

1186 initial_raiser = None 

1187 for action in actions: 

1188 pname, act = action[0], action[1] 

1189 if act in ('bets', 'raises') and initial_raiser is None: 

1190 initial_raiser = pname 

1191 elif act == 'checks' and initial_raiser is None: 

1192 checkers.add(pname) 

1193 elif initial_raiser is not None and pname in checkers and pname not in acted: 

1194 player_stats = self.handsplayers.get(pname) 

1195 player_stats['street%dCheckCallRaiseChance' % (i+1)] = True 

1196 player_stats['street%dCheckCallDone' % (i+1)] = act=='calls' 

1197 player_stats['street%dCheckRaiseDone' % (i+1)] = act=='raises' 

1198 acted.add(pname) 

1199 

1200 def aggr(self, hand, i): 

1201 aggrers = set() 

1202 others = set() 

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

1204 

1205 firstAggrMade=False 

1206 for act in hand.actions[hand.actionStreets[i+1]]: 

1207 if firstAggrMade: 

1208 others.add(act[0]) 

1209 if act[1] in ('completes', 'bets', 'raises'): 

1210 aggrers.add(act[0]) 

1211 firstAggrMade=True 

1212 

1213 for player in hand.players: 

1214 if player[1] in aggrers: 

1215 self.handsplayers[player[1]]['street%sAggr' % i] = True 

1216 

1217 if len(aggrers)>0 and i>0: 

1218 for playername in others: 

1219 self.handsplayers[playername]['otherRaisedStreet%s' % i] = True 

1220 #print "otherRaised detected on handid "+str(hand.handid)+" for "+playername+" on street "+str(i) 

1221 

1222 if i > 0 and len(aggrers) > 0: 

1223 for playername in others: 

1224 self.handsplayers[playername]['otherRaisedStreet%s' % i] = True 

1225 #print "DEBUG: otherRaised detected on handid %s for %s on actionStreet[%s]: %s"  

1226 # %(hand.handid, playername, hand.actionStreets[i+1], i) 

1227 

1228 def calls(self, hand, i): 

1229 callers = [] 

1230 for act in hand.actions[hand.actionStreets[i+1]]: 

1231 if act[1] in ('calls'): 

1232 player_stats = self.handsplayers.get(act[0]) 

1233 player_stats['street%sCalls' % i] = 1 + player_stats['street%sCalls' % i] 

1234 

1235 def bets(self, hand, i): 

1236 for act in hand.actions[hand.actionStreets[i+1]]: 

1237 if act[1] in ('bets'): 

1238 player_stats = self.handsplayers.get(act[0]) 

1239 player_stats['street%sBets' % i] = 1 + player_stats['street%sBets' % i] 

1240 

1241 def raises(self, hand, i): 

1242 for act in hand.actions[hand.actionStreets[i+1]]: 

1243 if act[1] in ('completes', 'raises'): 

1244 player_stats = self.handsplayers.get(act[0]) 

1245 player_stats['street%sRaises' % i] = 1 + player_stats['street%sRaises' % i] 

1246 

1247 def folds(self, hand, i): 

1248 for act in hand.actions[hand.actionStreets[i+1]]: 

1249 if act[1] in ('folds'): 

1250 player_stats = self.handsplayers.get(act[0]) 

1251 if player_stats['otherRaisedStreet%s' % i] == True: 

1252 player_stats['foldToOtherRaisedStreet%s' % i] = True 

1253 #print "DEBUG: fold detected on handid %s for %s on actionStreet[%s]: %s" 

1254 # %(hand.handid, act[0],hand.actionStreets[i+1], i) 

1255 

1256 def countPlayers(self, hand): 

1257 pass 

1258 

1259 def pfba(self, actions, f=None, l=None): 

1260 """Helper method. Returns set of PlayersFilteredByActions 

1261 

1262 f - forbidden actions 

1263 l - limited to actions 

1264 """ 

1265 players = set() 

1266 for action in actions: 

1267 if l is not None and action[1] not in l: continue 

1268 if f is not None and action[1] in f: continue 

1269 players.add(action[0]) 

1270 return players 

1271 

1272 def pfbao(self, actions, f=None, l=None, unique=True): 

1273 """Helper method. Returns set of PlayersFilteredByActionsOrdered 

1274 

1275 f - forbidden actions 

1276 l - limited to actions 

1277 """ 

1278 # Note, this is an adaptation of function 5 from: 

1279 # http://www.peterbe.com/plog/uniqifiers-benchmark 

1280 seen = {} 

1281 players = [] 

1282 for action in actions: 

1283 if l is not None and action[1] not in l: continue 

1284 if f is not None and action[1] in f: continue 

1285 if action[0] in seen and unique: continue 

1286 seen[action[0]] = 1 

1287 players.append(action[0]) 

1288 return players 

1289 

1290 def calcEffectiveStack(self,hand): 

1291 """Calculates the effective stack for each player on street 1 

1292 """ 

1293 seen = {} 

1294 pstacks = {} 

1295 actions = hand.actions[hand.holeStreets[0]] 

1296 for p in hand.players: 

1297 if p[1] not in hand.sitout: 

1298 pstacks[p[1]] = int(100 * Decimal(p[2])) 

1299 for action in actions: 

1300 if action[0] in seen: continue 

1301 if action[0] not in pstacks: continue 

1302 seen[action[0]] = 1 

1303 oppstacks = [v for (k,v) in list(pstacks.items()) if k != action[0]] 

1304 if oppstacks: 

1305 if pstacks[action[0]] > max(oppstacks): 

1306 self.handsplayers[action[0]]['effStack'] = max(oppstacks) 

1307 else: 

1308 self.handsplayers[action[0]]['effStack'] = pstacks[action[0]] 

1309 if action[1] == 'folds': 

1310 pstacks[action[0]] = 0 

1311 

1312 def firstsBetOrRaiser(self, actions): 

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

1314 

1315 None if there were no bets or raises on that street 

1316 """ 

1317 for act in actions: 

1318 if act[1] in ('bets', 'raises', 'completes'): 

1319 return act[0] 

1320 return None 

1321 

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

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

1324 

1325 None if there were no bets or raises on that street 

1326 """ 

1327 i, players = 0, {} 

1328 for act in actions[street]: 

1329 if i>1: break 

1330 if act[0] != aggressor: 

1331 if act[1] == 'folds': 

1332 players[act[0]] = True 

1333 else: 

1334 players[act[0]] = False 

1335 if act[1] == 'raises' or act[1] == 'completes': 

1336 break 

1337 elif act[1] not in ('discards', 'stands pat'): 

1338 i += 1 

1339 return players 

1340 

1341 def lastBetOrRaiser(self, actions, street): 

1342 """Returns player name that placed the last bet or raise for that street. 

1343 None if there were no bets or raises on that street""" 

1344 lastbet = None 

1345 for act in actions[street]: 

1346 if act[1] in ('bets', 'raises', 'completes'): 

1347 lastbet = act[0] 

1348 return lastbet 

1349 

1350 

1351 def noBetsBefore(self, actions, street, player): 

1352 """Returns true if there were no bets before the specified players turn, false otherwise""" 

1353 noBetsBefore = False 

1354 for act in actions[street]: 

1355 #Must test for player first in case UTG 

1356 if act[0] == player: 

1357 noBetsBefore = True 

1358 break 

1359 if act[1] in ('bets', 'raises', 'completes'): 

1360 break 

1361 return noBetsBefore 

1362 

1363 

1364 def betStreet(self, actions, street, player): 

1365 """Returns true if player bet/raised the street as their first action""" 

1366 betOrRaise = False 

1367 for act in actions[street]: 

1368 if act[0] == player and act[1] not in ('discards', 'stands pat'): 

1369 if act[1] in ('bets', 'raises', 'completes'): 

1370 betOrRaise = True 

1371 else: 

1372 # player found but did not bet or raise as their first action 

1373 pass 

1374 break 

1375 #else: 

1376 # haven't found player's first action yet 

1377 return betOrRaise