diff --git a/2015/day17.scala b/2015/day17.scala new file mode 100644 index 0000000..c393ec5 --- /dev/null +++ b/2015/day17.scala @@ -0,0 +1,60 @@ +package `2015`.day17 + +import prelude.* +import scala.collection.mutable.Map + +trait Pretty[A]: + extension (x: A) inline def pretty: String + +inline given [A]: Pretty[IterableOnce[A]] with + extension (x: IterableOnce[A]) + inline def pretty = x.iterator.mkString("[", ", ", "]") + +extension [A](x: Iterable[A]) + def combinationsRepeating(size: Int): Iterator[Seq[A]] = + x.zipWithIndex.toSeq.combinations(size).map(_.map(_._1)) + +trait Logger: + def log(msg: => Any): Unit + +given Logger = NoopLogger + +object NoopLogger extends Logger: + def log(msg: => Any): Unit = {} + +object PrintLogger extends Logger: + def log(msg: => Any): Unit = println(msg) + +def part1(target: Int, containers: Iterable[Int])(using logger: Logger) = + val dp = Map[Int, Int](0 -> 1) + logger.log(s"target: $target, containers: $containers, dp: ${dp.pretty}") + + for currentSize <- containers do + for nextSize <- (target to currentSize by -1) do + for x <- dp.get(nextSize - currentSize) do + dp(nextSize) = dp.getOrElse(nextSize, 0) + x + logger.log( + f"ways to fit $nextSize%2dL => using $currentSize%2dL: x${dp(nextSize)} ($x%2d ways to fit leftover ${nextSize - currentSize}%2dL), dp: ${dp.pretty}", + ) + dp(target) + +def part2(target: Int, containers: Seq[Int])(using logger: Logger) = + def matches(kinds: Int) = + containers + .combinationsRepeating(kinds) + .tapEach(logger.log(_)) + .count(_.sum == target) + .tap(logger.log(_)) + + (1 to containers.size) + .map(matches) + .tapEach(logger.log(_)) + .find(_ > 0) + .getOrElse(throw new Exception("no match found")) + +@main def main() = + val containers = fromFile(".cache/2015/17.txt").getLines.map(_.toInt).toSeq + val target = 150 + + println(part1(target, containers)) + println(part2(target, containers)) diff --git a/2015/day17.test.scala b/2015/day17.test.scala new file mode 100644 index 0000000..f008e0d --- /dev/null +++ b/2015/day17.test.scala @@ -0,0 +1,16 @@ +package `2015`.day17 + +import prelude.* +import munit.FunSuite +import `2015`.day17.* + + +class Tests extends FunSuite: + // given Logger = PrintLogger + test("part 1"): + val res = part1(target = 25, containers = Seq(20, 15, 10, 5, 5)) + assertEquals(res, 4) + + test("part2"): + val res = part2(target = 25, containers = Seq(20, 15, 10, 5, 5)) + assertEquals(res, 3)