aoc/core/src/main/scala/tf/bug/aoc/y2020/Day3.scala

49 lines
1.5 KiB
Scala

package tf.bug.aoc.y2020
import cats.effect._
import cats.syntax.all._
import fs2._
import tf.bug.aoc.Day
object Day3 extends Day {
case class Path(x: Int, treeCount: Long)
override def part1[F[_]: Async]: Pipe[F, String, String] =
_.through(checkSlope[F](3, 1)).map(_.show)
override def part2[F[_]: Async]: Pipe[F, String, String] =
(lines: Stream[F, String]) => {
val pipes: Vector[Pipe[F, String, Long]] = Vector(
checkSlope[F](1, 1),
checkSlope[F](3, 1),
checkSlope[F](5, 1),
checkSlope[F](7, 1),
checkSlope[F](1, 2),
)
val broadcast: Stream[F, String] => Stream[F, Stream[F, String]] = concurrent.Broadcast[F, String](pipes.size)
val channels: Stream[F, Stream[F, String]] = lines.through(broadcast).take(pipes.size)
val results: Stream[F, Long] = channels.zipWithIndex.map {
case (channel, ind) =>
val pipe = pipes(ind.toInt)
channel.through(pipe)
}.parJoin(pipes.size)
results.fold(1L)(_ * _).map(_.show)
}
def checkSlope[F[_]](right: Int, down: Int): Pipe[F, String, Long] =
(lines: Stream[F, String]) => {
lines.chunkN(down, true).fold(Path(0, 0L)) {
case (Path(x, numTrees), group) =>
group.head match {
case Some(line) =>
val isTree = line.charAt(x % line.length) == '#'
Path(x + right, if(isTree) numTrees + 1L else numTrees)
case None =>
Path(x, numTrees)
}
}.map(_.treeCount)
}
}