This commit is contained in:
Aly 2020-12-06 14:39:00 -08:00
parent 5dde3b6445
commit ba564dc7e1
10 changed files with 66 additions and 15 deletions

View file

@ -4,6 +4,7 @@ import cats._
import cats.effect._
import fs2._
trait Day:
trait Day {
def part1[F[_]: Async]: Pipe[F, String, String]
def part2[F[_]: Async]: Pipe[F, String, String]
}

View file

@ -7,13 +7,13 @@ import cats.syntax.all._
import java.nio.charset.StandardCharsets
import fs2._
object Main extends IOApp:
object Main extends IOApp {
def years: Map[Int, Year] = Map(
2020 -> y2020.y2020,
)
override def run(args: List[String]): IO[ExitCode] =
override def run(args: List[String]): IO[ExitCode] = {
val year: IO[Year] = selectYear[IO]
val day: IO[Day] = year >>= selectDay[IO]
val part: IO[Pipe[IO, String, String]] = day >>= selectPart[IO]
@ -24,8 +24,9 @@ object Main extends IOApp:
.through(io.stdoutLines(StandardCharsets.UTF_8))
.compile.drain
}.as(ExitCode.Success)
}
def selectYear[F[_]: Monad: Console]: F[Year] =
def selectYear[F[_]: Monad: Console]: F[Year] = {
val ask = Console[F].print("Select year: ")
val receive = Console[F].readLine
val year: F[Option[Year]] = (ask >> receive).map(_.toIntOption >>= years.get)
@ -33,8 +34,9 @@ object Main extends IOApp:
case Some(year) => year.pure[F]
case None => selectYear[F]
}
}
def selectDay[F[_]: Monad: Console](year: Year): F[Day] =
def selectDay[F[_]: Monad: Console](year: Year): F[Day] = {
val ask = Console[F].print("Select day: ")
val receive = Console[F].readLine
val day: F[Option[Day]] = (ask >> receive).map(_.toIntOption >>= year.days.get)
@ -42,8 +44,9 @@ object Main extends IOApp:
case Some(day) => day.pure[F]
case None => selectDay[F](year)
}
}
def selectPart[F[_]: Async: Console](day: Day): F[Pipe[F, String, String]] =
def selectPart[F[_]: Async: Console](day: Day): F[Pipe[F, String, String]] = {
val ask = Console[F].print("Select part: ")
val receive = Console[F].readLine
val part: F[Option[Int]] = (ask >> receive).map(_.toIntOption)
@ -52,3 +55,6 @@ object Main extends IOApp:
case Some(2) => day.part2[F].pure[F]
case _ => selectPart[F](day)
}
}
}

View file

@ -1,4 +1,5 @@
package tf.bug.aoc
trait Year:
trait Year {
def days: Map[Int, Day]
}

View file

@ -8,7 +8,7 @@ import java.nio.charset.StandardCharsets
import scala.util.Try
import tf.bug.aoc.Day
object Day1 extends Day:
object Day1 extends Day {
override def part1[F[_]: Async]: Pipe[F, String, String] =
parts[F](2)
@ -29,3 +29,5 @@ object Day1 extends Day:
ints.combinations(num).collectFirst {
case v if v.sum == target => v.product
}
}

View file

@ -6,7 +6,7 @@ import cats.syntax.all._
import fs2._
import tf.bug.aoc.Day
object Day2 extends Day:
object Day2 extends Day {
case class Policy(min: Int, max: Int, letter: Char)
case class Entry(policy: Policy, password: String)
@ -45,3 +45,5 @@ object Day2 extends Day:
.foldMap(ent => if(validation(ent)) 1 else 0)
.map(_.show)
}
}

View file

@ -5,7 +5,7 @@ import cats.syntax.all._
import fs2._
import tf.bug.aoc.Day
object Day3 extends Day:
object Day3 extends Day {
case class Path(x: Int, treeCount: Long)
@ -44,3 +44,5 @@ object Day3 extends Day:
}
}.map(_.treeCount)
}
}

View file

@ -6,7 +6,7 @@ import cats.syntax.all._
import fs2._
import tf.bug.aoc.Day
object Day4 extends Day:
object Day4 extends Day {
val whitespace: Parser1[Unit] = P.charIn(" ").void
@ -17,7 +17,7 @@ object Day4 extends Day:
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] =
override def part1[F[_]: Async]: Pipe[F, String, String] = {
val requiredKeys = Set(
"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"
)
@ -25,8 +25,9 @@ object Day4 extends Day:
val keySet = m.keySet
requiredKeys.forall(keySet.contains)
}
}
override def part2[F[_]: Async]: Pipe[F, String, String] =
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(
@ -56,6 +57,7 @@ object Day4 extends Day:
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]) => {
@ -70,3 +72,5 @@ object Day4 extends Day:
.foldMap(if(_) 1 else 0)
.map(_.show)
}
}

View file

@ -6,7 +6,7 @@ import fs2._
import scala.collection.SortedSet
import tf.bug.aoc.Day
object Day5 extends Day:
object Day5 extends Day {
override def part1[F[_]: Async]: Pipe[F, String, String] =
(lines: Stream[F, String]) => {
@ -44,3 +44,5 @@ object Day5 extends Day:
val newBit = ((ch & 4) >>> 2) ^ 1
(id * 2) + newBit
})
}

View file

@ -0,0 +1,27 @@
package tf.bug.aoc.y2020
import cats.effect._
import cats.syntax.all._
import fs2._
import tf.bug.aoc.Day
object Day6 extends Day {
override def part1[F[_]: Async]: Pipe[F, String, String] =
parts(_ | _)
override def part2[F[_]: Async]: Pipe[F, String, String] =
parts(_ & _)
def parts[F[_]: Async](reduction: (Set[Char], Set[Char]) => Set[Char]): Pipe[F, String, String] =
(lines: Stream[F, String]) => {
lines.groupAdjacentBy(_.nonEmpty).collect {
case (true, strings) =>
strings
.map(_.toSet)
.reduceLeftOption(reduction)
.getOrElse(Set.empty[Char])
}.map(_.size).foldMonoid.map(_.show)
}
}

View file

@ -2,11 +2,15 @@ package tf.bug.aoc.y2020
import tf.bug.aoc.{Day, Year}
object y2020 extends Year:
object y2020 extends Year {
override def days: Map[Int, Day] = Map(
1 -> Day1,
2 -> Day2,
3 -> Day3,
4 -> Day4,
5 -> Day5,
6 -> Day6,
)
}