Implementation
PokerHandEvaluator.jl's approach follows Cactus Kev. Here is a brief summary:
There are combinations(52,5)
, or 2,598,960, unique 5-card hands. However, many of these hands have the exact same rank
(e.g., (A♡,A♢,K♣,K♠,3♢) and (A♡,A♢,K♣,K♠,3♡)). There are only 7462 unique hand ranks:
- A K Q J 10 royal straight flush:
rank = 1
- K Q J 10 9 straight flush:
rank = 2
- ...
- 7 5 4 3 2:
rank = 7462
PokerHandEvaluator.jl's core method, evaluate5
, returns this rank so that any two hands can be compared to determine the winner. There's one more wrinkle to flatten. Exposing an interface that is order-agnostic (so that users don't need to sort the cards before evaluation) is important for performance. To make the evaluate5
order-agnostic, the card rank of each input is mapped to prime numbers:
const primes = (41,2,3,5,7,11,13,17,19,23,29,31,37)
prime(card::Card) = primes[rank(card)]
The product of prime numbers are (1) unique and (2) order-agnostic (due to the multiplication commutative property). This mapped relationship can be implemented in various ways, for example via lookup tables, binary search etc.. PokerHandEvaluator.jl leverages Combinatorics.jl to generate a constant dictionary for the look-up over the combinations of hands.
Finally, PokerHandEvaluator.jl checks to see the card's suit
to disambiguate flush vs off-suited hands:
function evaluate5(t::NTuple{N,Card}) where {N}
if suit(t[1]) == suit(t[2]) == suit(t[3]) == suit(t[4]) == suit(t[5])
hash_table_suited[prod(prime.(t))]
else
hash_table_offsuit[prod(prime.(t))]
end
end