Skip to content

Commit

Permalink
Solve 2023 day 7 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Dec 7, 2023
1 parent a4f4234 commit 3b3a0ab
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 24 deletions.
89 changes: 67 additions & 22 deletions src/main/scala/eu/sim642/adventofcode2023/Day7.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ object Day7 {
type Card = Char
type Hand = Seq[Card]

private val cardStrengthOrder =
Seq('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2')

val cardOrdering: Ordering[Card] = Ordering.by(cardStrengthOrder.indexOf).reverse

enum HandType {
case FiveOfAKind
case FourOfAKind
Expand All @@ -26,25 +21,74 @@ object Day7 {

val handTypeOrdering: Ordering[HandType] = Ordering.by[HandType, Int](_.ordinal)(Ordering[Int]).reverse

def handType(hand: Hand): HandType = hand.groupCount(identity).values.toSeq.sorted(Ordering[Int].reverse) match {
case Seq(5) => HandType.FiveOfAKind
case Seq(4, 1) => HandType.FourOfAKind
case Seq(3, 2) => HandType.FullHouse
case Seq(3, 1, 1) => HandType.ThreeOfAKind
case Seq(2, 2, 1) => HandType.TwoPair
case Seq(2, 1, 1, 1) => HandType.OnePair
case Seq(1, 1, 1, 1, 1) => HandType.HighCard
case _ => throw IllegalArgumentException("invalid hand")
trait Part {
protected val cardStrengthOrder: Seq[Card]

val cardOrdering: Ordering[Card] = Ordering.by(cardStrengthOrder.indexOf).reverse

def handType(hand: Hand): HandType

val handOrdering: Ordering[Hand] = Ordering.by(handType)(handTypeOrdering).orElse(Ordering.Implicits.seqOrdering(cardOrdering))

def totalWinnings(hands: Seq[(Hand, Int)]): Int = {
hands
.sortBy(_._1)(handOrdering)
.zipWithIndex
.map({ case ((hand, bid), i) => (i + 1) * bid })
.sum
}
}

object Part1 extends Part {
override protected val cardStrengthOrder: Seq[Card] =
Seq('A', 'K', 'Q', 'J', 'T', '9', '8', '7', '6', '5', '4', '3', '2')

override def handType(hand: Hand): HandType = hand.groupCount(identity).values.toSeq.sorted(Ordering[Int].reverse) match {
case Seq(5) => HandType.FiveOfAKind
case Seq(4, 1) => HandType.FourOfAKind
case Seq(3, 2) => HandType.FullHouse
case Seq(3, 1, 1) => HandType.ThreeOfAKind
case Seq(2, 2, 1) => HandType.TwoPair
case Seq(2, 1, 1, 1) => HandType.OnePair
case Seq(1, 1, 1, 1, 1) => HandType.HighCard
case _ => throw IllegalArgumentException("invalid hand")
}
}

val handOrdering: Ordering[Hand] = Ordering.by(handType)(handTypeOrdering).orElse(Ordering.Implicits.seqOrdering(cardOrdering))
object Part2 extends Part {
override protected val cardStrengthOrder: Seq[Card] =
Seq('A', 'K', 'Q', 'T', '9', '8', '7', '6', '5', '4', '3', '2', 'J')

override def handType(hand: Hand): HandType = {
val jokers = hand.count(_ == 'J')
val handWithoutJokers = hand.filter(_ != 'J')
// TODO: simplify, is complete?
(jokers, handWithoutJokers.groupCount(identity).values.toSeq.sorted(Ordering[Int].reverse)) match {
case (5, Seq()) => HandType.FiveOfAKind
case (_, Seq(_)) => HandType.FiveOfAKind

case (3, Seq(1, 1)) => HandType.FourOfAKind
case (2, Seq(2, 1)) => HandType.FourOfAKind
case (1, Seq(3, 1)) => HandType.FourOfAKind
case (0, Seq(4, 1)) => HandType.FourOfAKind

case (1, Seq(2, 2)) => HandType.FullHouse
case (0, Seq(3, 2)) => HandType.FullHouse

case (2, Seq(1, 1, 1)) => HandType.ThreeOfAKind
case (1, Seq(2, 1, 1)) => HandType.ThreeOfAKind
case (0, Seq(3, 1, 1)) => HandType.ThreeOfAKind

case (0, Seq(2, 2, 1)) => HandType.TwoPair

case (1, Seq(1, 1, 1, 1)) => HandType.OnePair
case (0, Seq(2, 1, 1, 1)) => HandType.OnePair

case (0, Seq(1, 1, 1, 1, 1)) => HandType.HighCard

def totalWinnings(hands: Seq[(Hand, Int)]): Int = {
hands
.sortBy(_._1)(handOrdering)
.zipWithIndex
.map({ case ((hand, bid), i) => (i + 1) * bid })
.sum
case _ => throw IllegalArgumentException(s"invalid hand $hand")
}
}
}


Expand All @@ -59,6 +103,7 @@ object Day7 {
lazy val input: String = io.Source.fromInputStream(getClass.getResourceAsStream("day7.txt")).mkString.trim

def main(args: Array[String]): Unit = {
println(totalWinnings(parseHands(input)))
println(Part1.totalWinnings(parseHands(input)))
println(Part2.totalWinnings(parseHands(input)))
}
}
27 changes: 25 additions & 2 deletions src/test/scala/eu/sim642/adventofcode2023/Day7Test.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package eu.sim642.adventofcode2023

import Day7.{cardOrdering, handTypeOrdering, *}
import Day7._
import org.scalatest.funsuite.AnyFunSuite

class Day7Test extends AnyFunSuite {
Expand All @@ -13,6 +13,8 @@ class Day7Test extends AnyFunSuite {
|QQQJA 483""".stripMargin

test("Part 1 examples") {
import Part1._

assert(handType(parseHand("AAAAA")) == HandType.FiveOfAKind)
assert(handType(parseHand("AA8AA")) == HandType.FourOfAKind)
assert(handType(parseHand("23332")) == HandType.FullHouse)
Expand All @@ -28,6 +30,27 @@ class Day7Test extends AnyFunSuite {
}

test("Part 1 input answer") {
assert(totalWinnings(parseHands(input)) == 247815719)
assert(Part1.totalWinnings(parseHands(input)) == 247815719)
}

test("Part 2 examples") {
import Part2._

assert(handType(parseHand("QJJQ2")) == HandType.FourOfAKind)

assert(handType(parseHand("32T3K")) == HandType.OnePair)
assert(handType(parseHand("KK677")) == HandType.TwoPair)
assert(handType(parseHand("T55J5")) == HandType.FourOfAKind)
assert(handType(parseHand("KTJJT")) == HandType.FourOfAKind)
assert(handType(parseHand("QQQJA")) == HandType.FourOfAKind)

assert(cardOrdering.lt('J', '2'))
assert(handOrdering.lt(parseHand("JKKK2"), parseHand("QQQQ2")))

assert(totalWinnings(parseHands(exampleInput)) == 5905)
}

test("Part 2 input answer") {
assert(Part2.totalWinnings(parseHands(input)) == 248747492)
}
}

0 comments on commit 3b3a0ab

Please sign in to comment.