Example #1
0
    def _two(self, cards):
        """
        Pre-flop: 169 non-equivalent starting hands out of 1326 hands
        Ranks: Pairs, suited, unsuited. Plus if connectors. Ace always better
        Find index from self.startingHandRanks
        """
        card1, card2 = cards[0], cards[1]
        rank1, rank2 = Card.get_rank_int(card1), Card.get_rank_int(card2)
        firstRank, secondRank = max(rank1, rank2), min(rank1, rank2)

        cardString = str(Card.STR_RANKS[firstRank]) + str(
            Card.STR_RANKS[secondRank])
        if (firstRank != secondRank):
            #check if suited or not
            if (Card.get_suit_int(card1) == Card.get_suit_int(card2)):
                cardString += "s"
            else:
                cardString += "o"

        return self.startingHandRanks.index(cardString)
    def GenerateMultipliers(self,
                            hands,
                            hand_values,
                            num_players=4,
                            data_dir=None):
        """
        Generates an array full of multipliers used to calculate the results of gameplay
        
        hands =         Array of hands (rounds*all_hands_per_round, 3) with format:
                        [[dealer_Cards], [player1_cards], [player2_cards], [player3_cards], ...]
            
        hand_values =   Array of the associated hand ranks returned from evaluate_hands
        
        num_players =   Number of players per round (excluding the player/dealer)
        
        
        Returns array of multipliers (num_rounds,num_players,4) specifying the following for each hand:

            multipliers[round,hand,0]:    Play multiplier -- determines whether hand should be played
                                                             and whether it wins, loses, or pushes
                                                             (1 = win, -1 = loss, 0 = no play or push)
                                                            
            multipliers[round,hand,1]:    Ante multiplier -- 0 = ante pushes, 1 = ante wins, -1 = ante loses
            
            multipliers[round,hand,2]:    Pair Plus multiplier -- -1 if Pair Plus loses, otherwise the bonus multiple
            
            multipliers[round,hand,3]:    6-Card Multiplier -- -1 if 6-Card bonus loses, otherwise the bonus multiple
        """

        if data_dir is None:
            data_dir = self.tmp_dir

        all_hands = num_players + 1
        num_rounds = len(hands) / all_hands

        multipliers = np.memmap(path.join(data_dir, 'multipliers'),
                                dtype='int32',
                                mode='w+',
                                shape=(num_rounds, num_players, 4))

        play_multipliers = multipliers[:, :, 0]
        ante_multipliers = multipliers[:, :, 1]
        pair_plus_multipliers = multipliers[:, :, 2]
        six_card_multipliers = multipliers[:, :, 3]

        #group hand values into sets of 1 dealer hand + related player hands
        #Split the dealer values into one group and player values into another
        splittable_values = hand_values.reshape(-1, all_hands)
        dealer_values = splittable_values.T[0].reshape(-1, 1)
        player_values = splittable_values.T[1:].T

        #Lower valued hands beat higher valued hands
        #Win_lose = -1 for player hands that lose, 1 for wins, and 0 for ties
        win_lose = np.memmap(path.join(data_dir, 'win_lose'),
                             dtype='int32',
                             mode='w+',
                             shape=(num_rounds, num_players))
        win_lose[:] = np.copysign(1, dealer_values - player_values)

        #Hand and card ranks needed to determine when play bet should be made for each hand
        #The suits are arbitrary as long as these are unsuited hands
        ACE_HIGH = self.evaluate_hand(Card.hand_to_binary(['Ac', '2d', '4d']))
        KING_HIGH = self.evaluate_hand(Card.hand_to_binary(['Kc', '2d', '3d']))
        QUEEN_HIGH = self.evaluate_hand(Card.hand_to_binary(['Qc', '2d',
                                                             '3d']))
        ACE = Card.get_rank_int(Card.new('Ac'))
        KING = Card.get_rank_int(Card.new('Kc'))
        QUEEN = Card.get_rank_int(Card.new('Qc'))

        #In each round, one dealer card is dealt face up
        #Here we build an array view containing the first dealer card for each round as our face up card
        #hands.reshape(1,-1)[0] is equivalent to hands.flatten() without copying into a new array

        dealer_face_up = hands.reshape(1,
                                       -1)[0][0::all_hands * 3].reshape(-1, 1)

        #Face up dealer cards are converted to int ranks
        #face_up_rank = (dealer_face_up>>8)&0xFF

        #Hand plays based on House Way:
        #    Always play A high or better
        #    Play K high if dealer face up card is K or worse
        #    Play Q high if dealer face up card is Q or worse
        #    Never play hands less than Q high

        #Play the hand = 1, don't play = 0

        play_multipliers[:] = np.where(
            player_values <= ACE_HIGH, 1,
            np.where(
                player_values <= KING_HIGH,
                np.where((dealer_face_up >> 8) & 0xF < ACE, 1, 0),
                np.where(player_values <= QUEEN_HIGH,
                         np.where((dealer_face_up >> 8) & 0xF < KING, 1, 0),
                         0)))

        #Pair Plus bonus loses (-1) when a player's hand is less than a pair
        #Otherwise it pays a multiple of the bet depending on the hand strength for pairs and better
        #The bet plays independently of the hand winning or losing,
        #but if the player does not make a play bet, the Pair Plus bonus is forfeit (-1)

        #Pay chart
        #    Worse than one pair:  -1
        #    Pair: 1x
        #    Flush: 3x
        #    Straight: 6x
        #    Straight flush: 30x
        #    Royal flush: 50x

        pair_plus_multipliers[:] = np.where(
            play_multipliers == 0, -1,
            np.where(
                player_values > self.tc_lookup.MAX_PAIR, -1,
                np.where(
                    player_values > self.tc_lookup.MAX_FLUSH, 1,
                    np.where(
                        player_values > self.tc_lookup.MAX_STRAIGHT, 3,
                        np.where(
                            player_values > self.tc_lookup.MAX_TRIPS, 6,
                            np.where(
                                player_values >
                                self.tc_lookup.MAX_STRAIGHT_FLUSH, 30,
                                np.where(player_values > 1, 40, 50)))))))

        #Calculate Ante wins/losses
        #If a play bet is not made, ante loses (-1) automatically
        #If a play bet is made:
        #    Hand wins:  ante wins (+1)
        #    Hand loses: ante loses (-1) if dealer has Q high or better, otherwise pushes (0)

        ante_multipliers[:] = np.where(
            play_multipliers == 0, -1,
            np.where(win_lose > -1, win_lose,
                     np.where(dealer_values <= QUEEN_HIGH, -1, 0)))

        #Calculate Play wins/losses
        #We need to do this after calculating the Pair Plus bonus
        #so we don't confuse ties with forfeit hands
        #If the dealer hand is worse than Q high, the Play bet pushes (0)

        play_multipliers[:] = np.where(dealer_values <= QUEEN_HIGH,
                                       play_multipliers * win_lose, 0)

        del win_lose

        #Now we need to compute the 6-card bonuses
        #These are based on the best 5-card hand that can be made from the player's 3 card and the dealer's 3

        #The first out of each group of hands is the dealer hand for that group
        #The rest are the player hands
        #we want groups like: [dcard1, dcard2, dcard3, pcard1, pcard2, pcard3]
        #to pass to deuces' hand evaluator
        #This requires some slicing arobatics to avoid building new arrays

        #Hands are arranged by round such as dealer_cards, player1_cards, player2_cards, etc.
        #Then stepped slices are taken and truncated to form slices for each combo of dealer and player cards

        #eg. If hands = [D01, D02, D03, Pa1, Pa2, Pa3, Pb1, Pb2, Pb3,
        #                D11, D12, D13, Pc1, Pc2, Pc3, Pd1, Pd2, Pd3]
        #
        #Then eval_hands[0] = [[D01, D02, D03, Pa1, Pa2, Pa3],
        #                      [D11, D12, D13, Pc1, Pc2, Pc3]]
        #
        #     eval_hands[1] = [[D01, D02, D03, Pb1, Pb2, Pb3],
        #                      [D11, D12, D13, Pd1, Pd2, Pd3]]

        eval_hands = [
            hands.reshape(-1, all_hands, 3)[:, ::i + 1][:, :2].reshape(-1, 6)
            for i in xrange(num_players)
        ]

        #calculate 6card bonuses
        evaluator = Evaluator()

        #This is the main bottleneck in the simulator
        #The only real way to resolve it is to rebuild the deuces library's
        #hand evaluator to process arrays of hands rather than one at a time,
        #which is currently beyond the scope of this project

        for i in xrange(num_players):
            six_card_multipliers[:, i] = np.apply_along_axis(
                evaluator._six, 1, eval_hands[i])

        #Map hand values to bonus payouts

        SC_MAX_STRAIGHT_FLUSH = evaluator.table.MAX_STRAIGHT_FLUSH
        SC_MAX_QUADS = evaluator.table.MAX_FOUR_OF_A_KIND
        SC_MAX_FULL_HOUSE = evaluator.table.MAX_FULL_HOUSE
        SC_MAX_FLUSH = evaluator.table.MAX_FLUSH
        SC_MAX_STRAIGHT = evaluator.table.MAX_STRAIGHT
        SC_MAX_TRIPS = evaluator.table.MAX_THREE_OF_A_KIND

        #6-Card Bonus Payout table
        #Worse than trips  -1
        #Trips              5
        #Straight           10
        #Flush              15
        #Full House         25
        #Quads              50
        #Straight Flush     200
        #Royal Flush        1000

        six_card_multipliers[:] = np.where(
            six_card_multipliers > SC_MAX_TRIPS, -1,
            np.where(
                six_card_multipliers > SC_MAX_STRAIGHT, 5,
                np.where(
                    six_card_multipliers > SC_MAX_FLUSH, 10,
                    np.where(
                        six_card_multipliers > SC_MAX_FULL_HOUSE, 15,
                        np.where(
                            six_card_multipliers > SC_MAX_QUADS, 25,
                            np.where(
                                six_card_multipliers > SC_MAX_STRAIGHT_FLUSH,
                                50,
                                np.where(six_card_multipliers > 1, 200,
                                         1000)))))))

        return multipliers