Skip to content

Commit c626e0b

Browse files
committed
Day 17
Part 1 is fine. Part 2 isn't exactly clever, but it works well enough.
1 parent 63a3bbb commit c626e0b

File tree

6 files changed

+152
-1
lines changed

6 files changed

+152
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ build/
33
!gradle/wrapper/gradle-wrapper.jar
44
!**/src/main/**/build/
55
!**/src/test/**/build/
6+
*.hprof
67

78
### IntelliJ IDEA ###
89
.idea

aoc-secret

src/main/kotlin/com/cluddles/aoc/y2024/All2024.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fun main() {
2222
Day14,
2323
Day15,
2424
Day16,
25+
Day17,
2526
)
2627

2728
measureTime {
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.cluddles.aoc.y2024
2+
3+
import com.cluddles.aoc.core.SolverInput
4+
import org.assertj.core.api.Assertions.assertThat
5+
import org.junit.jupiter.api.Test
6+
7+
class Day17Test {
8+
9+
val solver = Day17
10+
11+
val input = solver.prepareInput(SolverInput.fromPath(solver.examplePath))
12+
13+
private fun testInput(program: String, a: Int = 0, b: Int = 0, c: Int = 0): Day17.Input {
14+
return Day17.Input(a.toLong(), b.toLong(), c.toLong(), program.split(",").map { it.toInt() })
15+
}
16+
17+
@Test fun instructionOp_ex1() {
18+
assertThat(solver.run(testInput("2,6", c = 9)).b).isEqualTo(1)
19+
}
20+
21+
@Test fun instructionOp_ex2() {
22+
assertThat(solver.run(testInput("5,0,5,1,5,4", a = 10)).out).isEqualTo(listOf(0,1,2))
23+
}
24+
25+
@Test fun instructionOp_ex3() {
26+
val state = solver.run(testInput("0,1,5,4,3,0", a = 2024))
27+
assertThat(state.out).isEqualTo(listOf(4,2,5,6,7,7,7,7,3,1,0))
28+
assertThat(state.a).isEqualTo(0)
29+
}
30+
31+
@Test fun instructionOp_ex4() {
32+
assertThat(solver.run(testInput("1,7", b = 29)).b).isEqualTo(26)
33+
}
34+
35+
@Test fun instructionOp_ex5() {
36+
assertThat(solver.run(testInput("4,0", b = 2024, c = 43690)).b).isEqualTo(44354)
37+
}
38+
39+
@Test fun part1() {
40+
assertThat(solver.solvePart1(input)).isEqualTo("4,6,3,5,6,3,5,2,1,0")
41+
}
42+
43+
@Test fun part2_check() {
44+
assertThat(solver.run(testInput("0,3,5,4,3,0", a = 117440)).out).isEqualTo(listOf(0,3,5,4,3,0))
45+
}
46+
47+
@Test fun part2() {
48+
assertThat(solver.solvePart2(testInput("0,3,5,4,3,0"))).isEqualTo("117440")
49+
}
50+
51+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Register A: 729
2+
Register B: 0
3+
Register C: 0
4+
5+
Program: 0,1,5,4,3,0

0 commit comments

Comments
 (0)