def evaluate_rank(self, hand): """ Return the rank amongst all possible 5-card hands of any kind using the best 5-card hand from the given 6-card hand. """ if len(hand) != 7: raise self.HandLengthException("Only 7-card hands are supported by the Seven evaluator") # bh stands for binary hand, map to that representation card_to_binary = HandEvaluator.Seven.card_to_binary_lookup bh = map(card_to_binary, hand) # Use a lookup table to determine if it's a flush as with 6 cards flush_prime = reduce(mul, map(lambda card: (card >> 12) & 0xF, bh)) flush_suit = False if flush_prime in LookupTables.Seven.prime_products_to_flush: flush_suit = LookupTables.Seven.prime_products_to_flush[flush_prime] # Now use ranks to determine hand via lookup odd_xor = reduce(__xor__, bh) >> 16 even_xor = (reduce(__or__, bh) >> 16) ^ odd_xor if flush_suit: # There will be 0-2 cards not in the right suit even_popcount = PopCount.popcount(even_xor) if even_xor == 0: # TODO: There might be a faster way? bits = reduce(__or__, map( lambda card: (card >> 16), filter( lambda card: (card >> 12) & 0xF == flush_suit, bh))) return LookupTables.Seven.flush_rank_bits_to_rank[bits] else: if even_popcount == 2: return LookupTables.Seven.flush_rank_bits_to_rank[odd_xor | even_xor] else: bits = reduce(__or__, map( lambda card: (card >> 16), filter( lambda card: (card >> 12) & 0xF == flush_suit, bh))) return LookupTables.Seven.flush_rank_bits_to_rank[bits] # Odd-even XOR again, see Six.evaluate_rank for details # 7 is odd, so you have to have an odd number of bits in odd_xor # 7-0 => (1,1,1,1,1,1,1) - High card # 5-1 => (1,1,1,1,1,2) - Pair # 5-0 => (1,1,1,1,3) - Trips # 3-2 => (1,1,1,2,2) - Two pair # 3-1 => (1,1,1,4) or (1,1,3,2) - Quads or full house # 3-0 => (1,3,3) - Full house # 1-3 => (1,2,2,2) - Two pair # 1-2 => (1,2,4) or (3,2,2) - Quads or full house # 1-1 => (3,4) - Quads if even_xor == 0: # x-0 odd_popcount = PopCount.popcount(odd_xor) if odd_popcount == 7: # 7-0 return LookupTables.Seven.odd_xors_to_rank[odd_xor] else: # 5-0, 3-0 prime_product = reduce(mul, map(lambda card: card & 0xFF, bh)) return LookupTables.Seven.prime_products_to_rank[prime_product] else: odd_popcount = PopCount.popcount(odd_xor) if odd_popcount == 5: # 5-1 return LookupTables.Seven.even_xors_to_odd_xors_to_rank[even_xor][odd_xor] elif odd_popcount == 3: even_popcount = PopCount.popcount(even_xor) if even_popcount == 2: # 3-2 return LookupTables.Seven.even_xors_to_odd_xors_to_rank[even_xor][odd_xor] else: # 3-1 prime_product = reduce(mul, map(lambda card: card & 0xFF, bh)) return LookupTables.Seven.prime_products_to_rank[prime_product] else: even_popcount = PopCount.popcount(even_xor) if even_popcount == 3: # 1-3 return LookupTables.Seven.even_xors_to_odd_xors_to_rank[even_xor][odd_xor] elif even_popcount == 2: # 1-2 prime_product = reduce(mul, map(lambda card: card & 0xFF, bh)) return LookupTables.Seven.prime_products_to_rank[prime_product] else: # 1-1 return LookupTables.Seven.even_xors_to_odd_xors_to_rank[even_xor][odd_xor]
def evaluate_rank(self, hand): """ Return the rank amongst all possible 5-card hands of any kind using the best 5-card hand from the given 6-card hand. """ if len(hand) != 6: raise self.HandLengthException("Only 6-card hands are supported by the Six evaluator") # bh stands for binary hand, map to that representation card_to_binary = HandEvaluator.Six.card_to_binary_lookup bh = map(card_to_binary, hand) # We can determine if it's a flush using a lookup table. # Basically use prime number trick but map to bool instead of rank # Once you have a flush, there is no other higher hand you can make # except straight flush, so just need to determine the highest flush flush_prime = reduce(mul, map(lambda card: (card >> 12) & 0xF, bh)) flush_suit = False if flush_prime in LookupTables.Six.prime_products_to_flush: flush_suit = LookupTables.Six.prime_products_to_flush[flush_prime] # Now use ranks to determine hand via lookup odd_xor = reduce(__xor__, bh) >> 16 even_xor = (reduce(__or__, bh) >> 16) ^ odd_xor # If you have a flush, use odd_xor to find the rank # That value will have either 4 or 5 bits if flush_suit: if even_xor == 0: # There might be 0 or 1 cards in the wrong suit, so filter # TODO: There might be a faster way? bits = reduce(__or__, map( lambda card: (card >> 16), filter( lambda card: (card >> 12) & 0xF == flush_suit, bh))) return LookupTables.Six.flush_rank_bits_to_rank[bits] else: # you have a pair, one card in the flush suit, # so just use the ranks you have by or'ing the two return LookupTables.Six.flush_rank_bits_to_rank[odd_xor | even_xor] # Otherwise, get ready for a wild ride: # Can determine this by using 2 XORs to reduce the size of the # lookup. You have an even number of cards, so any odd_xor with # an odd number of bits set is not possible. # Possibilities are odd-even: # 6-0 => High card or straight (1,1,1,1,1,1) # Look up by odd_xor # 4-1 => Pair (1,1,1,1,2) # Look up by even_xor (which pair) then odd_xor (which set of kickers) # 4-0 => Trips (1,1,1,3) # Don't know which one is the triple, use prime product of ranks # 2-2 => Two pair (1,1,2,2) # Look up by odd_xor then even_xor (or vice-versa) # 2-1 => Four of a kind (1,1,4) or full house (1,3,2) # Look up by prime product # 2-0 => Full house using 2 trips (3,3) # Look up by odd_xor # 0-3 => Three pairs (2,2,2) # Look up by even_xor # 0-2 => Four of a kind with pair (2,4) # Look up by prime product # Any time you can't disambiguate 2/4 or 1/3, use primes. # We also assume you can count bits or determine a power of two. # (see PopCount class.) if even_xor == 0: # x-0 odd_popcount = PopCount.popcount(odd_xor) if odd_popcount == 4: # 4-0 prime_product = reduce(mul, map(lambda card: card & 0xFF, bh)) return LookupTables.Six.prime_products_to_rank[prime_product] else: # 6-0, 2-0 return LookupTables.Six.odd_xors_to_rank[odd_xor] elif odd_xor == 0: # 0-x even_popcount = PopCount.popcount(even_xor) if even_popcount == 2: # 0-2 prime_product = reduce(mul, map(lambda card: card & 0xFF, bh)) return LookupTables.Six.prime_products_to_rank[prime_product] else: # 0-3 return LookupTables.Six.even_xors_to_rank[even_xor] else: # odd_popcount is 4 or 2 odd_popcount = PopCount.popcount(odd_xor) if odd_popcount == 4: # 4-1 return LookupTables.Six.even_xors_to_odd_xors_to_rank[even_xor][odd_xor] else: # 2-x even_popcount = PopCount.popcount(even_xor) if even_popcount == 2: # 2-2 return LookupTables.Six.even_xors_to_odd_xors_to_rank[even_xor][odd_xor] else: # 2-1 prime_product = reduce(mul, map(lambda card: card & 0xFF, bh)) return LookupTables.Six.prime_products_to_rank[prime_product]