Day 4
This commit is contained in:
parent
457f034d73
commit
20656e0e95
3 changed files with 73 additions and 1 deletions
|
@ -20,7 +20,6 @@ object Main extends IOApp:
|
||||||
part.flatMap { p =>
|
part.flatMap { p =>
|
||||||
io.stdinUtf8[IO](1024)
|
io.stdinUtf8[IO](1024)
|
||||||
.through(text.lines)
|
.through(text.lines)
|
||||||
.takeWhile(_.nonEmpty)
|
|
||||||
.through(p)
|
.through(p)
|
||||||
.through(io.stdoutLines(StandardCharsets.UTF_8))
|
.through(io.stdoutLines(StandardCharsets.UTF_8))
|
||||||
.compile.drain
|
.compile.drain
|
||||||
|
|
72
core/src/main/scala/tf/bug/aoc/y2020/Day4.scala
Normal file
72
core/src/main/scala/tf/bug/aoc/y2020/Day4.scala
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package tf.bug.aoc.y2020
|
||||||
|
|
||||||
|
import cats.effect._
|
||||||
|
import cats.parse.{Parser => P, Parser1, Numbers}
|
||||||
|
import cats.syntax.all._
|
||||||
|
import fs2._
|
||||||
|
import tf.bug.aoc.Day
|
||||||
|
|
||||||
|
object Day4 extends Day:
|
||||||
|
|
||||||
|
val whitespace: Parser1[Unit] = P.charIn(" ").void
|
||||||
|
|
||||||
|
val parseEntry: Parser1[(String, String)] = (P.length1(3), P.char(':'), P.until1(whitespace)).mapN {
|
||||||
|
case (key, (), value) =>
|
||||||
|
key -> value
|
||||||
|
}
|
||||||
|
|
||||||
|
val parseMap: P[Map[String, String]] = (P.repSep(parseEntry, min = 0, sep = whitespace) <* P.until(P.end)).map(_.toMap)
|
||||||
|
|
||||||
|
override def part1[F[_]: Async]: Pipe[F, String, String] =
|
||||||
|
val requiredKeys = Set(
|
||||||
|
"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"
|
||||||
|
)
|
||||||
|
parts { m =>
|
||||||
|
val keySet = m.keySet
|
||||||
|
requiredKeys.forall(keySet.contains)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def part2[F[_]: Async]: Pipe[F, String, String] =
|
||||||
|
val hexChar: P[Unit] = P.charIn(('0' to '9') ++ ('a' to 'f')).void
|
||||||
|
val validateColor: P[Unit] = P.char('#') *> hexChar.replicateA(6).void
|
||||||
|
val eyeColors: Set[String] = Set(
|
||||||
|
"amb",
|
||||||
|
"blu",
|
||||||
|
"brn",
|
||||||
|
"gry",
|
||||||
|
"grn",
|
||||||
|
"hzl",
|
||||||
|
"oth",
|
||||||
|
)
|
||||||
|
val pidChar: P[Unit] = Numbers.digit.void
|
||||||
|
val validatePid: P[Unit] = pidChar.replicateA(9).void
|
||||||
|
parts { m =>
|
||||||
|
lazy val validByr = m.get("byr").flatMap(_.toIntOption).fold(false)(x => 1920 <= x && x <= 2002)
|
||||||
|
lazy val validIyr = m.get("iyr").flatMap(_.toIntOption).fold(false)(x => 2010 <= x && x <= 2020)
|
||||||
|
lazy val validEyr = m.get("eyr").flatMap(_.toIntOption).fold(false)(x => 2020 <= x && x <= 2030)
|
||||||
|
lazy val validHgt = m.get("hgt").fold(false) {
|
||||||
|
case s"${hgtCm}cm" =>
|
||||||
|
hgtCm.toIntOption.fold(false)(x => 150 <= x && x <= 193)
|
||||||
|
case s"${hgtIn}in" =>
|
||||||
|
hgtIn.toIntOption.fold(false)(x => 59 <= x && x <= 76)
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
lazy val validHcl = m.get("hcl").fold(false)(validateColor.parseAll(_).isRight)
|
||||||
|
lazy val validEcl = m.get("ecl").fold(false)(eyeColors.contains)
|
||||||
|
lazy val validPid = m.get("pid").fold(false)(validatePid.parseAll(_).isRight)
|
||||||
|
validByr && validIyr && validEyr && validHgt && validHcl && validEcl && validPid
|
||||||
|
}
|
||||||
|
|
||||||
|
def parts[F[_]: Async](validator: Map[String, String] => Boolean): Pipe[F, String, String] =
|
||||||
|
(lines: Stream[F, String]) => {
|
||||||
|
val passportStrings: Stream[F, String] = lines.groupAdjacentBy(_.nonEmpty).collect {
|
||||||
|
case (true, strings) => strings.mkString_(" ")
|
||||||
|
}
|
||||||
|
val passportMaps: Stream[F, Map[String, String]] = passportStrings.map(parseMap.parseAll).collect {
|
||||||
|
case Right(map) => map
|
||||||
|
}
|
||||||
|
passportMaps
|
||||||
|
.map(validator)
|
||||||
|
.foldMap(if(_) 1 else 0)
|
||||||
|
.map(_.show)
|
||||||
|
}
|
|
@ -7,4 +7,5 @@ object y2020 extends Year:
|
||||||
1 -> Day1,
|
1 -> Day1,
|
||||||
2 -> Day2,
|
2 -> Day2,
|
||||||
3 -> Day3,
|
3 -> Day3,
|
||||||
|
4 -> Day4,
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue