Last active
December 2, 2023 15:55
-
-
Save CJSmith-0141/b7a43228aeadfe2169cd163d38e732b3 to your computer and use it in GitHub Desktop.
AoC 2023 Day 2
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.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