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