|
| 1 | +package com.cluddles.aoc.y2024 |
| 2 | + |
| 3 | +import com.cluddles.aoc.core.Harness |
| 4 | +import com.cluddles.aoc.core.Solver |
| 5 | +import com.cluddles.aoc.core.SolverInput |
| 6 | +import kotlin.math.pow |
| 7 | + |
| 8 | +/** Chronospatial Computer */ |
| 9 | +object Day17: Solver<Day17.Input, String> { |
| 10 | + |
| 11 | + data class Input(val a: Long, val b: Long, val c: Long, val program: List<Int>) |
| 12 | + |
| 13 | + override fun prepareInput(src: SolverInput): Input { |
| 14 | + val reg = src.lines().take(3).map { it.split(": ")[1].toLong() }.toList() |
| 15 | + return Input( |
| 16 | + reg[0], |
| 17 | + reg[1], |
| 18 | + reg[2], |
| 19 | + src.lines() |
| 20 | + .dropWhile { !it.contains("Program") } |
| 21 | + .first() |
| 22 | + .split(": ")[1] |
| 23 | + .split(",") |
| 24 | + .map { it.toInt() } |
| 25 | + ) |
| 26 | + } |
| 27 | + |
| 28 | + data class State(var a: Long, var b: Long, var c: Long, var ip: Int, val out: MutableList<Int>) |
| 29 | + |
| 30 | + enum class Instruction { ADV, BXL, BST, JNZ, BXC, OUT, BDV, CDV } |
| 31 | + |
| 32 | + fun apply(state: State, i: Instruction, operand: Int) { |
| 33 | + var skipIpUpdate = false |
| 34 | + when (i) { |
| 35 | + Instruction.ADV -> state.a = state.a / (2.0).pow(combo(state, operand).toDouble()).toLong() |
| 36 | + Instruction.BXL -> state.b = state.b xor operand.toLong() |
| 37 | + Instruction.BST -> state.b = combo(state, operand) % 8 |
| 38 | + Instruction.JNZ -> if (state.a != 0L) { state.ip = operand; skipIpUpdate = true } |
| 39 | + Instruction.BXC -> state.b = state.b xor state.c |
| 40 | + Instruction.OUT -> state.out += (combo(state, operand) % 8).toInt() |
| 41 | + Instruction.BDV -> state.b = state.a / (2.0).pow(combo(state, operand).toDouble()).toLong() |
| 42 | + Instruction.CDV -> state.c = state.a / (2.0).pow(combo(state, operand).toDouble()).toLong() |
| 43 | + } |
| 44 | + if (!skipIpUpdate) state.ip += 2 |
| 45 | + } |
| 46 | + |
| 47 | + private fun combo(state: State, v: Int): Long { |
| 48 | + return when(v) { |
| 49 | + 0, 1, 2, 3 -> v.toLong() |
| 50 | + 4 -> state.a |
| 51 | + 5 -> state.b |
| 52 | + 6 -> state.c |
| 53 | + else -> error("Unsupported $v") |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + fun run(input: Input): State { |
| 58 | + val state = State(input.a, input.b, input.c, 0, mutableListOf()) |
| 59 | + while (state.ip < input.program.size) { |
| 60 | + apply(state, Instruction.entries[input.program[state.ip]], input.program[state.ip + 1]) |
| 61 | + } |
| 62 | + return state |
| 63 | + } |
| 64 | + |
| 65 | + override fun solvePart1(input: Input): String { |
| 66 | + return run(input).out.joinToString(",") |
| 67 | + } |
| 68 | + |
| 69 | + override fun solvePart2(input: Input): String { |
| 70 | + // For my input (And possibly others? It works on the test input too) |
| 71 | + // Each iteration around the program reduces the "a" register by dividing it by 8 |
| 72 | + // So we can try to find a match for the last digit, then multiply by 8 and try for the next digit, and so on |
| 73 | + // Doing this with string.endsWith is probably not optimal, but it is concise... |
| 74 | + var a = 0L |
| 75 | + val inputProgStr = input.program.joinToString() |
| 76 | + while (true) { |
| 77 | + val stateOutStr = run(Input(a, input.b, input.c, input.program)).out.joinToString() |
| 78 | + if (stateOutStr == inputProgStr) { |
| 79 | + return a.toString() |
| 80 | + } else if (inputProgStr.endsWith(stateOutStr)) { |
| 81 | + // 0*8 = 0... try not to get stuck |
| 82 | + a = maxOf(a * 8, 1) |
| 83 | + } else { |
| 84 | + a++ |
| 85 | + } |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | +} |
| 90 | + |
| 91 | +fun main() { |
| 92 | + Harness.run(Day17) |
| 93 | +} |
0 commit comments