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