Skip to content

Instantly share code, notes, and snippets.

@baweaver
Created January 15, 2021 06:45
Show Gist options
  • Save baweaver/f641a4d70e563c6dadf61b93c6faabf8 to your computer and use it in GitHub Desktop.
Save baweaver/f641a4d70e563c6dadf61b93c6faabf8 to your computer and use it in GitHub Desktop.
This time with a bit more gusto in the refactoring and care to remembering classes exist and things related to them should go in them
class Card
SUITS = %w(S H D C).freeze
SUITS_SCORES = SUITS.each_with_index.to_h
RANKS = %w(2 3 4 5 6 7 8 9 10 J Q K A).freeze
RANKS_SCORES = RANKS.each_with_index.to_h
include Comparable
attr_reader :suit, :rank
def initialize(suit, rank)
@suit = suit
@rank = rank
end
def self.[](suit, rank) = new(suit, rank)
def to_a() = [@suit, @rank]
alias_method :deconstruct, :to_a
def precedence() = RANKS_SCORES[@rank]
def <=>(other) = precedence <=> other.precedence
def to_s() = "#{@suit}#{@rank}"
def add_rank(n) = Card[@suit, RANKS[RANKS_SCORES[@rank] + n]]
def self.add_rank(rank, n) = RANKS[RANKS_SCORES[rank] + n]
end
class Hand
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
SCORE_MAP = SCORES.invert
attr_reader :cards
def initialize(*cards)
@cards = cards.sort
end
def self.[](*cards) = new(*cards)
def to_s() = @cards.map(&:to_s).join(', ')
def to_a() = @cards
alias_method :deconstruct, :to_a
def royal_flush?
@cards in [
Card[suit, '10'],
Card[^suit, 'J'],
Card[^suit, 'Q'],
Card[^suit, 'K'],
Card[^suit, 'A']
]
end
def straight_flush?
straight? && flush?
end
def straight?
@cards in [
Card[*, rank],
Card[*, "#{Card.add_rank(rank, 1)}"],
Card[*, "#{Card.add_rank(rank, 2)}"],
Card[*, "#{Card.add_rank(rank, 3)}"],
Card[*, "#{Card.add_rank(rank, 4)}"],
]
end
def flush?
@cards in [
Card[suit, *],
Card[^suit, *],
Card[^suit, *],
Card[^suit, *],
Card[^suit, *]
]
end
def four_of_a_kind?
@cards in [
*,
Card[*, rank],
Card[*, ^rank],
Card[*, ^rank],
Card[*, ^rank],
*
]
end
def full_house?
return true if @cards in [
Card[*, rank_one],
Card[*, ^rank_one],
Card[*, rank_two],
Card[*, ^rank_two],
Card[*, ^rank_two]
]
@cards in [
Card[*, rank_one],
Card[*, ^rank_one],
Card[*, ^rank_one],
Card[*, rank_two],
Card[*, ^rank_two]
]
end
def three_of_a_kind?
@cards in [
*,
Card[*, rank],
Card[*, ^rank],
Card[*, ^rank],
*
]
end
def two_pair?
return true if @cards in [
Card[*, rank_one],
Card[*, ^rank_one],
*,
Card[*, rank_two],
Card[*, ^rank_two]
]
@cards in [
*,
Card[*, rank_one],
Card[*, ^rank_one],
Card[*, rank_two],
Card[*, ^rank_two],
*
]
end
def one_pair?
@cards in [
*,
Card[*, rank],
Card[*, ^rank],
*
]
end
def score
return SCORES[:royal_flush] if royal_flush?
return SCORES[:straight_flush] if straight_flush?
return SCORES[:four_of_a_kind] if four_of_a_kind?
return SCORES[:full_house] if full_house?
return SCORES[:flush] if flush?
return SCORES[:straight] if straight?
return SCORES[:three_of_a_kind] if three_of_a_kind?
return SCORES[:two_pair] if two_pair?
return SCORES[:one_pair] if one_pair?
SCORES[:high_card]
end
end
# --- Testing ------
CARDS = Card::SUITS.flat_map { |suit|
Card::RANKS.map { |rank| Card[suit, rank] }
}.freeze
EXAMPLES = {
royal_flush:
Card::RANKS.last(5).map { Card['S', _1] },
straight_flush:
Card::RANKS.first(5).map { Card['S', _1] },
four_of_a_kind: [
CARDS[0],
*Card::SUITS.map { Card[_1, 'A'] }
],
full_house:
Card::SUITS.first(3).map { Card[_1, 'A'] } +
Card::SUITS.first(2).map { Card[_1, 'K'] },
flush:
(0..Card::RANKS.size)
.step(2)
.first(5)
.map { Card['S', Card::RANKS[_1]] },
straight: [
Card['H', Card::RANKS.first],
*Card::RANKS[1..4].map { Card['S', _1] }
],
three_of_a_kind:
CARDS.first(2) +
Card::SUITS.first(3).map { Card[_1, 'A'] },
two_pair:
CARDS.first(1) +
Card::SUITS.first(2).flat_map { [Card[_1, 'A'], Card[_1, 'K']] },
one_pair: [
CARDS[10],
CARDS[15],
CARDS[20],
*Card::SUITS.first(2).map { Card[_1, 'A'] }
],
high_card: [
CARDS[10],
CARDS[15],
CARDS[20],
CARDS[5],
Card['S', 'A']
]
}.freeze
EXAMPLES.each do |hand_type, cards|
hand = Hand[*cards]
score = hand.score
correct_text = hand_type == Hand::SCORE_MAP[score] ? 'correct' : 'incorrect'
puts <<~OUT
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