package aoc.day7 import aoc._ val possibleCards = "AKQJT98765432".reverse enum DeckKind: case High case Pair case Pairs case Three case House case Four case Five object DeckKind: def apply(deck: String) = val counts = deck.iterator.toSeq.groupBy(identity).values.map(_.length).toSeq if counts.contains(5) then Five else if counts.contains(4) then Four else if counts.contains(3) then if counts.contains(2) then House else Three else if counts.count(_ == 2) == 2 then Pairs else if counts.contains(2) then Pair else High import DeckKind._ case class Hand(deck: String, bet: Int): def deckRank(using cardToRank: Map[Char, Int]) = deck.map(cardToRank(_)) val kind = DeckKind(deck) lazy val bestKind = possibleCards .map(c => deck.replace('J', c)) .map(DeckKind(_)) .maxBy(_.ordinal) inline def nonZero(ins: Int*) = ins.find(_ != 0).getOrElse(0) object Parser extends CommonParser: val deck = (s"[${possibleCards}]{5}").r val hand = deck ~ num ^^ { case (deck ~ num) => Hand(deck, num) } def apply(s: String) = parse(hand, s).get // part 1 def handOrdering(toKind: Hand => DeckKind)(using cardOrdering: Map[Char, Int]) = new Ordering[Hand]: override def compare(dx: Hand, dy: Hand): Int = val (x, y) = (toKind(dx), toKind(dy)) if x.ordinal != y.ordinal then x.ordinal.compareTo(y.ordinal) else nonZero(dx.deckRank.zip(dy.deckRank).map(_ compareTo _)*) def print(using Ordering[Hand]) = val hands = lines.map(Parser(_)).toSeq val res = hands.sorted.zipWithIndex .map((h, idx) => h.bet * (idx + 1L)) .sum println(res) def part1 = val ordering = possibleCards.zipWithIndex.toMap val handOrd = handOrdering(_.kind)(using ordering) print(using handOrd) // part 2 def part2 = val ordering = "J23456789TQKA".zipWithIndex.toMap val handOrd = handOrdering(_.bestKind)(using ordering) print(using handOrd) @main def Day7(part: Int) = part match case 1 => part1 case 2 => part2