Skip to content

Instantly share code, notes, and snippets.

@baweaver
Created January 14, 2021 04:45
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baweaver/0d566a6afe1214b37718b166bbfa9ece to your computer and use it in GitHub Desktop.
Save baweaver/0d566a6afe1214b37718b166bbfa9ece to your computer and use it in GitHub Desktop.
Card = Struct.new(:suit, :rank) do
include Comparable
def precedence() = [SUITS_SCORES[self.suit], RANKS_SCORES[self.rank]]
def rank_precedence() = RANKS_SCORES[self.rank]
def suit_precedence() = SUITS_SCORES[self.rank]
def <=>(other) = self.precedence <=> other.precedence
def to_s() = "#{self.suit}#{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
SUITS = %w(S H D C).freeze
SUITS_SCORES = SUITS.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 = SUITS.flat_map { |suit|
RANKS.map { |rank| Card[suit, rank] }
}.freeze
def add_rank(rank, n) = RANKS[RANKS_SCORES[rank] + n]
def royal_flush?(hand)
hand in [
Card[suit, '10'],
Card[^suit, 'J'],
Card[^suit, 'Q'],
Card[^suit, 'K'],
Card[^suit, 'A']
]
end
def straight_flush?(hand) = straight?(hand) && flush?(hand)
def straight?(hand)
hand in [
Card[*, rank],
Card[*, "#{add_rank(rank, 1)}"],
Card[*, "#{add_rank(rank, 2)}"],
Card[*, "#{add_rank(rank, 3)}"],
Card[*, "#{add_rank(rank, 4)}"],
]
end
def flush?(hand)
hand in [
Card[suit, *],
Card[^suit, *],
Card[^suit, *],
Card[^suit, *],
Card[^suit, *]
]
end
def four_of_a_kind?(hand)
hand in [
*,
Card[*, rank],
Card[*, ^rank],
Card[*, ^rank],
Card[*, ^rank],
*
]
end
def full_house?(hand)
return true if hand in [
Card[*, rank_one],
Card[*, ^rank_one],
Card[*, rank_two],
Card[*, ^rank_two],
Card[*, ^rank_two]
]
hand in [
Card[*, rank_one],
Card[*, ^rank_one],
Card[*, ^rank_one],
Card[*, rank_two],
Card[*, ^rank_two]
]
end
def three_of_a_kind?(hand)
hand in [
*,
Card[*, rank],
Card[*, ^rank],
Card[*, ^rank],
*
]
end
def two_pair?(hand)
return true if hand in [
Card[*, rank_one],
Card[*, ^rank_one],
*,
Card[*, rank_two],
Card[*, ^rank_two]
]
hand in [
*,
Card[*, rank_one],
Card[*, ^rank_one],
Card[*, rank_two],
Card[*, ^rank_two],
*
]
end
def one_pair?(hand)
hand in [
*,
Card[*, rank],
Card[*, ^rank],
*
]
end
def hand_score(unsorted_hand)
hand = Hand[unsorted_hand].sort_by_rank.cards
return SCORES[:royal_flush] if royal_flush?(hand)
return SCORES[:straight_flush] if straight_flush?(hand)
return SCORES[:four_of_a_kind] if four_of_a_kind?(hand)
return SCORES[:full_house] if full_house?(hand)
return SCORES[:flush] if flush?(hand)
return SCORES[:straight] if straight?(hand)
return SCORES[:three_of_a_kind] if three_of_a_kind?(hand)
return SCORES[:two_pair] if two_pair?(hand)
return SCORES[:one_pair] if one_pair?(hand)
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], *SUITS.map { Card[_1, 'A'] }],
full_house:
SUITS.first(3).map { Card[_1, 'A'] } +
SUITS.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) +
SUITS.first(3).map { Card[_1, 'A'] },
two_pair:
CARDS.first(1) +
SUITS.first(2).flat_map { [Card[_1, 'A'], Card[_1, 'K']] },
one_pair:
[CARDS[10], CARDS[15], CARDS[20], *SUITS.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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment