module PartOne CARD_ORDER = ((?2..?9).to_a + %w[ T J Q K A ]).each.with_index.to_h Card = Data.define(:rank) do def <=>(other) CARD_ORDER.fetch(self.rank) <=> CARD_ORDER.fetch(other.rank) end end Hand = Data.define(:cards) do def type tally = self.cards.tally case tally.values.sort when [5] then 5 # five of a kind when [1, 4] then 4 # four of a kind when [2, 3] then 3.5 # full house when [1, 1, 3] then 3 # three of a kind when [1, 2, 2] then 2.5 # two pair when [1, 1, 1, 2] then 2 # one pair when [1, 1, 1, 1, 1] then 1 # high card else fail "couldn't calculate type: #{self.inspect}" end end def <=>(other) v = self.type <=> other.type v.zero? ? self.cards <=> other.cards : v end end end module PartTwo CARD_ORDER = ([?J] + (?2..?9).to_a + %w[ T Q K A ]).each.with_index.to_h Card = Data.define(:rank) do def <=>(other) CARD_ORDER.fetch(self.rank) <=> CARD_ORDER.fetch(other.rank) end end Hand = Data.define(:cards) do def type tally = self.cards.map(&:rank).tally jokers = tally.delete(?J) || 0 counts = tally.values.sort if counts.empty? counts = [5] else counts[-1] += jokers end case counts when [5] then 5 # five of a kind when [1, 4] then 4 # four of a kind when [2, 3] then 3.5 # full house when [1, 1, 3] then 3 # three of a kind when [1, 2, 2] then 2.5 # two pair when [1, 1, 1, 2] then 2 # one pair when [1, 1, 1, 1, 1] then 1 # high card else fail "couldn't calculate type: #{self.inspect}" end end def <=>(other) v = self.type <=> other.type v.zero? ? self.cards <=> other.cards : v end end end hands = ARGF.read.scan(/(\w+)\s+(\d+)/).to_h {|hand, bid| [PartTwo::Hand.new(hand.chars.map { PartTwo::Card.new(_1) }), bid.to_i] } p hands .sort_by(&:first) .map.with_index {|(_,bid),i| bid * (i+1) } .sum