Dwi Wahyudi
Senior Software Engineer (Ruby, Golang, Java)
After getting 5 cards from deck, we can then calculate the values based on holdem poker rule.
Overview
https://en.wikipedia.org/wiki/Texas_hold_%27em
My approach for this is quite simple, create a class, for calculating those 5 cards. Check the cards for each hand value with if else and the checking is prioritized by highest values first. So if a hand is already Royal Flush, we don’t need to check other hand values.
module HoldemPoker
class HandValuesCalculator
ROYAL_FLUSH_RANKS = [10, 11, 12, 13, 1].to_set
def initialize(cards)
@cards = cards
end
def perform
raise 'Must supply 5 cards.' if @cards.length != 5
if royal_flush?
'Royal Flush'
elsif straight_flush?
'Straight Flush'
elsif four_of_a_kind?
'Four of a Kind'
elsif full_house?
'Full House'
elsif flush?
'Flush'
elsif straight?
'Straight'
elsif three_of_a_kind?
'Three of a Kind'
elsif two_pairs?
'Two Pairs'
elsif pair?
'Pair'
else
'Highest Card'
end
end
private
# ... Private methods for checking the values.
end
end
Calculating Hands
Now let’s write the private methods for checking the hands for each hand value.
Royal Flush
Royal Flush example is like 10♣ J♣ Q♣ K♣ A♣. It is the highest hand value in holdem poker. In order for a hand to have this value, cards must be in the same suit, and they must have 10, J, Q, K and A.
Before creating royal_flush?
method, let’s create a method to check if cards have same suit.
def same_suit?
suits = @cards.map(&:suit).uniq
suits.length == 1
end
Then we create new method values
which gathers all of values from the cards.
def values
@cards.map(&:value)
end
Then we can use those 2 methods to check if the hand is royal flush.
def royal_flush?
return false if !same_suit?
ranks_set = values.to_set
ROYAL_FLUSH_RANKS == ranks_set
end
NB. Set data will ignore order.
Straight Flush
Straight Flush is basically a hand value where cards are in the same suit, and is sequential/continuous but not Royal Flush.
There are 2 kinds of straight: the normal one, and the ace one. Straight with ace one is Royal Flush, this Straight Flush is only normal straight (without A / Ace).
We will create a method to check whether a hand is normal straight.
But, do note that a straight hand value must not contain duplicated rank/value. The highest value is 4 values higher than the lowest value.
def contain_duplicate_value?
values.uniq.length < 5
end
def normal_values_straight?
return false if contain_duplicate_value?
sorted_values = values.sort
(values.sum % 5 == 0) && (sorted_values[4] - sorted_values[0] == 4)
end
After creating above methods, we can now create straight_flush?
method.
def straight_flush?
return false if !same_suit?
normal_values_straight?
end
Four of a Kind
Four of a Kind is where we have 4 cards with the same rank.
Before we create the method, we will need to write another method in order to help us to group cards by values. This is in order to identify card-pairing.
def grouped_by_values
grouped_by_values = @cards.group_by(&:value)
grouped_by_values.map { |value, cards| cards.length }.sort
end
In newest Ruby version (2.7), we can use Enumerable#tally
, https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-tally
But since I’m still using older version of Ruby, that method will do.
We can then create four_of_a_kind?
method.
def four_of_a_kind?
full_house_set = [1, 4].sort
grouped_by_values == full_house_set
end
Full House
Full House is where we have 3 cards with the same rank, and another 2 cards with another same rank as well.
- 3 Kings and 2 7s.
- 3 10s and 2 Aces.
- 3 2s and 2 8s.
- etc.
Since we already have grouped_by_values
method above, checking Full House is easy.
def full_house?
full_house_set = [2, 3].sort
grouped_by_values == full_house_set
end
Flush
Flush is when we have all 5 cards to have same suit.
We already have same_suit?
method above.
def flush?
same_suit?
end
Straight
Straight is when all 5 cards are sequential/continuous, but doesn’t have the same suit. We already have normal_values_straight?
method which checks straight without Ace.
Now we will create a method to check straight with Ace. Like straight without Ace, straight with Ace, all 5 cards must have unique rank. Unlike normal straight which has 4 values difference between highest and lowest values, straight with Ace has 12, because King has 13 value and Ace has 1 value. 13 - 1 = 12. And all values summed together must be 47.
def upper_ace_straight
return false if contain_duplicate_value?
sorted_values = values.sort
values.sum == 47 && (sorted_values[4] - sorted_values[0] == 12)
end
Now we can create straight?
method.
def straight?
normal_values_straight? || upper_ace_straight
end
Three of a Kind
Three of a Kind is where we have 3 cards with the same rank.
We already have grouped_by_values
method above. So now we can easily create three_of_a_kind?
method.
def three_of_a_kind?
three_of_a_kind_set = [1, 1, 3].sort
grouped_by_values == three_of_a_kind_set
end
Two Pairs
Then two_pairs?
method.
def two_pairs?
two_pairs_set = [2, 2, 1].sort
grouped_by_values == two_pairs_set
end
One Pair
And finally pair?
method, which check if hand is One Pair.
def pair?
pair_set = [2, 1, 1, 1].sort
grouped_by_values == pair_set
end