diff --git a/2023/ruby/day_07.rb b/2023/ruby/day_07.rb new file mode 100644 index 0000000..5894498 --- /dev/null +++ b/2023/ruby/day_07.rb @@ -0,0 +1,77 @@ +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