commit 1f224d4ed61c963219e6afbc6c380069c44d9cca Author: Aly Date: Fri Dec 4 14:22:14 2020 -0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a16e406 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/sbt,scala,metals,bloop,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=sbt,scala,metals,bloop,visualstudiocode + +### Bloop ### +.bloop/ + +### Metals ### +.metals/ +project/metals.sbt +project/project/ + +### SBT ### +# Simple Build Tool +# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control + +dist/* +target/ +lib_managed/ +src_managed/ +project/boot/ +project/plugins/project/ +.history +.cache +.lib/ + +### Scala ### +*.class +*.log + +### VisualStudioCode ### +.vscode/* +!.vscode/tasks.json +!.vscode/launch.json +*.code-workspace + +### VisualStudioCode Patch ### +# Ignore all local history of files +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/sbt,scala,metals,bloop,visualstudiocode diff --git a/build.sbt b/build.sbt new file mode 100644 index 0000000..ba560f9 --- /dev/null +++ b/build.sbt @@ -0,0 +1,12 @@ +lazy val core = (project in file("core")).settings( + organization := "tf.bug", + name := "aoc", + version := "0.1.0", + scalaVersion := "3.0.0-M2", + libraryDependencies ++= Seq( + "org.typelevel" %% "cats-core" % "2.3.0", + "org.typelevel" %% "cats-effect" % "3.0.0-M4", + "co.fs2" %% "fs2-core" % "3.0.0-M6", + "co.fs2" %% "fs2-io" % "3.0.0-M6", + ), +) diff --git a/core/src/main/scala/tf/bug/aoc/Day.scala b/core/src/main/scala/tf/bug/aoc/Day.scala new file mode 100644 index 0000000..8c0172a --- /dev/null +++ b/core/src/main/scala/tf/bug/aoc/Day.scala @@ -0,0 +1,9 @@ +package tf.bug.aoc + +import cats._ +import cats.effect._ +import fs2._ + +trait Day: + def part1[F[_]: Sync]: Pipe[F, String, String] + def part2[F[_]: Sync]: Pipe[F, String, String] diff --git a/core/src/main/scala/tf/bug/aoc/Main.scala b/core/src/main/scala/tf/bug/aoc/Main.scala new file mode 100644 index 0000000..0fa6754 --- /dev/null +++ b/core/src/main/scala/tf/bug/aoc/Main.scala @@ -0,0 +1,54 @@ +package tf.bug.aoc + +import cats.effect._ +import cats.effect.std.Console +import cats.syntax.all._ +import java.nio.charset.StandardCharsets +import fs2._ + +object Main extends IOApp: + + def years: Map[Int, Year] = Map( + 2020 -> y2020.y2020, + ) + + 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] + part.flatMap { p => + io.stdinUtf8[IO](1024) + .through(text.lines) + .takeWhile(_.nonEmpty) + .through(p) + .through(io.stdoutLines(StandardCharsets.UTF_8)) + .compile.drain + }.as(ExitCode.Success) + + def selectYear[F[_]: Sync: Console]: F[Year] = + val ask = Sync[F].delay(print("Select year: ")) + val receive = Console[F].readLine + val year: F[Option[Year]] = (ask >> receive).map(_.toIntOption >>= years.get) + year.flatMap { + case Some(year) => year.pure[F] + case None => selectYear[F] + } + + def selectDay[F[_]: Sync: Console](year: Year): F[Day] = + val ask = Sync[F].delay(print("Select day: ")) + val receive = Console[F].readLine + val day: F[Option[Day]] = (ask >> receive).map(_.toIntOption >>= year.days.get) + day.flatMap { + case Some(day) => day.pure[F] + case None => selectDay[F](year) + } + + def selectPart[F[_]: Sync: Console](day: Day): F[Pipe[F, String, String]] = + val ask = Sync[F].delay(print("Select part: ")) + val receive = Console[F].readLine + val part: F[Option[Int]] = (ask >> receive).map(_.toIntOption) + part.flatMap { + case Some(1) => day.part1[F].pure[F] + case Some(2) => day.part2[F].pure[F] + case _ => selectPart[F](day) + } diff --git a/core/src/main/scala/tf/bug/aoc/Year.scala b/core/src/main/scala/tf/bug/aoc/Year.scala new file mode 100644 index 0000000..8abe910 --- /dev/null +++ b/core/src/main/scala/tf/bug/aoc/Year.scala @@ -0,0 +1,4 @@ +package tf.bug.aoc + +trait Year: + def days: Map[Int, Day] diff --git a/core/src/main/scala/tf/bug/aoc/y2020/Day1.scala b/core/src/main/scala/tf/bug/aoc/y2020/Day1.scala new file mode 100644 index 0000000..c172310 --- /dev/null +++ b/core/src/main/scala/tf/bug/aoc/y2020/Day1.scala @@ -0,0 +1,31 @@ +package tf.bug.aoc.y2020 + +import cats._ +import cats.effect._ +import cats.syntax.all._ +import fs2._ +import java.nio.charset.StandardCharsets +import scala.util.Try +import tf.bug.aoc.Day + +object Day1 extends Day: + + def part1[F[_]: Sync]: Pipe[F, String, String] = + parts[F](2) + + def part2[F[_]: Sync]: Pipe[F, String, String] = + parts[F](3) + + def parts[F[_]: Sync](nums: Int): Pipe[F, String, String] = + (lines: Stream[F, String]) => { + val ints: F[Vector[Int]] = lines + .map(_.toInt) + .compile.toVector + val result: F[Option[Int]] = ints.map(sumTargetProduct(2020, nums, _)) + Stream.eval(result).map(_.show) + } + + def sumTargetProduct(target: Int, num: Int, ints: Vector[Int]): Option[Int] = + ints.combinations(num).collectFirst { + case v if v.sum == target => v.product + } diff --git a/core/src/main/scala/tf/bug/aoc/y2020/y2020.scala b/core/src/main/scala/tf/bug/aoc/y2020/y2020.scala new file mode 100644 index 0000000..97ee44e --- /dev/null +++ b/core/src/main/scala/tf/bug/aoc/y2020/y2020.scala @@ -0,0 +1,8 @@ +package tf.bug.aoc.y2020 + +import tf.bug.aoc.{Day, Year} + +object y2020 extends Year: + override def days: Map[Int, Day] = Map( + 1 -> Day1, + ) diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..7de0a93 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.4.4 diff --git a/project/plugins.sbt b/project/plugins.sbt new file mode 100644 index 0000000..8770c50 --- /dev/null +++ b/project/plugins.sbt @@ -0,0 +1,2 @@ +addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.16") +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % "0.4.6")