2020-12-04 23:06:03 +00:00
|
|
|
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 Day2 extends Day:
|
|
|
|
|
|
|
|
case class Policy(min: Int, max: Int, letter: Char)
|
|
|
|
case class Entry(policy: Policy, password: String)
|
|
|
|
|
|
|
|
val parsePolicy: Parser1[Policy] =
|
|
|
|
(Numbers.nonNegativeIntString, P.char('-'), Numbers.nonNegativeIntString, P.char(' '), P.anyChar).mapN {
|
|
|
|
case (min, (), max, (), letter) =>
|
|
|
|
Policy(min.toInt, max.toInt, letter)
|
|
|
|
}
|
|
|
|
|
|
|
|
val parseEntry: Parser1[Entry] =
|
|
|
|
(parsePolicy, P.string1(": "), P.until1(P.end)).mapN {
|
|
|
|
case (policy, (), password) =>
|
|
|
|
Entry(policy, password)
|
|
|
|
}
|
|
|
|
|
2020-12-05 01:26:20 +00:00
|
|
|
override def part1[F[_]: Async]: Pipe[F, String, String] =
|
2020-12-04 23:06:03 +00:00
|
|
|
parts[F] { ent =>
|
|
|
|
val charCount = ent.password.count(_ == ent.policy.letter)
|
|
|
|
ent.policy.min <= charCount && charCount <= ent.policy.max
|
|
|
|
}
|
|
|
|
|
2020-12-05 01:26:20 +00:00
|
|
|
override def part2[F[_]: Async]: Pipe[F, String, String] =
|
2020-12-04 23:06:03 +00:00
|
|
|
parts[F] { ent =>
|
|
|
|
val a = ent.policy.min <= ent.password.length && ent.password.charAt(ent.policy.min - 1) == ent.policy.letter
|
|
|
|
val b = ent.policy.max <= ent.password.length && ent.password.charAt(ent.policy.max - 1) == ent.policy.letter
|
|
|
|
(a || b) && !(a && b)
|
|
|
|
}
|
|
|
|
|
|
|
|
def parts[F[_]: Sync](validation: Entry => Boolean): Pipe[F, String, String] =
|
|
|
|
(lines: Stream[F, String]) => {
|
|
|
|
lines.map(parseEntry.parseAll)
|
|
|
|
.collect {
|
|
|
|
case Right(entry) => entry
|
|
|
|
}
|
|
|
|
.foldMap(ent => if(validation(ent)) 1 else 0)
|
|
|
|
.map(_.show)
|
|
|
|
}
|