aoc2023/Day7.scala

93 lines
2.5 KiB
Scala
Raw Normal View History

2023-12-07 10:29:25 +00:00
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 ranks = deck.sorted.toArray
if ranks(0) == ranks(4) then Five
else if ranks(0) == ranks(3) || ranks(1) == ranks(4) then Four
else if ranks(0) == ranks(2) && ranks(3) == ranks(4) || ranks(0) == ranks(
1
) && ranks(2) == ranks(4)
then House
else if ranks(0) == ranks(1) && ranks(2) == ranks(3) || ranks(0) == ranks(
1
) && ranks(3) == ranks(4) || ranks(1) == ranks(2) && ranks(3) == ranks(4)
then Pairs
else
lazy val three = (0 until ranks.length - 2)
.find(i => ranks(i) == ranks(i + 2))
.map(i => Three)
lazy val pair = (0 until ranks.length - 1)
.find(i => ranks(i) == ranks(i + 1))
.map(i => Pair)
three.orElse(pair).getOrElse(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 =
deck.iterator
.foldLeft(Iterator("")) { (it, chr) =>
if chr == 'J' then it.flatMap(s => possibleCards.map(s + _))
else it.map(_ + chr)
}
.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