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