[2023][ruby][7.x]
change xktuywmyumqrquumkwukyrwqmkmywsnp
commit f31f95dcc8c84bcb399cd6cfbd5a57059a687de5
author Alpha Chen <alpha@kejadlen.dev>
date
parent pmwvxnky
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