Esempio n. 1
0
def estimate_proba(hand, board, n_player, n_simul=1000):

    hand = Card.str_to_int(hand)
    board = Card.str_to_int(board)

    evaluator = Evaluator()
    to_draw = 5 - len(board)
    deck = set_deck(hand, board)

    winnings = []

    for _ in range(n_simul):
        deck2 = deepcopy(deck)
        shuffle(deck2.cards)
        if to_draw == 1:
            board2 = board + [deck2.draw(to_draw)]
        else:
            board2 = board + deck2.draw(to_draw)
        if n_player > 2:
            other_hands = list(
                zip(deck2.draw(n_player - 1), deck2.draw(n_player - 1)))
            score_others = min([
                evaluator.evaluate(list(hand2), board2)
                for hand2 in other_hands
            ])
        elif n_player == 2:
            other_hand = deck2.draw(2)
            score_others = evaluator.evaluate(other_hand, board2)

        score_player = evaluator.evaluate(hand, board2)

        winnings += [score_player < score_others]

    return mean(winnings)
Esempio n. 2
0
 def __update_community(self):
     # card increment based on which round we are in
     if self.turn_nb == 0:
         pass
     elif self.turn_nb == 1:
         self.community_cards += Card.int_to_str(self.deck.draw(3))
     elif self.turn_nb == 2:
         self.community_cards += [Card.int_to_str(self.deck.draw(1))]
     elif self.turn_nb == 3:
         self.community_cards += [Card.int_to_str(self.deck.draw(1))]
     elif self.turn_nb > 3:
         return
Esempio n. 3
0
    def straight_and_highcards(self, straights, highcards):
        """
        Unique five card sets. Straights and highcards.

        Reuses bit sequences from flush calculations.
        """
        rank = LookupTable.MAX_FLUSH + 1

        for s in straights:
            prime_product = Card.prime_product_from_rankbits(s)
            self.unsuited_lookup[prime_product] = rank
            rank += 1

        rank = LookupTable.MAX_PAIR + 1
        for h in highcards:
            prime_product = Card.prime_product_from_rankbits(h)
            self.unsuited_lookup[prime_product] = rank
            rank += 1
Esempio n. 4
0
    def __rank_players(self):

        # we first compute the strength of each player's hand
        # warning: the evaluator gives value 0 to the best possible hand
        evaluator = Evaluator()
        hand_strength = np.array([
            evaluator.evaluate(Card.str_to_int(player.hand),
                               Card.str_to_int(self.community_cards))
            if player.round_status != 'out' else np.inf
            for player in self.players
        ])

        # we then rank them, making sure equal players have equal ranks
        player_ranking = np.zeros(self.n_players)
        for rank, hand_value in enumerate(np.unique(np.sort(hand_strength))):
            player_ranking[hand_strength == hand_value] = rank

        return player_ranking
Esempio n. 5
0
    def _five(self, cards):
        """
        Performs an evalution given cards in integer form, mapping them to
        a rank in the range [1, 7462], with lower ranks being more powerful.

        Variant of Cactus Kev's 5 card evaluator, though I saved a lot of memory
        space using a hash table and condensing some of the calculations.
        """
        # if flush
        if cards[0] & cards[1] & cards[2] & cards[3] & cards[4] & 0xF000:
            handOR = (cards[0] | cards[1] | cards[2] | cards[3]
                      | cards[4]) >> 16
            prime = Card.prime_product_from_rankbits(handOR)
            return self.table.flush_lookup[prime]

        # otherwise
        else:
            prime = Card.prime_product_from_hand(cards)
            return self.table.unsuited_lookup[prime]
Esempio n. 6
0
    def GetFullDeck():
        if Deck._FULL_DECK:
            return list(Deck._FULL_DECK)

        # create the standard 52 card deck
        for rank in Card.STR_RANKS:
            for suit, val in Card.CHAR_SUIT_TO_INT_SUIT.items():
                Deck._FULL_DECK.append(Card.str_to_int(rank + suit))

        return list(Deck._FULL_DECK)
Esempio n. 7
0
    def __next_round(self):

        # reinitializing/updating round data

        self.players_info = [
            player.get_player_data() for player in self.players
        ]
        self.round_logger = []

        self.dealer += 1
        self.turn_nb = 0
        self.dealer %= self.n_players

        # distributing cards to players still in
        self.deck = Deck()
        self.community_cards = []

        print('Starting round %d with players %s' %
              (self.round_nb,
               str([
                   i for i, player in enumerate(self.players)
                   if player.game_status == 'in'
               ])))

        for player in self.players:
            if player.game_status == 'in':
                player.new_round(hand=Card.int_to_str(self.deck.draw(2)),
                                 blind=self.blind)

        # running the 4 betting turns
        for _ in range(4):
            self.__next_turn()

        self.display()

        # attributing the gains
        pots = self.__create_pots()
        ranking = self.__rank_players()
        self.__distribute_pots(pots, ranking)

        for player in self.players:
            if player.stack <= 0 and player.game_status != 'out':
                player.game_status = 'out'
                player.round_status = 'out'

        # logging
        self.game_logger += [self.__get_round_info()]
        self.game_logger[-1]['round_history'] = self.round_logger

        self.game_logger[-1]["winner"] = np.where(ranking == 0)[0].tolist()
        self.display()
Esempio n. 8
0
    def flushes(self):
        """
        Straight flushes and flushes. 

        Lookup is done on 13 bit integer (2^13 > 7462):
        xxxbbbbb bbbbbbbb => integer hand index
        """

        # straight flushes in rank order
        straight_flushes = [
            7936,  # int('0b1111100000000', 2), # royal flush
            3968,  # int('0b111110000000', 2),
            1984,  # int('0b11111000000', 2),
            992,  # int('0b1111100000', 2),
            496,  # int('0b111110000', 2),
            248,  # int('0b11111000', 2),
            124,  # int('0b1111100', 2),
            62,  # int('0b111110', 2),
            31,  # int('0b11111', 2),
            4111  # int('0b1000000001111', 2) # 5 high
        ]

        # now we'll dynamically generate all the other
        # flushes (including straight flushes)
        flushes = []
        gen = self.get_lexographically_next_bit_sequence(int('0b11111', 2))

        # 1277 = number of high cards
        # 1277 + len(str_flushes) is number of hands with all cards unique rank
        for i in range(1277 + len(straight_flushes) -
                       1):  # we also iterate over SFs
            # pull the next flush pattern from our generator
            f = next(gen)

            # if this flush matches perfectly any
            # straight flush, do not add it
            notSF = True
            for sf in straight_flushes:
                # if f XOR sf == 0, then bit pattern
                # is same, and we should not add
                if not f ^ sf:
                    notSF = False

            if notSF:
                flushes.append(f)

        # we started from the lowest straight pattern, now we want to start ranking from
        # the most powerful hands, so we reverse
        flushes.reverse()

        # now add to the lookup map:
        # start with straight flushes and the rank of 1
        # since theyit is the best hand in poker
        # rank 1 = Royal Flush!
        rank = 1
        for sf in straight_flushes:
            prime_product = Card.prime_product_from_rankbits(sf)
            self.flush_lookup[prime_product] = rank
            rank += 1

        # we start the counting for flushes on max full house, which
        # is the worst rank that a full house can have (2,2,2,3,3)
        rank = LookupTable.MAX_FULL_HOUSE + 1
        for f in flushes:
            prime_product = Card.prime_product_from_rankbits(f)
            self.flush_lookup[prime_product] = rank
            rank += 1

        # we can reuse these bit sequences for straights
        # and high cards since they are inherently related
        # and differ only by context
        self.straight_and_highcards(straight_flushes, flushes)
Esempio n. 9
0
 def __str__(self):
     return Card.print_pretty_cards(self.cards)