diff --git a/aoc/src/dev/ctsk/aoc/Main.scala b/aoc/src/dev/ctsk/aoc/Main.scala index f4957ce..fa100cc 100644 --- a/aoc/src/dev/ctsk/aoc/Main.scala +++ b/aoc/src/dev/ctsk/aoc/Main.scala @@ -18,7 +18,8 @@ val solvers = Map[Int, Solver]( 13 -> Day13, 14 -> Day14, 15 -> Day15, - 16 -> Day16 + 16 -> Day16, + 17 -> Day17 ) def runSolver(solver: Solver, input: os.Path): Unit = diff --git a/aoc/src/dev/ctsk/aoc/days/Day17.scala b/aoc/src/dev/ctsk/aoc/days/Day17.scala new file mode 100644 index 0000000..e260fa1 --- /dev/null +++ b/aoc/src/dev/ctsk/aoc/days/Day17.scala @@ -0,0 +1,78 @@ +package dev.ctsk.aoc.days + +import dev.ctsk.aoc.* + +import scala.util.boundary +import scala.util.boundary.break + +object Day17 extends Solver(17): + case class Machine(var code: IndexedSeq[Long]): + def execute( + registers: Map[Char, Long], + output: Option[Int] = None + ): Vector[Long] = + val reg = Array.from(registers).sorted.map(_._2) + var pc = 0 + + def combo(i: Long): Long = + i match + case i if i <= 3 => i + case i if i > 3 && i <= 6 => reg(i.toInt - 4) + + var out = Vector.empty[Long] + boundary: + while pc < code.length do + code(pc) match + case 0 => reg(0) >>= combo(code(pc + 1)); pc += 2 + case 1 => reg(1) ^= code(pc + 1); pc += 2 + case 2 => reg(1) = combo(code(pc + 1)) % 8; pc += 2 + case 3 => if reg(0) != 0 then pc = code(pc + 1).toInt else pc += 2 + case 4 => reg(1) = reg(1) ^ reg(2); pc += 2 + case 5 => + out :+= combo(code(pc + 1)) % 8; + if output.exists(_ >= out.length) then break() + pc += 2 + case 6 => reg(1) = reg(0) >> combo(code(pc + 1)); pc += 2 + case 7 => reg(2) = reg(0) >> combo(code(pc + 1)); pc += 2 + + out + + def part1(machine: Machine, initialRegs: Map[Char, Long]): String = + machine.execute(initialRegs).mkString(",") + + def part2(machine: Machine, initialRegs: Map[Char, Long]): Long = + val target = machine.code.reverse + + def rec(pos: Int = 0, acc: Long = 0): Option[Long] = + if pos >= target.length then return Some(acc) + def check(aVal: Long): Boolean = + machine.execute(initialRegs.updated('A', aVal), Some(1)).head == target( + pos + ) + + (0 until 8) + .map((acc << 3) + _) + .filter(check) + .flatMap(rec(pos + 1, _)) + .headOption + + rec().get + + override def run(input: os.ReadablePath): (Timings, Solution) = + val Array(initial_, code_) = os.read(input).split("\n\n") + val initialRegs = initial_.linesIterator + .map(line => { + val spl = line.split(" "); + (spl(1)(0) -> spl(2).toLong) + }) + .toMap + val code = longs(code_) + val machine = Machine(code) + + val (p1_time, p1_solution) = timed { part1(machine, initialRegs) } + val (p2_time, p2_solution) = timed { part2(machine, initialRegs) } + + ( + Timings(0, p1_time, p2_time), + Solution(p1_solution, Long.box(p2_solution)) + ) diff --git a/data/17.ans b/data/17.ans new file mode 100644 index 0000000..c826253 --- /dev/null +++ b/data/17.ans @@ -0,0 +1,2 @@ +2,0,7,3,0,3,1,3,7 +247839539763386 diff --git a/data/17.in b/data/17.in new file mode 100644 index 0000000..6cb64ca --- /dev/null +++ b/data/17.in @@ -0,0 +1,5 @@ +Register A: 18427963 +Register B: 0 +Register C: 0 + +Program: 2,4,1,1,7,5,0,3,4,3,1,6,5,5,3,0