euler/ruby/euler054.rb

246 lines
5.0 KiB
Ruby

VALUES = [:two, :three, :four, :five, :six, :seven, :eight, :nine, :ten, :jack, :queen, :king, :ace]
SUITS = [:heart, :diamond, :spade, :club]
STRING_TO_SUIT_MAP = { 'H' => :heart,
'D' => :diamond,
'S' => :spade,
'C' => :club }
STRING_TO_VALUE_MAP = {'A' => :ace,
'K' => :king,
'Q' => :queen,
'J' => :jack,
'10' => :ten,
'T' => :ten,
'9' => :nine,
'8' => :eight,
'7' => :seven,
'6' => :six,
'5' => :five,
'4' => :four,
'3' => :three,
'2' => :two}
class Card
include Comparable
attr_reader :suit, :value
def initialize(suit, value)
@suit = STRING_TO_SUIT_MAP[suit]
@value = STRING_TO_VALUE_MAP[value]
end
def succ_value
if value == :ace
:two
else
VALUES[VALUES.index(value) + 1]
end
end
def succ? other
succ_value == other.value
end
def <=> other
VALUES.index(value) <=> VALUES.index(other.value)
end
def === other
suit == other.suit and value == other.value
end
def ace?
value == :ace
end
end
class Hand
include Comparable
attr_reader :cards
def initialize(*cards)
if cards.first.is_a? String
card_strings = cards.first.split(' ')
@cards = card_strings.map { |str| str.split('') }.map { |value, suit| Card.new(suit, value) }
else
@cards = cards
end
end
def flush?
suits.uniq.one?
end
def straight?
if ace?
all_successors? cards.sort or all_successors? aces_low
else
all_successors? cards.sort
end
end
def straight_flush?
straight? and flush?
end
def royal_flush?
flush? and
values.include?(:ten) and
values.include?(:jack) and
values.include?(:queen) and
values.include?(:king) and
values.include?(:ace)
end
def four_of_a_kind?
same_of_value?(4)
end
def three_of_a_kind?
same_of_value?(3)
end
def full_house?
same_of_value?(3) and same_of_value?(2)
end
def two_pair?
collapsed == 2 and same_of_value?(2)
end
def pair?
same_of_value?(2)
end
def values
cards.map(&:value)
end
def suits
cards.map(&:suit)
end
def aces_low
sorted = cards.sort
while sorted.last.ace?
ace = sorted.pop
sorted.insert(0, ace)
end
sorted
end
def ace?
cards.any? { |card| card.ace? }
end
def high_card
cards.sort.last
end
def full_house_high_value
cards.group_by { |card| card.value }.detect { |k, v| v.count == 3 }.first
end
def full_house_low_value
cards.group_by { |card| card.value }.detect { |k, v| v.count == 2 }.first
end
def high_match_value
cards.group_by { |card| card.value }.select { |k, v| v.count > 1 }.keys.max { |a, b| VALUES.index(a) <=> VALUES.index(b) }
end
def low_match_value
cards.group_by { |card| card.value }.select { |k, v| v.count > 1 }.keys.min { |a, b| VALUES.index(a) <=> VALUES.index(b) }
end
def kicker
removed_value(high_match_value).sort.last
end
def <=> other
poker_score <=> other.poker_score
end
def poker_score
if royal_flush?
900000
elsif straight_flush?
800000 + high_card_score
elsif four_of_a_kind?
700000 + high_match_score * 100 + kicker_score
elsif full_house?
600000 + full_house_high_score * 1000 + full_house_low_score * 10
elsif flush?
500000 + high_card_score
elsif straight?
400000 + high_card_score
elsif three_of_a_kind?
300000 + high_match_score * 100 + kicker_score
elsif two_pair?
200000 + high_match_score * 1000 + low_match_score * 10 + kicker_score
elsif pair?
100000 + high_match_score * 100 + kicker_score
else
high_card_score
end
end
private
def kicker_score
VALUES.index(kicker.value)
end
def high_card_score
VALUES.index(high_card.value)
end
def high_match_score
VALUES.index(high_match_value)
end
def low_match_score
VALUES.index(low_match_value)
end
def full_house_high_score
VALUES.index(full_house_high_value)
end
def full_house_low_score
VALUES.index(full_house_low_value)
end
def removed_value(value)
cards.reject { |card| card.value == value }
end
def collapsed
cards.size - values.uniq.size
end
def same_of_value(n)
cards.detect { |card| cards.count { |c| c.value == card.value } == n }.value
end
def same_of_value?(n)
cards.any? { |card| cards.count { |c| c.value == card.value } == n }
end
def all_successors?(cards)
cards.all? {|card| card === cards.last || card.succ?(cards[cards.index(card) + 1]) }
end
end
def games
game_lines = File.readlines("../input/p054_poker.txt")
game_lines.map { |game_line| [game_line[0..13], game_line[15..28]] }.map { |one, two| [Hand.new(one), Hand.new(two)] }
end
def solution
games.map { |one, two| one > two }.count(true)
end