diff --git a/2023/ruby/day_07.rb b/2023/ruby/day_07.rb index 5894498..ce8eff7 100644 --- a/2023/ruby/day_07.rb +++ b/2023/ruby/day_07.rb @@ -1,77 +1,50 @@ -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_STRENGTH = [ + [5], # five of a kind + [1, 4], # four of a kind + [2, 3], # full house + [1, 1, 3], # three of a kind + [1, 2, 2], # two pair + [1, 1, 1, 2], # one pair + [1, 1, 1, 1, 1], # high card +].reverse.each.with_index.to_h { [_1, _2] } - 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 +module PartOne + CARD_STRENGTH = ((?2..?9).to_a + %w[ T J Q K A ]).each.with_index.to_h + + def self.strength(cards) + tally = cards.tally + strengths = [ + HAND_STRENGTH.fetch(tally.values.sort), + *cards.map { CARD_STRENGTH.fetch(_1) }, + ] + strengths.map { (?a.ord + _1).chr }.join 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 + CARD_STRENGTH = ([?J] + (?2..?9).to_a + %w[ T Q K A ]).each.with_index.to_h + + def self.strength(cards) + tally = cards.tally + jokers = tally.delete(?J) || 0 + counts = tally.values.sort + if counts.empty? + counts = [5] + else + counts[-1] += jokers end + strengths = [ + HAND_STRENGTH.fetch(counts), + *cards.map { CARD_STRENGTH.fetch(_1) }, + ] + strengths.map { (?a.ord + _1).chr }.join 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] -} +hands = ARGF.read.scan(/(\w+)\s+(\d+)/).to_h { [_1.chars, _2.to_i ] } p hands - .sort_by(&:first) + # .sort_by {|(hand,_)| PartOne::strength(hand) } + .sort_by {|(hand,_)| PartTwo::strength(hand) } .map.with_index {|(_,bid),i| bid * (i+1) } .sum