|
| 1 | +package `2024`.day17 |
| 2 | + |
| 3 | +import prelude.* |
| 4 | +import scala.collection.immutable.Queue |
| 5 | + |
| 6 | +enum Code: |
| 7 | + case Adv, Bxl, Bst, Jnz, Bxc, Out, Bdv, Cdv |
| 8 | +object Code: |
| 9 | + val ops = Vector(Adv, Bxl, Bst, Jnz, Bxc, Out, Bdv, Cdv) |
| 10 | + def parse(c: Int): Code = ops(c) |
| 11 | + |
| 12 | +enum Reg(val v: Int): |
| 13 | + case A extends Reg(0) |
| 14 | + case B extends Reg(1) |
| 15 | + case C extends Reg(2) |
| 16 | + |
| 17 | +case class Regs(regs: Vector[Int]): |
| 18 | + def apply(r: Reg): Int = regs(r.v) |
| 19 | + def update(r: Reg, v: Int): Regs = Regs(regs.updated(r.v, v)) |
| 20 | +object Regs: |
| 21 | + def apply(regs: Int*) = new Regs(regs.toVector) |
| 22 | + |
| 23 | +case class Computer( |
| 24 | + regs: Regs = Regs(0, 0, 0), |
| 25 | + inst: Vector[(Code, Int)], |
| 26 | + output: Queue[Int] = Queue.empty, |
| 27 | + ptr: Int = 0, |
| 28 | +): |
| 29 | + extension (r: Reg) |
| 30 | + def <--(v: Int) = copy(regs = (regs(r) = v), ptr = ptr + 1) |
| 31 | + def get: Int = regs(r) |
| 32 | + |
| 33 | + extension (op: Int) |
| 34 | + def combo: Int = op match |
| 35 | + case 4 => Reg.A.get |
| 36 | + case 5 => Reg.B.get |
| 37 | + case 6 => Reg.C.get |
| 38 | + case 7 => ??? |
| 39 | + case _ => op |
| 40 | + def div = Reg.A.get / (2 ** op.combo) |
| 41 | + def modulo = op.combo & 0b111 |
| 42 | + |
| 43 | + def unfold: Iterator[Computer] = |
| 44 | + Iterator.unfold(this)(_.next.map(x => (x, x))) |
| 45 | + def run: Computer = unfold.foldLeft(this)((_, current) => current) |
| 46 | + |
| 47 | + def next: Option[Computer] = |
| 48 | + inst.lift(ptr).map { (code, op) => next(code, op) } |
| 49 | + |
| 50 | + def next(code: Code, op: Int): Computer = code match |
| 51 | + case Code.Adv => Reg.A <-- op.div |
| 52 | + case Code.Bdv => Reg.B <-- op.div |
| 53 | + case Code.Cdv => Reg.C <-- op.div |
| 54 | + case Code.Bxl => Reg.B <-- (Reg.B.get ^ op) |
| 55 | + case Code.Bst => Reg.B <-- op.modulo |
| 56 | + case Code.Bxc => Reg.B <-- (Reg.B.get ^ Reg.C.get) |
| 57 | + case Code.Jnz => copy(ptr = if Reg.A.get == 0 then ptr + 1 else op) |
| 58 | + case Code.Out => copy(output = output :+ op.modulo, ptr = ptr + 1) |
| 59 | + |
| 60 | + def pretty = s""" |
| 61 | + ${ptr.toString.padTo(3, ' ')} @ ${regs.regs |
| 62 | + .map(x => s"${x.toBinaryString} ($x)") |
| 63 | + .mkString(", ")} |
| 64 | + $output >> |
| 65 | + $inst |
| 66 | + """.dedent |
| 67 | + |
| 68 | +object Computer: |
| 69 | + def parse(input: String): Computer = input match |
| 70 | + case s"""Register A: ${I(a)} |
| 71 | +Register B: ${I(b)} |
| 72 | +Register C: ${I(c)} |
| 73 | + |
| 74 | +Program: $xs""" => Computer(Regs(a, b, c), `2024`.day17.parse(xs)) |
| 75 | + |
| 76 | +def parse(input: String): Vector[(Code, Int)] = input |
| 77 | + .split(",") |
| 78 | + .map(_.toInt) |
| 79 | + .grouped(2) |
| 80 | + .collect { case Array(c, o) => (Code.parse(c), o) } |
| 81 | + .toVector |
| 82 | + |
| 83 | +@main def main() = |
| 84 | + val input = readInput(this).mkString.trim |
| 85 | + val c = Computer.parse(input) |
| 86 | + println(c.pretty) |
| 87 | + val res = c.run |
| 88 | + println(res.pretty) |
| 89 | + res.output.mkString(",") |> println |
0 commit comments