def test_compare(self):
     """
     Test BestHandEval7 compare
     """
     hand1 = BestHandEval7(Card.many_from_text("AsTdTs5sAc5cQd"))
     hand2 = BestHandEval7(Card.many_from_text("AsTdTs5sAc5cJd"))
     self.assertTrue(hand1 > hand2)  # Q kicker beats J kicker
Exemple #2
0
 def do_test_re_deal(self, action, dealt, key, board, result, re_dealt,
                     iterations=1, delta=0.0):
     """
     Worker function for testing re_deal
     """
     # pylint:disable=R0913,R0914
     action2 = ActionDetails(HandRange(action[0]),
                             HandRange(action[1]),
                             HandRange(action[2]), 2)
     board2 = Card.many_from_text(board)
     re_dealt2 = [(HandRange(text).generate_options(board2), ratio)
                  for text, ratio in re_dealt]
     counts = [0 for _ in re_dealt2]
     for _ndx in xrange(iterations):
         dealt2 = {k: Card.many_from_text(val) for k, val in dealt.items()}
         _te, fpct, _pp, _ap = re_deal(action2, dealt2, key, board2, False,
                                     True)
         continue_ratio = 1.0 - fpct
         self.assertEqual(continue_ratio, result,
             msg="Expected continue ratio %0.3f but got %0.3f" %
                 (result, continue_ratio))
         for ndx, (options, ratio) in enumerate(re_dealt2):
             if (frozenset(dealt2[key]), 1) in options:
                 counts[ndx] += 1
                 break
         else:
             self.fail("dealt[key]=%r not in expected re_dealt2 ranges: %r" %
                       (dealt2[key], re_dealt2))
     for ndx, val in enumerate(counts):
         expected = re_dealt[ndx][1] * iterations
         self.assertAlmostEqual(val, expected, delta=delta,
             msg=("Expected count %0.0f but got count %0.0f" +  \
             " for re_dealt item %s") % (expected, val, re_dealt[ndx][0]))
 def test_cmp_options(self):
     """
     Test _cmp_options
     """
     cqh = Card.from_text("Qh")
     ckh = Card.from_text("Kh")
     cah = Card.from_text("Ah")
     cks = Card.from_text("Ks")
     ckc = Card.from_text("Kc")
     kh_qh = set([ckh, cqh])
     ah_kh = set([ckh, cah])
     ks_kh = set([ckh, cks])
     kh_kc = set([ckh, ckc])
     self.assertEqual(-1, _cmp_options(kh_qh, ah_kh))
     self.assertEqual(-1, _cmp_options(kh_qh, ks_kh))
     self.assertEqual(-1, _cmp_options(kh_qh, kh_kc))
     self.assertEqual(1, _cmp_options(ah_kh, kh_qh))
     self.assertEqual(1, _cmp_options(ah_kh, ks_kh))
     self.assertEqual(1, _cmp_options(ah_kh, kh_kc))
     self.assertEqual(1, _cmp_options(ks_kh, kh_qh))
     self.assertEqual(-1, _cmp_options(ks_kh, ah_kh))
     self.assertEqual(1, _cmp_options(ks_kh, kh_kc))
     self.assertEqual(1, _cmp_options(kh_kc, kh_qh))
     self.assertEqual(-1, _cmp_options(kh_kc, ah_kh))
     self.assertEqual(-1, _cmp_options(kh_kc, ks_kh))
Exemple #4
0
 def test_cmp_options(self):
     """
     Test _cmp_options
     """
     cqh = Card.from_text("Qh")
     ckh = Card.from_text("Kh")
     cah = Card.from_text("Ah")
     cks = Card.from_text("Ks")
     ckc = Card.from_text("Kc")
     kh_qh = set([ckh, cqh])
     ah_kh = set([ckh, cah])
     ks_kh = set([ckh, cks])
     kh_kc = set([ckh, ckc])
     self.assertEqual(-1, _cmp_options(kh_qh, ah_kh))
     self.assertEqual(-1, _cmp_options(kh_qh, ks_kh))
     self.assertEqual(-1, _cmp_options(kh_qh, kh_kc))
     self.assertEqual(1, _cmp_options(ah_kh, kh_qh))
     self.assertEqual(1, _cmp_options(ah_kh, ks_kh))
     self.assertEqual(1, _cmp_options(ah_kh, kh_kc))
     self.assertEqual(1, _cmp_options(ks_kh, kh_qh))
     self.assertEqual(-1, _cmp_options(ks_kh, ah_kh))
     self.assertEqual(1, _cmp_options(ks_kh, kh_kc))
     self.assertEqual(1, _cmp_options(kh_kc, kh_qh))
     self.assertEqual(-1, _cmp_options(kh_kc, ah_kh))
     self.assertEqual(-1, _cmp_options(kh_kc, ks_kh))
Exemple #5
0
 def from_afei(cls, afei):
     """
     Create from AnalysisFoldEquityItem
     """
     cards = [Card.from_text(afei.higher_card),
              Card.from_text(afei.lower_card)]
     return cls(cards, afei.is_aggressive, afei.is_passive, afei.is_fold,
         afei.fold_ratio, afei.immediate_result,
         afei.semibluff_ev,
         afei.semibluff_equity)
Exemple #6
0
 def from_afei(cls, afei):
     """
     Create from AnalysisFoldEquityItem
     """
     cards = [Card.from_text(afei.higher_card),
              Card.from_text(afei.lower_card)]
     return cls(cards, afei.is_aggressive, afei.is_passive, afei.is_fold,
         afei.fold_ratio, afei.immediate_result,
         afei.semibluff_ev,
         afei.semibluff_equity)
 def test_create_afei_two_fodlers_reraise(self):
     """ Test _create_afei for a reraise against two players"""
     # raise from 30 to 50 on an original pot of 10
     # profitable bluff
     fea = FoldEquityAccumulator(
         gameid=0,
         order=0,
         street=RIVER,
         board=[],
         bettor=0,
         range_action=None,
         raise_total=50,
         pot_before_bet=50,
         bet_cost=50,
         pot_if_called=120,  # assumes called by the raiser, not the bettor
         potential_folders=[])
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 2/3
     fold_range = HandRange("KK-JJ")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 5/6
     afei = fea._create_afei(combo=Card.many_from_text("AsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 10.0 / 18.0)
     self.assertAlmostEqual(afei.immediate_result,
         10.0 / 18.0 * 50.0 + 8.0 / 18.0 * (-50.0))  # 5.55...
     self.assertAlmostEqual(afei.semibluff_ev, None)
     self.assertAlmostEqual(afei.semibluff_equity, None)
Exemple #8
0
 def generate_options(self, board=None):
     """
     option is a list of (hand, weight)
     """
     # TODO: 3: remove the concept of weighted options from HandRange
     # It might still be worth having a WeightedHandRange, specifically for
     # situations, but then convert to (unweighted) HandRange when the game
     # starts.
     excluded_cards = board or []
     excluded_mnemonics = [card.to_mnemonic() for card in excluded_cards]
     # it's really nice for this to be a list, for self.polarise_weights
     options = []
     for part, weight in self.subranges:
         option_mnemonics = hands_in_subrange(part)  # list of e.g. "AhKh"
         not_excluded = lambda hand: (hand[
             0:2] not in excluded_mnemonics and hand[2:4] not in
                                      excluded_mnemonics)
         option_mnemonics = [o for o in option_mnemonics if not_excluded(o)]
         hands = [
             frozenset(Card.many_from_text(txt)) for txt in option_mnemonics
         ]
         options.extend([(hand, weight) for hand in hands])
     if self.is_strict:
         return options
     else:
         return list(set(options))
 def test_create_afei_one_folder_reraise(self):
     """ Test _create_afei for a reraise against one player"""
     # raise from 30 to 50 on an original pot of 10
     # unprofitable bluff
     fea = FoldEquityAccumulator(
         gameid=0,
         order=0,
         street=PREFLOP,
         board=[],
         bettor=0,
         range_action=None,
         raise_total=50,
         pot_before_bet=50,
         bet_cost=40,
         pot_if_called=110,
         potential_folders=[])
     fold_range = HandRange("QQ")
     nonfold_range = HandRange("AA-KK")
     fea.folds.append((1, fold_range, nonfold_range))
     afei = fea._create_afei(combo=Card.many_from_text("KsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 1.0 / 4.0)
     self.assertAlmostEqual(afei.immediate_result,
         1.0 / 4.0 * 50.0 + (3.0 / 4.0) * (-40.0))  # -17.5
     self.assertAlmostEqual(afei.semibluff_ev, 4.0 / 3.0 * 17.5)
     self.assertAlmostEqual(afei.semibluff_equity, 4.0 / 3.0 * 17.5 / 110.0)
 def test_description(self):
     """
     Test BestHandEval7 to string
     """
     cards = Card.many_from_text("AsTdTs5sAc5cQd")
     hand = BestHandEval7(cards)
     self.assertEqual(str(hand), "two pair, Aces and Tens")
 def test_create_afei_two_folders_bet(self):
     """ Test _create_afei for a bet against two players"""
     # bet 10 on a pot of 10
     # profitable bluff
     fea = FoldEquityAccumulator(
         gameid=0,
         order=0,
         street=RIVER,
         board=[],
         bettor=0,
         range_action=None,
         raise_total=10,
         pot_before_bet=10,
         bet_cost=10,
         pot_if_called=30,
         potential_folders=[])
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))
     afei = fea._create_afei(combo=Card.many_from_text("AsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 4.0 / 9.0)
     self.assertAlmostEqual(afei.immediate_result,
         4.0 / 9.0 * 10.0 + (5.0 / 9.0) * (-10))
     self.assertEqual(afei.semibluff_ev, None)
     self.assertEqual(afei.semibluff_equity, None)
 def test_create_afei_one_folder_raise(self):
     """ Test _create_afei for a raise against one player"""
     # raise from 10 to 30 on an original pot of 10
     # profitable bluff
     fea = FoldEquityAccumulator(
         gameid=0,
         order=0,
         street=PREFLOP,
         board=[],
         bettor=0,
         range_action=None,
         raise_total=30,
         pot_before_bet=20,
         bet_cost=30,
         pot_if_called=70,
         potential_folders=[])
     fold_range = HandRange("KK-JJ")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))
     afei = fea._create_afei(combo=Card.many_from_text("KsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 2.0 / 3.0)
     self.assertAlmostEqual(afei.immediate_result,
         2.0 / 3.0 * 20.0 + (1.0 / 3.0) * (-30.0))  # 3.33...
     self.assertAlmostEqual(afei.semibluff_ev, -10.0)
     self.assertAlmostEqual(afei.semibluff_equity, -10.0 / 70.0)
Exemple #13
0
 def test_hand_vs_range_monte_carlo(self):
     hand = Card.many_from_text("AsAd")
     villain = HandRange("AA,A3o,32s")
     board = []
     equity = py_hand_vs_range_monte_carlo(
         hand, villain, board, 10000000)
     self.assertAlmostEqual(equity, 0.85337, delta=0.002)
Exemple #14
0
 def test_create_afei_two_folders_raise(self):
     """ Test _create_afei for a raise against two players"""
     # raise from 10 to 30 on an original pot of 10
     # unprofitable bluff
     fea = FoldEquityAccumulator(gameid=0,
                                 order=0,
                                 street=RIVER,
                                 board=[],
                                 bettor=0,
                                 range_action=None,
                                 raise_total=30,
                                 pot_before_bet=20,
                                 bet_cost=30,
                                 pot_if_called=70,
                                 potential_folders=[])
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 2/3
     fold_range = HandRange("QQ")
     nonfold_range = HandRange("KK+")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 1/4
     afei = fea._create_afei(combo=Card.many_from_text("AsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 1.0 / 6.0)
     self.assertAlmostEqual(afei.immediate_result, 1.0 / 6.0 * 20.0 +
                            (5.0 / 6.0) * (-30))  # -21.66...
     self.assertAlmostEqual(afei.semibluff_ev, None)
     self.assertAlmostEqual(afei.semibluff_equity, None)
Exemple #15
0
 def test_create_afei_two_fodlers_reraise(self):
     """ Test _create_afei for a reraise against two players"""
     # raise from 30 to 50 on an original pot of 10
     # profitable bluff
     fea = FoldEquityAccumulator(
         gameid=0,
         order=0,
         street=RIVER,
         board=[],
         bettor=0,
         range_action=None,
         raise_total=50,
         pot_before_bet=50,
         bet_cost=50,
         pot_if_called=120,  # assumes called by the raiser, not the bettor
         potential_folders=[])
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 2/3
     fold_range = HandRange("KK-JJ")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 5/6
     afei = fea._create_afei(combo=Card.many_from_text("AsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 10.0 / 18.0)
     self.assertAlmostEqual(afei.immediate_result, 10.0 / 18.0 * 50.0 +
                            8.0 / 18.0 * (-50.0))  # 5.55...
     self.assertAlmostEqual(afei.semibluff_ev, None)
     self.assertAlmostEqual(afei.semibluff_equity, None)
Exemple #16
0
 def generate_options_unweighted(self, board=None):
     """
     just hand, no weight
     error if weights are not all the same
     """
     excluded_cards = board or []
     excluded_mnemonics = [card.to_mnemonic() for card in excluded_cards]
     # it's really nice for this to be a list, for self.polarise_weights
     options = []
     first_weight = None
     for part, weight in self.subranges:
         if first_weight == None:
             first_weight = weight
         elif weight != first_weight:
             raise ValueError("range is not evenly weighted")
         option_mnemonics = hands_in_subrange(part)  # list of e.g. "AhKh"
         not_excluded = lambda hand: (hand[0:2] not in excluded_mnemonics
             and hand[2:4] not in excluded_mnemonics)
         option_mnemonics = [o for o in option_mnemonics if not_excluded(o)]
         hands = [frozenset(Card.many_from_text(txt))
                  for txt in option_mnemonics]
         options.extend(hands)
     if self.is_strict:
         return options
     else:
         return list(set(options))
Exemple #17
0
 def test_create_afei_two_folders_bet(self):
     """ Test _create_afei for a bet against two players"""
     # bet 10 on a pot of 10
     # profitable bluff
     fea = FoldEquityAccumulator(gameid=0,
                                 order=0,
                                 street=RIVER,
                                 board=[],
                                 bettor=0,
                                 range_action=None,
                                 raise_total=10,
                                 pot_before_bet=10,
                                 bet_cost=10,
                                 pot_if_called=30,
                                 potential_folders=[])
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))
     afei = fea._create_afei(combo=Card.many_from_text("AsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 4.0 / 9.0)
     self.assertAlmostEqual(afei.immediate_result,
                            4.0 / 9.0 * 10.0 + (5.0 / 9.0) * (-10))
     self.assertEqual(afei.semibluff_ev, None)
     self.assertEqual(afei.semibluff_equity, None)
 def test_create_afei_two_folders_raise(self):
     """ Test _create_afei for a raise against two players"""
     # raise from 10 to 30 on an original pot of 10
     # unprofitable bluff
     fea = FoldEquityAccumulator(
         gameid=0,
         order=0,
         street=RIVER,
         board=[],
         bettor=0,
         range_action=None,
         raise_total=30,
         pot_before_bet=20,
         bet_cost=30,
         pot_if_called=70,
         potential_folders=[])
     fold_range = HandRange("KK")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 2/3
     fold_range = HandRange("QQ")
     nonfold_range = HandRange("KK+")
     fea.folds.append((1, fold_range, nonfold_range))  # folds 1/4
     afei = fea._create_afei(combo=Card.many_from_text("AsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 1.0 / 6.0)
     self.assertAlmostEqual(afei.immediate_result,
         1.0 / 6.0 * 20.0 + (5.0 / 6.0) * (-30))  # -21.66...
     self.assertAlmostEqual(afei.semibluff_ev, None)
     self.assertAlmostEqual(afei.semibluff_equity, None)
Exemple #19
0
def inject_range_sizes(players, board_raw):
    """
    Inject a range size into each situation player
    """
    for player in players:
        player.range_size = len(HandRange(player.range_raw).generate_options(
            Card.many_from_text(board_raw)))
Exemple #20
0
 def generate_options_unweighted(self, board=None):
     """
     just hand, no weight
     error if weights are not all the same
     """
     excluded_cards = board or []
     excluded_mnemonics = [card.to_mnemonic() for card in excluded_cards]
     # it's really nice for this to be a list, for self.polarise_weights
     options = []
     first_weight = None
     for part, weight in self.subranges:
         if first_weight == None:
             first_weight = weight
         elif weight != first_weight:
             raise ValueError("range is not evenly weighted")
         option_mnemonics = hands_in_subrange(part)  # list of e.g. "AhKh"
         not_excluded = lambda hand: (hand[
             0:2] not in excluded_mnemonics and hand[2:4] not in
                                      excluded_mnemonics)
         option_mnemonics = [o for o in option_mnemonics if not_excluded(o)]
         hands = [
             frozenset(Card.many_from_text(txt)) for txt in option_mnemonics
         ]
         options.extend(hands)
     if self.is_strict:
         return options
     else:
         return list(set(options))
Exemple #21
0
 def test_all_hands_vs_range(self):
     hero = HandRange("AsAd,3h2c")
     villain = HandRange("AA,A3o,32s")
     board = []
     equity_map = py_all_hands_vs_range(hero, villain, board, 10000000)
     self.assertEqual(len(equity_map), 2)
     hand1 = frozenset(Card.many_from_text("AsAd"))
     hand2 = frozenset(Card.many_from_text("3h2c"))
     self.assertAlmostEqual(equity_map[hand1], 0.85337, delta=0.002)
     self.assertAlmostEqual(equity_map[hand2], 0.22865, delta=0.002)
     
     # Hero has an impossible hand in his range.
     hero = HandRange("JsJc,QsJs")
     villain = HandRange("JJ")
     board = Card.many_from_text("KhJd8c")
     equity_map = py_all_hands_vs_range(hero, villain, board, 10000000)
     hand = frozenset(Card.many_from_text("QsJs"))
     self.assertAlmostEqual(equity_map[hand], 0.03687, delta=0.0002)
     self.assertEqual(len(equity_map), 1)
def safe_board_form(field_name):
    """
    Pull a board (list of Card) from request form field <field_name>.

    If there is a problem, return an empty list.
    """
    value = request.form.get(field_name, '', type=str)
    try:
        return Card.many_from_text(value)
    except ValueError:
        return []
def safe_board_form(field_name):
    """
    Pull a board (list of Card) from request form field <field_name>.

    If there is a problem, return an empty list.
    """
    value = request.form.get(field_name, '', type=str)
    try:
        return Card.many_from_text(value)
    except ValueError:
        return []
def safe_board(arg_name):
    """
    Pull a board (list of Card) from request arg <arg_name>.

    If there is a problem, return an empty list.
    """
    value = request.args.get(arg_name, '', type=str)
    try:
        return Card.many_from_text(value)
    except ValueError:
        return []
def safe_board(arg_name):
    """
    Pull a board (list of Card) from request arg <arg_name>.

    If there is a problem, return an empty list.
    """
    value = request.args.get(arg_name, '', type=str)
    try:
        return Card.many_from_text(value)
    except ValueError:
        return []
 def _test_calculate_equity(self, ranges_txt, board_txt, expect,
                            expect_iters):
     from rvr.poker.handrange import HandRange
     from rvr.poker.cards import Card
     ranges = {"Player %d" % i: HandRange(ranges_txt[i])
               for i in range(len(ranges_txt))}
     board = Card.many_from_text(board_txt)
     expected_results = {"Player %d" % i: expect[i]
                         for i in range(len(expect))}
     results, iterations = showdown_equity(ranges, board, 20000)
     self.assertEqual(iterations, expect_iters)
     self.assert_equity_almost_equal(results, expected_results)
 def process_board(self, item):
     """
     Process a GameHistoryBoard
     """
     self.contrib = {u:0 for u in self.remaining_userids}
     self.left_to_act = self.remaining_userids[:]
     assert self.fea is None
     self.street = item.street
     board_before = self.board
     self.board = Card.many_from_text(item.cards)
     board_after = self.board
     self.board_payment(item, board_before, board_after)
Exemple #28
0
 def __init__(self, gameid, order, street, board, bettor, range_action,
              raise_total, pot_before_bet, bet_cost, pot_if_called,
              potential_folders):
     logging.debug("gameid %d, FEA %d, initialising", gameid, order)
     self.gameid = gameid
     self.order = order
     self.street = street
     self.board = Card.many_from_text(board)
     self.bettor = bettor
     self.range_action = range_action
     self.raise_total = raise_total
     self.pot_before_bet = pot_before_bet
     self.bet_cost = bet_cost
     self.pot_if_called = pot_if_called
     self.potential_folders = potential_folders  # userids
     self.folds = []  # list of (userid, fold_ratio)
Exemple #29
0
 def _add_card_combos(self):
     """
     Populate table of RangeItem
     """
     deck1 = [
         Card(rank, suit) for rank in RANKS_HIGH_TO_LOW
         for suit in SUITS_HIGH_TO_LOW
     ]
     deck2 = deck1[:]
     err = None
     for card1 in deck1:
         for card2 in deck2:
             if card1 > card2:
                 err = err or self._add_card_combo(higher_card=card1,
                                                   lower_card=card2)
     return err
    def all_combos_ev(self, userid, local=False):
        """
        Return a mapping of combo to EV at this point in the game tree, for each
        combo in the user's range at this point.

        Recall that a combo is a frozenset of two cards.
        """
        user_range = self.ranges_by_userid[userid]
        combos = user_range.generate_options(Card.many_from_text(self.board))
        results = {}
        for combo in combos:
            try:
                results[combo] = self.combo_ev(combo, userid, local)
            except InvalidComboForTree as ex:
                pass  # TODO: 0.0: happening way too often!
        return results
Exemple #31
0
 def __init__(self, gameid, order, street, board, bettor, range_action,
              raise_total, pot_before_bet, bet_cost,
              pot_if_called, potential_folders):
     logging.debug("gameid %d, FEA %d, initialising",
                   gameid, order)
     self.gameid = gameid
     self.order = order
     self.street = street
     self.board = Card.many_from_text(board)
     self.bettor = bettor
     self.range_action = range_action
     self.raise_total = raise_total
     self.pot_before_bet = pot_before_bet
     self.bet_cost = bet_cost
     self.pot_if_called = pot_if_called
     self.potential_folders = potential_folders  # userids
     self.folds = []  # list of (userid, fold_ratio)
 def _test_estimate_showdown_equity(self, ranges_txt, board_txt, expect,
                                    delta, iterations):
     from rvr.poker.handrange import HandRange
     from rvr.poker.cards import Card
     ranges = {"Player %d" % i: HandRange(ranges_txt[i])
               for i in range(len(ranges_txt))}
     board = Card.many_from_text(board_txt)
     expected_results = {"Player %d" % i: expect[i]
                         for i in range(len(expect))}
     delta_by_player = {"Player %d" % i: delta[i]
                        for i in range(len(expect))}
     options_by_player = {p: r.generate_options(board)
                          for p, r in ranges.iteritems()}
     wins_by_player = _estimate_showdown_equity(options_by_player,
                                                board, iterations)
     print "\nwins_by_player: %r\n" % wins_by_player
     self.assert_wins_almost_equal(wins_by_player, expected_results,
                                   delta_by_player)
Exemple #33
0
 def test_range_contains_hand(self):
     """
     Test range_contains_hand
     """
     from rvr.poker import cards
     range_ = HandRange("AA(5),KK")
     hands_in = [[
         Card(cards.ACE, cards.SPADES),
         Card(cards.ACE, cards.HEARTS)
     ], [Card(cards.KING, cards.CLUBS),
         Card(cards.KING, cards.DIAMONDS)]]
     hands_out = [[
         Card(cards.ACE, cards.SPADES),
         Card(cards.KING, cards.HEARTS)
     ], [Card(cards.DEUCE, cards.CLUBS),
         Card(cards.DEUCE, cards.DIAMONDS)]]
     for hand_in in hands_in:
         self.assertTrue(range_contains_hand(range_, hand_in))
     for hand_out in hands_out:
         self.assertFalse(range_contains_hand(range_, hand_out))
 def generate_options(self, board=None):
     """
     option is a list of hand
     """
     excluded_cards = board or []
     excluded_mnemonics = [card.to_mnemonic() for card in excluded_cards]
     options = []
     for part in self.subranges:
         option_mnemonics = hands_in_subrange(part)  # list of e.g. "AhKh"
         not_excluded = lambda hand: (hand[0:2] not in excluded_mnemonics
             and hand[2:4] not in excluded_mnemonics)
         option_mnemonics = [o for o in option_mnemonics if not_excluded(o)]
         hands = [frozenset(Card.many_from_text(txt))
                  for txt in option_mnemonics]
         options.extend(hands)
     if self.is_strict:
         return options
     else:
         return list(set(options))
 def _test_estimate_showdown_equity2(self, ranges_txt, board_txt, expect,
                                     sds, iterations):
     from rvr.poker.handrange import HandRange
     from rvr.poker.cards import Card
     ranges = {"Player %d" % i: HandRange(ranges_txt[i])
               for i in range(len(ranges_txt))}
     board = Card.many_from_text(board_txt)
     sdev = [stddev_one(prob) for prob in expect]  # e.g. p=0.05, sdev=0.2179
     expected_results = {"Player %d" % i: expect[i] * iterations
                         for i in range(len(expect))}
     delta_by_player = {"Player %d" % i: sds * sdev[i] * (iterations ** 0.5)
                        for i in range(len(expect))}
     options_by_player = {p: r.generate_options(board)
                          for p, r in ranges.iteritems()}
     wins_by_player = _estimate_showdown_equity(options_by_player,
                                                board, iterations)
     print "\nwins_by_player: %r\n" % wins_by_player
     self.assert_wins_almost_equal(wins_by_player, expected_results,
                                   delta_by_player)
Exemple #36
0
 def generate_options(self, board=None):
     """
     option is a list of (hand, weight)
     """
     excluded_cards = board or []
     excluded_mnemonics = [card.to_mnemonic() for card in excluded_cards]
     # it's really nice for this to be a list, for self.polarise_weights
     options = []
     for part, weight in self.subranges:
         option_mnemonics = hands_in_subrange(part)  # list of e.g. "AhKh"
         not_excluded = lambda hand: (hand[0:2] not in excluded_mnemonics
             and hand[2:4] not in excluded_mnemonics)
         option_mnemonics = [o for o in option_mnemonics if not_excluded(o)]
         hands = [frozenset(Card.many_from_text(txt))
                  for txt in option_mnemonics]
         options.extend([(hand, weight) for hand in hands])
     if self.is_strict:
         return options
     else:
         return list(set(options))
Exemple #37
0
    def test_hand_vs_range_exact(self):
        hand = Card.many_from_text("AcAh")
        villain = HandRange("AA")
        board = Card.many_from_text("KhJd8c5d2s")
        equity = py_hand_vs_range_exact(hand, villain, board)
        self.assertEqual(equity, 0.5)

        hand = Card.many_from_text("AcAh")
        villain = HandRange("AsAd")
        board = Card.many_from_text("KhJd8c5d2s")
        equity = py_hand_vs_range_exact(hand, villain, board)
        self.assertEqual(equity, 0.5)

        hand = Card.many_from_text("AsAd")
        villain = HandRange("AA,A3o,32s")
        board = Card.many_from_text("KhJd8c5d2s")
        equity = py_hand_vs_range_exact(hand, villain, board)
        self.assertAlmostEqual(equity, 0.95, places=7)
Exemple #38
0
 def test_create_afei_one_folder_reraise(self):
     """ Test _create_afei for a reraise against one player"""
     # raise from 30 to 50 on an original pot of 10
     # unprofitable bluff
     fea = FoldEquityAccumulator(gameid=0,
                                 order=0,
                                 street=PREFLOP,
                                 board=[],
                                 bettor=0,
                                 range_action=None,
                                 raise_total=50,
                                 pot_before_bet=50,
                                 bet_cost=40,
                                 pot_if_called=110,
                                 potential_folders=[])
     fold_range = HandRange("QQ")
     nonfold_range = HandRange("AA-KK")
     fea.folds.append((1, fold_range, nonfold_range))
     afei = fea._create_afei(combo=Card.many_from_text("KsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 1.0 / 4.0)
     self.assertAlmostEqual(afei.immediate_result, 1.0 / 4.0 * 50.0 +
                            (3.0 / 4.0) * (-40.0))  # -17.5
     self.assertAlmostEqual(afei.semibluff_ev, 4.0 / 3.0 * 17.5)
     self.assertAlmostEqual(afei.semibluff_equity, 4.0 / 3.0 * 17.5 / 110.0)
Exemple #39
0
 def generate_options(self, board=None):
     """
     option is a list of (hand, weight)
     """
     # TODO: 3: remove the concept of weighted options from HandRange
     # It might still be worth having a WeightedHandRange, specifically for
     # situations, but then convert to (unweighted) HandRange when the game
     # starts.
     excluded_cards = board or []
     excluded_mnemonics = [card.to_mnemonic() for card in excluded_cards]
     # it's really nice for this to be a list, for self.polarise_weights
     options = []
     for part, weight in self.subranges:
         option_mnemonics = hands_in_subrange(part)  # list of e.g. "AhKh"
         not_excluded = lambda hand: (hand[0:2] not in excluded_mnemonics
             and hand[2:4] not in excluded_mnemonics)
         option_mnemonics = [o for o in option_mnemonics if not_excluded(o)]
         hands = [frozenset(Card.many_from_text(txt))
                  for txt in option_mnemonics]
         options.extend([(hand, weight) for hand in hands])
     if self.is_strict:
         return options
     else:
         return list(set(options))
Exemple #40
0
 def test_create_afei_one_folder_raise(self):
     """ Test _create_afei for a raise against one player"""
     # raise from 10 to 30 on an original pot of 10
     # profitable bluff
     fea = FoldEquityAccumulator(gameid=0,
                                 order=0,
                                 street=PREFLOP,
                                 board=[],
                                 bettor=0,
                                 range_action=None,
                                 raise_total=30,
                                 pot_before_bet=20,
                                 bet_cost=30,
                                 pot_if_called=70,
                                 potential_folders=[])
     fold_range = HandRange("KK-JJ")
     nonfold_range = HandRange("AA")
     fea.folds.append((1, fold_range, nonfold_range))
     afei = fea._create_afei(combo=Card.many_from_text("KsQh"), is_agg=True)
     self.assertAlmostEqual(afei.fold_ratio, 2.0 / 3.0)
     self.assertAlmostEqual(afei.immediate_result, 2.0 / 3.0 * 20.0 +
                            (1.0 / 3.0) * (-30.0))  # 3.33...
     self.assertAlmostEqual(afei.semibluff_ev, -10.0)
     self.assertAlmostEqual(afei.semibluff_equity, -10.0 / 70.0)
Exemple #41
0
 def get_cards_dealt(self):
     """
     Get cards dealt, as list of two Card
     """
     return Card.many_from_text(self.cards_dealt_raw)
Exemple #42
0
 def get_board(self):
     """
     Get board, as list of Card
     """
     return Card.many_from_text(self.board_raw)
Exemple #43
0
    def test_range_action_to_action(self):
        """
        Test range_action_to_action
        """
        # pylint:disable=R0914,R0915

        # will test:
        # - act fold,
        # - act check,
        # - act call,
        # - act raise,
        # - act case where hand is in no range (although invalid)
        # won't test:
        # - act case where hand is in all ranges (because the result is
        #   undefined)
        
        options_with_check = ActionOptions(0, False, 2, 196) 
        options_with_call = ActionOptions(10, True, 20, 196)
        options_without_raise = ActionOptions(196)

        range_aa = HandRange("AA")
        range_72o = HandRange("72o")
        range_22_72o = HandRange("22,72o")
        range_empty = HandRange("nothing")
        range_22_weighted = HandRange("22(3)")

        hand_72o = Card.many_from_text("7h2c")
        hand_22 = Card.many_from_text("2h2c")
        hand_aa = Card.many_from_text("AhAc")
        hand_72s = Card.many_from_text("7h2h")

        action_with_raise = ActionDetails(range_72o, range_22_weighted,
                                          range_aa, 40)

        rng, act = range_action_to_action(action_with_raise,
                                          hand_72o, options_with_check)
        self.assert_(act.is_fold)
        self.assertEqual("72o", rng.description)
        rng, act = range_action_to_action(action_with_raise,
                                          hand_22, options_with_check)
        self.assert_(act.is_passive)
        self.assertEqual(act.call_cost, 0)
        rng, act = range_action_to_action(action_with_raise,
                                          hand_aa, options_with_check)
        self.assert_(act.is_aggressive)
        self.assertEqual(act.raise_total, 40)
        try:
            act = range_action_to_action(action_with_raise,
                                         hand_72s, options_with_check)
            self.assertTrue(False)
        except ValueError:
            pass

        rng, act = range_action_to_action(action_with_raise,
                                          hand_72o, options_with_call)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_with_raise,
                                          hand_22, options_with_call)
        self.assert_(act.is_passive)
        self.assertEqual(act.call_cost, 10)
        rng, act = range_action_to_action(action_with_raise,
                                          hand_aa, options_with_call)
        self.assert_(act.is_aggressive)
        self.assertEqual(act.raise_total, 40)
        try:
            act = range_action_to_action(action_with_raise,
                                         hand_72s, options_with_call)
            self.assertTrue(False)
        except ValueError:
            pass

        action_without_raise = ActionDetails(range_22_72o, range_aa,
                                             range_empty, 0)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_72o, options_with_check)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_22, options_with_check)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_aa, options_with_check)
        self.assert_(act.is_passive)
        self.assertEqual(act.call_cost, 0)
        try:
            act = range_action_to_action(action_without_raise,
                                         hand_72s, options_with_check)
            self.assertTrue(False)
        except ValueError:
            pass

        rng, act = range_action_to_action(action_without_raise,
                                          hand_72o, options_with_call)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_22, options_with_call)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_aa, options_with_call)
        self.assert_(act.is_passive)
        self.assertEqual(act.call_cost, 10)
        try:
            act = range_action_to_action(action_without_raise,
                                         hand_72s, options_with_call)
            self.assertTrue(False)
        except ValueError:
            pass

        rng, act = range_action_to_action(action_without_raise,
                                          hand_72o, options_without_raise)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_22, options_without_raise)
        self.assert_(act.is_fold)
        rng, act = range_action_to_action(action_without_raise,
                                          hand_aa, options_without_raise)
        self.assert_(act.is_passive)
        self.assertEqual(act.call_cost, 196)
        try:
            rng, act = range_action_to_action(action_without_raise,
                hand_72s, options_without_raise)
            self.assertTrue(False)
        except ValueError:
            pass
Exemple #44
0
 def get_cards_dealt(self):
     """
     Get cards dealt, as list of two Card
     """
     return Card.many_from_text(self.cards_dealt_raw)
    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]
 def from_game(cls, game):
     """
     Create a partial game tree from a single game. Note that this still
     creates branches (multi-child nodes) due to non-terminal folds and
     showdowns.
     """
     # TODO: REVISIT: Blackbox testing? Unit testing? Something!
     # Any error in any of this logic will create some obscure bug in some
     # game tree.
     # TODO: 4: A general purpose replayer. How many times do we have to
     # write this code before we refactor it into something reusable... and
     # demonstrably correct!
     actual_ranges = {}
     for rgp, player in zip(game.rgps, game.situation.players):
         new_range = remove_board_from_range(player.range,
             game.situation.board)
         actual_ranges[rgp.userid] = new_range.description
     board_raw = game.situation.board_raw
     session = object_session(game)
     # We need to look for:
     # - GameHistoryActionResult, to find actions that happened
     # - GameHistoryRangeAction, to find folds
     # - GameHistoryShowdown, to find showdown calls
     # - GameHistoryBoard, to know its the river, because then three-handed
     #   folds can be terminal
     history = []
     for table in [tables.GameHistoryActionResult,
                   tables.GameHistoryRangeAction,
                   tables.GameHistoryShowdown,
                   tables.GameHistoryBoard]:
         history.extend(session.query(table)  \
             .filter(table.gameid == game.gameid).all())
     history.sort(key=lambda row: row.order)
     current_round = game.situation.current_round
     stacks = {rgp.userid: player.stack
               for rgp, player in zip(game.rgps, game.situation.players)}
     contrib = {rgp.userid: player.contributed
                for rgp, player in zip(game.rgps, game.situation.players)}
     total_contrib = dict(contrib)
     to_act = {rgp.userid
               for rgp, player in zip(game.rgps, game.situation.players)
               if player.left_to_act}
     pot = game.situation.pot_pre +  \
         sum(p.contributed for p in game.situation.players)
     raise_total = max(p.contributed for p in game.situation.players)
     remain = {rgp.userid for rgp in game.rgps}
     root = cls(game.situation.current_round, board_raw, None,
                None, None, actual_ranges, total_contrib)
     node = root  # where we're adding actions
     prev_range_action = None
     for item in history:
         # reset game state for new round
         if isinstance(item, tables.GameHistoryBoard):
             current_round = item.street
             board_raw = item.cards
             to_act = set(remain)
             contrib = {rgp.userid: 0 for rgp in game.rgps}
             raise_total = 0
             for userid, old_range in actual_ranges.iteritems():
                 new_range = remove_board_from_range(HandRange(old_range),
                     Card.many_from_text(item.cards))
                 actual_ranges[userid] = new_range.description
         if isinstance(item, tables.GameHistoryShowdown):
             # add call
             call_cost = raise_total - contrib[prev_range_action.userid]
             action = ActionResult.call(call_cost)
             ranges = dict(actual_ranges)
             ranges[prev_range_action.userid] =  \
                 prev_range_action.passive_range
             showdown_contrib = dict(total_contrib)
             showdown_contrib[prev_range_action.userid] += call_cost
             showdown_pot = pot + call_cost
             child = cls(current_round, board_raw,
                         prev_range_action.userid, action,
                         node, ranges,
                         total_contrib=showdown_contrib, winners=remain,
                         final_pot=showdown_pot)
             node.children.append(child)
         # Only if the fold is terminal is it part of the tree.
         # Folds are terminal when:
         # - two-handed; or,
         # - three-handed when:
         #   - all other players have acted on the river; or,
         #   - are all in before the river; or,
         # - they fold 100%
         if isinstance(item, tables.GameHistoryRangeAction):
             prev_range_action = item
             if item.fold_ratio is None:
                 has_fold = item.fold_range != NOTHING
             else:
                 has_fold = item.fold_ratio != 0.0
             is_final_round = current_round == RIVER or  \
                 not all(stacks.values())
             heads_up = len(remain) == 2
             final_action = len(to_act) == 1 and is_final_round
             # There's no (implicit) fold when multi-way and play continues.
             if has_fold and (final_action or heads_up):
                 # Play would not continue with a fold here, so there will be
                 # no actual fold action.
                 # TODO: 3: use game_continues?
                 # Add a non-played fold. We keep the folded player's range
                 # in here, because it is relevant to consider the EV of each
                 # folded combo.
                 winners = set(remain)
                 winners.remove(item.userid)
                 # winners may be one (HU) or multiple (multiway)
                 ranges = actual_ranges
                 ranges[item.userid] = item.fold_range
                 child = cls(current_round, board_raw, item.userid,
                             ActionResult.fold(), node, ranges,
                             total_contrib=total_contrib, winners=winners,
                             final_pot=pot)
                 node.children.append(child)
         if isinstance(item, tables.GameHistoryActionResult):
             # maintain game state
             if item.is_passive:
                 actual_ranges[item.userid] = prev_range_action.passive_range
                 stacks[item.userid] -= item.call_cost
                 contrib[item.userid] += item.call_cost
                 total_contrib[item.userid] += item.call_cost
                 pot += item.call_cost
                 action = ActionResult.call(item.call_cost)
             if item.is_aggressive:
                 actual_ranges[item.userid] =  \
                     prev_range_action.aggressive_range
                 chips = item.raise_total - contrib[item.userid]
                 stacks[item.userid] -= chips
                 contrib[item.userid] += chips
                 total_contrib[item.userid] += chips
                 pot += chips
                 raise_total = item.raise_total
                 to_act = set(remain)
                 action = ActionResult.raise_to(raise_total, item.is_raise)
             if item.is_fold:
                 remain.remove(item.userid)
                 action = ActionResult.fold()
                 # and yes, we still traverse in (multi-way)
             to_act.remove(item.userid)
             # add fold, check, call, raise or bet, and traverse in
             child = cls(current_round, board_raw, item.userid, action, node,
                         actual_ranges, total_contrib)
             node.children.append(child)
             # traverse down
             node = child
     return root