Skip to content

Instantly share code, notes, and snippets.

@CJSmith-0141
Created December 4, 2023 16:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CJSmith-0141/11981323258a79e497539639763777e4 to your computer and use it in GitHub Desktop.
Save CJSmith-0141/11981323258a79e497539639763777e4 to your computer and use it in GitHub Desktop.
AoC 2023 Day 4
package net.tazato
import cats.*
import cats.effect.*
import cats.syntax.all.*
import cats.parse.Parser as P
import cats.parse.Rfc5234.{digit, wsp}
import scala.collection.mutable
object Day4 extends IOApp.Simple {
case class Card(id: Int, winners: Set[Int], yours: Set[Int])
case class RectifiedCard(id: Int, matches: Int)
case class CurrentState(stack: mutable.Stack[RectifiedCard], points: Int)
val input: String = io.Source.fromResource("day4.txt").mkString
def calculatePoints(cards: List[RectifiedCard]): Int =
cards.map(c => scala.math.pow(2, c.matches - 1).toInt).sum
def rectifyCards(cards: List[Card]): List[RectifiedCard] = {
val intersects = cards.map(card => card.yours intersect card.winners)
val sizes = intersects.map(_.size)
val rectified =
cards.zip(sizes).map((card, size) => RectifiedCard(card.id, size))
rectified
}
def createInitialStack(cards: List[RectifiedCard]): CurrentState = {
val stack = mutable.Stack[RectifiedCard]()
cards.reverse.foreach(stack.push)
CurrentState(stack, cards.length)
}
def processTopCard(
cards: List[RectifiedCard],
state: CurrentState
): CurrentState =
val topCard = state.stack.pop()
val cardsToPush = cards.slice(topCard.id, topCard.id + topCard.matches)
cardsToPush.foreach(state.stack.push)
CurrentState(state.stack, state.points + cardsToPush.length)
def processState(
cards: List[RectifiedCard],
state: CurrentState
): CurrentState =
if state.stack.isEmpty then state
else processState(cards, processTopCard(cards, state))
val result1F = {
CardsParser.parse(input) match
case Left(error) => IO.raiseError(new Throwable(s"\n${error.show}"))
case Right(cards) => IO.delay(calculatePoints(rectifyCards(cards.toList)))
}.flatMap(IO.println(_))
val result2F =
CardsParser.parse(input) match
case Left(error) => IO.raiseError(new Throwable(s"\n${error.show}"))
case Right(cards) =>
IO.delay {
val rectified = rectifyCards(cards.toList)
val state = createInitialStack(rectified)
val finalState = processState(rectified, state)
finalState.points
}.flatMap(IO.println(_))
def run: IO[Unit] = result2F
object CardsParser {
val number: P[Int] =
wsp.rep0.with1 *> digit.rep.string.map(_.toInt) <* wsp.rep0
val card: P[Card] = for {
id <- P.string("Card") ~ wsp.rep *> digit.rep.string.map(_.toInt) <* P
.char(':')
winners <- number.rep <* P.char('|')
yours <- number.rep <* P.char('\n').?
} yield Card(id, winners.toList.toSet, yours.toList.toSet)
def parse(input: String) = card.rep.parseAll(input)
}
}
package net.tazato
import weaver.*
import cats.syntax.all.*
object Day4Suite extends weaver.FunSuite {
val example: String = """Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
*Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
*Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
*Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
*Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
*Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11"""
.stripMargin('*')
test("parse example") {
val cards = Day4.CardsParser.parse(example)
cards match
case Left(value) => failure(value.show)
case Right(value) => success
}
test("calculate example") {
Day4.CardsParser.parse(example) match
case Left(value) => failure(s"\n${value.show}")
case Right(allCards) =>
expect(Day4.calculatePoints(Day4.rectifyCards(allCards.toList)) == 13)
}
test("initial stack") {
val cards = Day4.CardsParser.parse(example)
cards match
case Left(value) => failure(value.show)
case Right(cards) =>
val r = Day4.rectifyCards(cards.toList)
expect(r.head.matches == 4)
}
test("process state") {
val cards = Day4.CardsParser.parse(example)
cards match
case Left(value) => failure(value.show)
case Right(cards) =>
val r = Day4.rectifyCards(cards.toList)
val init = Day4.createInitialStack(r)
val s = Day4.processState(r, init)
expect(s.points == 30)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment