def board_payment(self, board_item, board_before, board_after): """ Payments in compensation of change of equity due to board cards. """ range_map = {key: HandRange(txt) for key, txt in self.ranges.iteritems() if key in self.remaining_userids} equity_map_before, _ = showdown_equity(range_map, board_before) equity_map_after, _ = showdown_equity(range_map, board_after) for userid in self.remaining_userids: # payment equal to what was lost amount = self.pot * (equity_map_before[userid] - equity_map_after[userid]) * board_item.factor logging.debug('gameid %d, order %d, userid %d, board payment: ' 'pot %d * (before %0.4f - after %0.4f) * factor %0.4f' ' = amount %0.8f', board_item.gameid, board_item.order, userid, self.pot, equity_map_before[userid], equity_map_after[userid], board_item.factor, amount) payment = PaymentToPlayer() payment.reason = PaymentToPlayer.REASON_BOARD payment.gameid = board_item.gameid payment.order = board_item.order payment.userid = userid payment.amount = amount self.session.add(payment)
def pot_payment(self, action_item, contribution): """ A simple payment from a player to the pot. """ if contribution == 0: return amount = -action_item.factor * contribution logging.debug('gameid %d, order %d, userid %d, pot payment: ' 'factor %0.4f * contribution %d = amount %0.8f', action_item.gameid, action_item.order, action_item.userid, action_item.factor, contribution, amount) payment = PaymentToPlayer() payment.reason = PaymentToPlayer.REASON_POT payment.gameid = action_item.gameid payment.order = action_item.order payment.userid = action_item.userid payment.amount = amount self.session.add(payment)
def showdown_call(self, gameid, order, caller, call_cost, call_ratio, factor): """ This is a call that doesn't really happen (it's not in the game tree main branch), but it's terminal (like a fold), and we pay out showdowns, but to do so we also need to charge for the call that doesn't happen. """ payment = PaymentToPlayer() payment.reason = PaymentToPlayer.REASON_SHOWDOWN_CALL payment.gameid = gameid payment.order = order payment.userid = caller payment.amount = -call_cost * factor * call_ratio logging.debug('gameid %d, order %d, userid %d, showdown call payment: ' 'call cost %d * factor %0.4f * call ratio %0.4f = ' 'amount %0.8f', gameid, order, caller, call_cost, factor, call_ratio, payment.amount) self.session.add(payment)
def showdown_payments(self, showdown, equities): """ Create showdown payments for all participants of this showdown. """ logging.debug('gameid %d, order %d, creating showdown payments', showdown.gameid, showdown.order) for participant in equities: payment = PaymentToPlayer() payment.reason = PaymentToPlayer.REASON_SHOWDOWN payment.gameid = showdown.gameid payment.order = showdown.order payment.userid = participant.userid payment.amount = showdown.factor * showdown.pot * participant.equity logging.debug('gameid %d, order %d, userid %d, showdown payment: ' 'factor %0.4f * pot %d * equity %0.4f = ' 'amount %0.8f', showdown.gameid, showdown.order, participant.userid, showdown.factor, showdown.pot, participant.equity, payment.amount) self.session.add(payment)
def fold_equity_payments(self, range_action, fold_ratio): """ Fold equity payment occurs for every range action with only two players remaining, and is a payment equal to the current pot multiplied by the current factor multiplied by the fold ratio (up to and including 100% of the pot, e.g. when in a HU situation BTN open folds 100%). This is not a payment from one player to the other. It is a payment the pot to the player who bet. The bettor gains a portion of the pot equal to the fold ratio, and the pot loses this by virtue of a reduction in the current factor. """ if len(self.remaining_userids) != 2: return # Note that this includes the bet amount from the betting player, an as- # yet unmatched bet. This is correct, because this is (for example) the # amount the betting player will win if this player folds 100% - no # more, no less. if not fold_ratio: logging.debug('gameid %d, order %d, fold ratio 0.0, ' 'skipping fold equity payments', range_action.gameid, range_action.order) return amount = self.pot * range_action.factor * fold_ratio assert range_action.userid in self.remaining_userids if self.remaining_userids[0] == range_action.userid: nonfolder = self.remaining_userids[1] else: nonfolder = self.remaining_userids[0] logging.debug('gameid %d, order %d, userid %d, fold equity payment: ' 'pot %d * factor %0.4f * fold ratio %0.4f = amount %0.8f', range_action.gameid, range_action.order, nonfolder, self.pot, range_action.factor, fold_ratio, amount) nonfolder_payment = PaymentToPlayer() nonfolder_payment.reason = PaymentToPlayer.REASON_FOLD_EQUITY nonfolder_payment.gameid = range_action.gameid nonfolder_payment.order = range_action.order nonfolder_payment.userid = nonfolder nonfolder_payment.amount = amount self.session.add(nonfolder_payment)
def equity_payments(self, item): """ Payments in compensation due to one player's range changing. Note that every remaining player gets a payment. This happens whenever a player has multiple "play continues" ranges. When heads up, this means any time before the river when someone has a passive range and an aggressive range. More generally, when three-handed, it can include folds. In fact, it could probably be generalised to include fold equity! (But note that fold equity is much less contentious!) (The issue with fold equity is that it requires us to scale down the rest of play, because we don't allow the fold line to happen, ever.) item is a GameHistoryRangeAction """ # Establish "old ranges", meaning after anything that can't happen # doesn't happen, but before it has been decided which of the things # that can happen will happen. userid = item.userid old_ranges = {k: HandRange(v) for k, v in self.ranges.iteritems() if k in self.remaining_userids} allowed = {'f', 'p', 'a'} if not self.fold_continues: allowed.discard('f') old_ranges[userid] = old_ranges[userid] \ .subtract(HandRange(self.prev_range_action.fold_range)) if not self.passive_continues: allowed.discard('p') old_ranges[userid] = old_ranges[userid] \ .subtract(HandRange(self.prev_range_action.passive_range)) if self.prev_range_action.fold_ratio == 0.0: allowed.discard('f') if self.prev_range_action.passive_ratio == 0.0: allowed.discard('p') if self.prev_range_action.aggressive_ratio == 0.0: allowed.discard('a') if len(allowed) <= 1: # one path, or zero paths (end of the game) logging.debug("gameid %d, order %d, no equity payments, " "allowed: %s", item.gameid, item.order, ','.join(allowed)) return # Establish "new ranges", what will actually be. new_ranges = dict(old_ranges) if item.is_fold: new_ranges[userid] = HandRange(self.prev_range_action.fold_range) if item.is_passive: new_ranges[userid] = HandRange(self.prev_range_action.passive_range) if item.is_aggressive: new_ranges[userid] = \ HandRange(self.prev_range_action.aggressive_range) # Calculate equities old_equities, _ = showdown_equity(old_ranges, self.board) new_equities, _ = showdown_equity(new_ranges, self.board) for userid in self.remaining_userids: # payment equal to what was lost # note the use of item.factor (new), not prev_range_action.factor amount = self.pot * (old_equities[userid] - new_equities[userid]) * item.factor logging.debug('gameid %d, order %d, userid %d, equity payment: ' 'pot %d * (before %0.4f - after %0.4f) * factor %0.4f' ' = amount %0.8f', item.gameid, item.order, userid, self.pot, old_equities[userid], new_equities[userid], item.factor, amount) payment = PaymentToPlayer() payment.reason = PaymentToPlayer.REASON_BRANCH payment.gameid = item.gameid payment.order = item.order payment.userid = userid payment.amount = amount self.session.add(payment)