Skip to content

Instantly share code, notes, and snippets.

@baweaver
Last active February 3, 2021 12:14
Show Gist options
  • Save baweaver/e89846ca73a7874c9ac8c9d1d21d40fc to your computer and use it in GitHub Desktop.
Save baweaver/e89846ca73a7874c9ac8c9d1d21d40fc to your computer and use it in GitHub Desktop.
Computing poker hand scores using Ruby
Card = Struct.new(:suite, :rank) do
include Comparable
def precedence() = [SUITES_SCORES[self.suite], RANKS_SCORES[self.rank]]
def rank_precedence() = RANKS_SCORES[self.rank]
def suite_precedence() = SUITES_SCORES[self.rank]
def <=>(other) = self.precedence <=> other.precedence
def to_s() = "#{self.suite}#{self.rank}"
end
Hand = Struct.new(:cards) do
def sort() = Hand[self.cards.sort]
def sort_by_rank() = Hand[self.cards.sort_by(&:rank_precedence)]
def to_s() = self.cards.map(&:to_s).join(', ')
end
SUITES = %w(S H D C).freeze
SUITES_SCORES = SUITES.each_with_index.to_h
RANKS = [*2..10, *%w(J Q K A)].map(&:to_s).freeze
RANKS_SCORES = RANKS.each_with_index.to_h
SCORES = %i(
royal_flush
straight_flush
four_of_a_kind
full_house
flush
straight
three_of_a_kind
two_pair
one_pair
high_card
).reverse_each.with_index(1).to_h.freeze
CARDS = SUITES.flat_map { |s| RANKS.map { |r| Card[s, r] } }.freeze
def hand_score(unsorted_hand)
hand = Hand[unsorted_hand].sort_by_rank.cards
is_straight = -> hand {
hand
.map { RANKS_SCORES[_1.rank] }
.sort
.each_cons(2)
.all? { |a, b| b - a == 1 }
}
return SCORES[:royal_flush] if hand in [
Card[s, '10'], Card[^s, 'J'], Card[^s, 'Q'], Card[^s, 'K'], Card[^s, 'A']
]
return SCORES[:straight_flush] if is_straight[hand] && hand in [
Card[s, *], Card[^s, *], Card[^s, *], Card[^s, *], Card[^s, *]
]
return SCORES[:four_of_a_kind] if hand in [
*, Card['S', *], Card['H', *], Card['D', *], Card['C', *], *
]
return SCORES[:full_house] if hand in [
Card[*, r1], Card[*, ^r1], Card[*, ^r1], Card[*, r2], Card[*, ^r2]
]
return SCORES[:full_house] if hand in [
Card[*, r1], Card[*, ^r1], Card[*, r2], Card[*, ^r2], Card[*, ^r2]
]
return SCORES[:flush] if hand in [
Card[s, *], Card[^s, *], Card[^s, *], Card[^s, *], Card[^s, *]
]
return SCORES[:straight] if is_straight[hand]
return SCORES[:three_of_a_kind] if hand in [
*, Card[*, r], Card[*, ^r], Card[*, ^r], *
]
return SCORES[:two_pair] if hand in [
*, Card[*, r1], Card[*, ^r1], Card[*, r2], Card[*, ^r2], *
]
return SCORES[:two_pair] if hand in [
Card[*, r1], Card[*, ^r1], *, Card[*, r2], Card[*, ^r2]
]
return SCORES[:one_pair] if hand in [
*, Card[*, r], Card[*, ^r], *
]
SCORES[:high_card]
end
# --- Testing ------
EXAMPLES = {
royal_flush:
RANKS.last(5).map { Card['S', _1] },
straight_flush:
RANKS.first(5).map { Card['S', _1] },
four_of_a_kind:
[CARDS[0], *SUITES.map { Card[_1, 'A'] }],
full_house:
SUITES.first(3).map { Card[_1, 'A'] } +
SUITES.first(2).map { Card[_1, 'K'] },
flush:
(0..RANKS.size).step(2).first(5).map { Card['S', RANKS[_1]] },
straight:
[Card['H', RANKS.first], *RANKS[1..4].map { Card['S', _1] }],
three_of_a_kind:
CARDS.first(2) +
SUITES.first(3).map { Card[_1, 'A'] },
two_pair:
CARDS.first(1) +
SUITES.first(2).flat_map { [Card[_1, 'A'], Card[_1, 'K']] },
one_pair:
[CARDS[10], CARDS[15], CARDS[20], *SUITES.first(2).map { Card[_1, 'A'] }],
high_card:
[CARDS[10], CARDS[15], CARDS[20], CARDS[5], Card['S', 'A']]
}.freeze
SCORE_MAP = SCORES.invert
EXAMPLES.each do |hand_type, hand|
score = hand_score(hand)
correct_text = hand_type == SCORE_MAP[score] ? 'correct' : 'incorrect'
puts <<~OUT
Hand: #{Hand[hand]} (#{hand_type})
Score: #{score} (#{correct_text})
OUT
puts
end
@gerrywastaken
Copy link

But this will only work for 5 cards. If you have 7 cards (eg. texas holdem -> 5 community + 2 in the hole) I think this becomes a lot more complicated, especially as ruby doesn't appear to allow you to have a wildcard in the middle and at the ends in one pattern.

For example the following seems tricky to match:

A K K Q J J 10

I suspect at this point using something like:

@cards.group_by { _1.rank_score }

and then looking for groups of the correct number becomes easier to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment