Created
December 4, 2023 16:25
-
-
Save CJSmith-0141/11981323258a79e497539639763777e4 to your computer and use it in GitHub Desktop.
AoC 2023 Day 4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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