Skip to content

Instantly share code, notes, and snippets.

@CJSmith-0141
Last active December 2, 2023 15:55
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/b7a43228aeadfe2169cd163d38e732b3 to your computer and use it in GitHub Desktop.
Save CJSmith-0141/b7a43228aeadfe2169cd163d38e732b3 to your computer and use it in GitHub Desktop.
AoC 2023 Day 2
package net.tazato
import cats.*
import cats.data.NonEmptyList
import cats.effect.*
import cats.syntax.all.*
import cats.parse.*
import cats.derived.*
import cats.parse.Rfc5234.{alpha, digit, wsp}
import scala.io.BufferedSource
object Day2 extends IOApp.Simple {
enum Color(name: String, value: Int) {
case Red(value: Int) extends Color("red", value)
case Green(value: Int) extends Color("green", value)
case Blue(value: Int) extends Color("blue", value)
}
object Color {
given Eq[Color] = Eq.fromUniversalEquals
given Show[Color] = semiauto.showPretty
}
case class Pull(red: Color.Red, green: Color.Green, blue: Color.Blue)
object Pull {
val empty: Pull = Pull(Color.Red(0), Color.Green(0), Color.Blue(0))
given Eq[Pull] = Eq.fromUniversalEquals
given Show[Pull] = c =>
s"Pull(r, ${c.red.value}, g = ${c.green.value}, b = ${c.blue.value})"
}
case class Game(id: Int, rounds: List[Pull])
object Game {
given Eq[Game] = Eq.fromUniversalEquals
given Show[Game] = g =>
s"""Game(${g.id}, ${g.rounds.map(_.show).mkString(", ")})"""
given Show[List[Game]] = {
_.map { g =>
s"""Game(id = ${g.id},
| ${g.rounds.map(_.show).mkString(",\n ")}
|)""".stripMargin
}.mkString(",\n")
}
}
val gameIdParser = {
Parser.string("Game ") *> digit.rep.string.map(_.toInt) <* Parser.char(':')
}
val oneColorPull = {
digit.rep.string.surroundedBy(wsp.rep0).map(_.toInt) ~ alpha.rep.string
.surroundedBy(wsp.rep0) <* Parser.char(',').?
}
val pullParser = (oneColorPull.rep <* Parser.char(';').?).map { pulls =>
pulls.toList
.map { case (value, name) =>
name match
case "red" => Color.Red(value)
case "green" => Color.Green(value)
case "blue" => Color.Blue(value)
}
.foldLeft(Pull.empty) {
case (acc, Color.Red(value)) =>
acc.copy(red = Color.Red(acc.red.value + value))
case (acc, Color.Green(value)) =>
acc.copy(green = Color.Green(acc.green.value + value))
case (acc, Color.Blue(value)) =>
acc.copy(blue = Color.Blue(acc.blue.value + value))
}
}
val gameParser = {
(gameIdParser ~ pullParser.rep <* Parser.char('\n').?).map {
case (id, rounds) => Game(id, rounds.toList)
}
}
def cubeIsPossible(c: Pull): Boolean = {
c.red.value <= 12 && c.green.value <= 13 && c.blue.value <= 14
}
def powerForGame(g: List[Pull]): Long = {
val red = g.map(_.red.value).max
val green = g.map(_.green.value).max
val blue = g.map(_.blue.value).max
red * green * blue
}
val input: String =
"""Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
|Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
|Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
|Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
|""".stripMargin
val input2F: BufferedSource = scala.io.Source.fromResource("day2.txt")
val input2: String = {
try input2F.getLines().mkString("\n")
finally input2F.close()
}
def totalGameParser(i: String) = {
gameParser.rep.parse(i) match {
case Left(value) =>
IO.raiseError(new Throwable(s"parse error 😔\n ${value.show}"))
case Right(value) => IO.delay(value._2)
}
}
def answerPart1(g: List[Game]) = {
g.map { case Game(id, rounds) =>
val possible = rounds.foldLeft(true) { case (acc, round) =>
acc && cubeIsPossible(round)
}
(id, possible)
}.filter(_._2)
.map(_._1)
.sum
}
def answerPart2(g: List[Game]) = {
g.map:
case Game(_, rounds) =>
powerForGame(rounds)
.sum
}
def run: IO[Unit] = for {
res <- totalGameParser(input2)
_ <- IO.println(res.toList.show)
_ <- IO.println(answerPart1(res.toList))
_ <- IO.println(answerPart2(res.toList))
} yield ()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment