def calculate_deuces(board_arg, hand_arg): """ Calculates the Deuces score as well as its class (high card, pair, etc) :param board: list of string with len > 2 :param hand: list of string with len = 2 :return: Tuple of (deuces score, deuces class) """ log.debug('Calculating Deuces of {} and {}'.format(board_arg, hand_arg)) if input_type_checker(board_arg, hand_arg): # convert string to Card object board = list(map(lambda c: Card.new(c), board_arg)) hand = list(map(lambda c: Card.new(c), hand_arg)) evaluator = Evaluator() deuces_score = evaluator.evaluate(board, hand) deuces_class = evaluator.class_to_string( evaluator.get_rank_class(deuces_score)) return (deuces_score, deuces_class) else: log.error('Please see documentation for list of valid input.') sys.exit(1)
def test_cards_numerical_value(self): """ Each card has a unique numerical value :return: """ card1 = Card.new('4h') card2 = Card.new('2s') self.assertEqual(card1, 270853) self.assertEqual(card2, 69634)
def compute_win_percentage(board_arg, hand_arg): """ Computes your winning percentage using a Monte Carlo Simulation :param board_arg: list of string with len > 2 :param hand_arg: list of string with len = 2 :return: """ log.debug('Computing win percentage of {} and {}'.format( board_arg, hand_arg)) board = [] hand = [] if input_type_checker(board_arg, hand_arg): # convert string to Card object board = list(map(lambda c: Card.new(c), board_arg)) hand = list(map(lambda c: Card.new(c), hand_arg)) simulation_count = 3000 win_count = 0 runs = 0 evaluator = Evaluator() for sim in range(simulation_count): board_fill = 5 - len(board) simulate_board = board + draw_from_deck(board_fill, board + hand) simulate_hand = draw_from_deck(2, simulate_board + hand) my_score = evaluator.evaluate(simulate_board, hand) enemy_score = evaluator.evaluate(simulate_board, simulate_hand) if my_score < enemy_score: win_count += 1 runs += 1 log.debug('Your score: {} {} vs {} {}'.format( evaluator.class_to_string(evaluator.get_rank_class(my_score)), my_score, evaluator.class_to_string(evaluator.get_rank_class(enemy_score)), enemy_score)) win_chance = win_count / float(runs) return win_chance
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]
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
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.iteritems(): Deck._FULL_DECK.append(Card.new(rank + suit)) return list(Deck._FULL_DECK)
def test_evaluator(self): """ Checks if the deuces score produced is correct :return: """ board = [ Card.new('4h'), Card.new('5h'), Card.new('8h'), Card.new('7h'), Card.new('9d') ] hand = [Card.new('2s'), Card.new('3s')] evaluator = Evaluator() deuces_score = evaluator.evaluate(board, hand) self.assertEqual(deuces_score, 7414)
def __str__(self): return Card.print_pretty_cards(self.cards)
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)