Exemplo n.º 1
0
 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)
Exemplo n.º 2
0
 def analyse_showdown(self, ranges, order, is_passive, userids):
     """
     Create a showdown with given userids. Pre-river if pre-river.
     """
     showdowns = self.session.query(GameHistoryShowdown)  \
         .filter(GameHistoryShowdown.gameid == self.game.gameid)  \
         .filter(GameHistoryShowdown.order == order)  \
         .filter(GameHistoryShowdown.is_passive == is_passive).all()
     assert len(showdowns) == 1
     logging.debug("gameid %d, order %d, confirmed existing showdown",
                   self.game.gameid, order)
     showdown = showdowns[0]
     # TODO: REVISIT: this ignores ranges of folded players
     # It might make a difference in situations where a player has (for
     # example) limited their range to Ax and later folded, hence surely
     # removing an ace from the deck for the other players (significantly
     # changing their equities)
     # Actually, where it makes a difference, it would be really neat to see
     # it. Imagine someone saying "hey, this stupid site says I made a bad
     # call here with 23% equity when really I had 32% equity and it was a
     # great call!" Well no actually, the card removal effects of the folded
     # players change your equity, and you suck at poker.
     range_map = {k: v for k, v in ranges.iteritems() if k in userids}
     equity_map, iterations = showdown_equity(range_map, self.game.board)
     logging.debug('gameid %d, order %d, is_passive %r, factor %0.8f, '
                   'showdown with userids: %r, equity: %r '
                   '(iterations %d)',
                   self.game.gameid, order, is_passive, showdown.factor,
                   userids, equity_map, iterations)
     existing_equities = {p.showdown_order: p
         for p in showdown.participants}  #pylint:disable=no-member
     for showdown_order, userid in enumerate(userids):
         # create if not exist, otherwise update
         if showdown_order in existing_equities:
             participant = existing_equities[showdown_order]
         else:
             # TODO: REVISIT: this is ordered by situation player order,
             # not showdown order
             participant = GameHistoryShowdownEquity()
             self.session.add(participant)
             existing_equities[showdown_order] = participant
             participant.gameid = self.game.gameid
             participant.order = order
             participant.is_passive = is_passive
             participant.showdown_order = showdown_order
             participant.userid = userid
         participant.equity = equity_map[userid]
     self.showdown_payments(showdown=showdown,
                            equities=existing_equities.values())
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
    def calculate_combo_ev(self, combo, userid):
        """
        Calculate EV for a combo at this point in the game tree.
        """
        if self.children:
            # intermediate node, EV is combination of children's
            # oh, each child needs a weight / probability
            # oh, it's combo specific... "it depends" ;)

            # EV of combo is:
            # weighted sum of EV of children, but only for children where the
            # combo is present (where combo isn't present, probability is zero)

            # to assess probability:
            # - if this node's children are performed by this user:
            #   - EV is the EV of the action that contains this combo
            # - otherwise:
            #   - remove combo from the children's actor's current range
            #   - consider how many combo's of children's actor's current range
            #     proceed to each child
            #   - voila
            # This conveniently works even in a multi-way pot. It's a very good
            # approximation of the true probabilities. And note that truly
            # calculating the true probabilities is not possible.
            actor = self.children[0].actor
            if actor == userid:
                # EV is the EV of the action that contains this combo
                for node in self.children:
                    if range_contains_hand(node.ranges_by_userid[userid],
                                           combo):
                        return node.combo_ev(combo, userid)
                raise InvalidComboForTree('Combo not in child ranges for userid'
                    ' %d at betting line %s.' %
                    (userid, line_description(self.betting_line)))
            else:
                # probabilistic weighting of child EVs
                # size of bucket is probability of this child
                valid_children = [child for child in self.children
                    if range_contains_hand(child.ranges_by_userid[userid],
                                           combo)]
                buckets = {child: child.ranges_by_userid[actor]  \
                    .generate_options(Card.many_from_text(child.board) +
                                      list(combo))
                    for child in valid_children}
                total = len(concatenate(buckets.values()))
                if total == 0:
                    raise InvalidComboForTree('Combo not in child ranges for'
                        ' userid %d at betting line %s. ' % (userid,
                        line_description(self.betting_line)))
                probabilities = {child: 1.0 * len(buckets[child]) / total
                                 for child in valid_children}
                ev = sum(probabilities[child] * child.combo_ev(combo, userid)
                    for child in valid_children)
                return ev
                # Invalid combos are ignored / not calculated or aggregated.
        elif userid not in self.winners:
            # they folded
            return 0.0 - self.total_contrib[userid]
        elif len(self.winners) == 1:
            # uncontested pot
            return 0.0 + self.final_pot - self.total_contrib[userid]
        else:
            # showdown
            ranges = {userid: range_
                      for userid, range_ in self.ranges_by_userid.items()}
            combos = set([combo])
            description = unweighted_options_to_description(combos)
            ranges[userid] = HandRange(description)
            equities, _iteration =  \
                showdown_equity(ranges, Card.many_from_text(self.board),
                    hard_limit=10000)  # TODO: 1: this is not good enough
            equity = equities[userid]
            return equity * self.final_pot - self.total_contrib[userid]