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