From 6db15ba9af3051a6f3535c5ac271068eeecf16c0 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 21 Dec 2024 20:01:19 +0100 Subject: [PATCH] Day 21 --- aoc/src/dev/ctsk/aoc/Main.scala | 3 +- aoc/src/dev/ctsk/aoc/days/Day21.scala | 48 +++++++++++++++++++++++++++ data/21.ans | 2 ++ data/21.in | 5 +++ 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 aoc/src/dev/ctsk/aoc/days/Day21.scala create mode 100644 data/21.ans create mode 100644 data/21.in diff --git a/aoc/src/dev/ctsk/aoc/Main.scala b/aoc/src/dev/ctsk/aoc/Main.scala index b8555c7..81de216 100644 --- a/aoc/src/dev/ctsk/aoc/Main.scala +++ b/aoc/src/dev/ctsk/aoc/Main.scala @@ -22,7 +22,8 @@ val solvers = Map[Int, Solver]( 17 -> Day17, 18 -> Day18, 19 -> Day19, - 20 -> Day20 + 20 -> Day20, + 21 -> Day21 ) def runSolver(solver: Solver, input: os.Path): Unit = diff --git a/aoc/src/dev/ctsk/aoc/days/Day21.scala b/aoc/src/dev/ctsk/aoc/days/Day21.scala new file mode 100644 index 0000000..1e4aff7 --- /dev/null +++ b/aoc/src/dev/ctsk/aoc/days/Day21.scala @@ -0,0 +1,48 @@ +package dev.ctsk.aoc.days + +import dev.ctsk.aoc.* + +object Day21 extends Solver(21): + private val keyPad = Grid(Array("789", "456", "123", " 0A").map(_.toArray)) + private val buttonPad = Grid(Array(" ^A", "").map(_.toArray)) + + private val paths = Memo(paths_) + private def paths_(args: (Char, Char, Grid[Char])): Array[Array[Char]] = + val (from, to, grid) = args + val Point(fx, fy) = grid.findFirst(_ == from).get + val Point(tx, ty) = grid.findFirst(_ == to).get + val ops = Seq((fx, tx, 'v'), (tx, fx, '^'), (fy, ty, '>'), (ty, fy, '<')) + val seq = ops.flatMap((a, b, c) => (a until b).map(_ => c)) + + def gap(path: Array[Char]): Boolean = + path + .scanLeft(Point(fx, fy))((acc, c) => Direction.from(c)(acc)) + .exists(grid(_).contains(' ')) + + seq.permutations.map(_.toArray).filterNot(gap).toArray + + private def countPresses(codes: Array[String], numRobots: Int): Long = + type State = (Char, Char, Int) + def minCost_(state: State, f: State => Long): Long = + val (from, to, robots) = state + if robots == 0 then return 1 + paths(from, to, if robots == numRobots then keyPad else buttonPad) + .map('A' +: _ :+ 'A') + .map(path => path.sliding(2).map(w => f(w(0), w(1), robots - 1)).sum) + .min + val minCost = Memo.Y(minCost_) + + def evalCode(line: String): Long = + ('A' +: line).sliding(2).map(w => minCost(w(0), w(1), numRobots)).sum + + codes.map(code => code.init.toInt * evalCode(code)).sum + + override def run(input: os.ReadablePath): (Timings, Solution) = + val codes = os.read.lines(input).toArray + val (p1_time, p1_solution) = timed { countPresses(codes, 3) } + val (p2_time, p2_solution) = timed { countPresses(codes, 26) } + + ( + Timings(0, p1_time, p2_time), + Solution(Long.box(p1_solution), Long.box(p2_solution)) + ) diff --git a/data/21.ans b/data/21.ans new file mode 100644 index 0000000..6a76924 --- /dev/null +++ b/data/21.ans @@ -0,0 +1,2 @@ +202274 +245881705840972 diff --git a/data/21.in b/data/21.in new file mode 100644 index 0000000..b9addc5 --- /dev/null +++ b/data/21.in @@ -0,0 +1,5 @@ +319A +670A +349A +964A +586A