Skip to content

Commit 61bc001

Browse files
committed
Solution Day 17, part one
1 parent 07c38ab commit 61bc001

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/main/kotlin/Day17.kt

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import kotlin.math.pow
2+
import kotlin.properties.Delegates
3+
4+
fun main() {
5+
val day = "Day17"
6+
7+
println("$day part 1")
8+
9+
fun part1(input: List<String>): String {
10+
return ThreeBitComputer().runProgram(input)
11+
}
12+
13+
printAndCheck(
14+
"""
15+
Register A: 729
16+
Register B: 0
17+
Register C: 0
18+
19+
Program: 0,1,5,4,3,0
20+
""".trimIndent().lines(),
21+
::part1, "4,6,3,5,6,3,5,2,1,0"
22+
)
23+
24+
val input = readInput(day)
25+
printAndCheck(input, ::part1, "4,1,7,6,4,1,0,2,7")
26+
}
27+
28+
/**
29+
* - program = list of 3-bit numbers
30+
* - 3 registers: A, B, C of type Int
31+
* - 8 instructions: 3-bit opcode + 3-bit operand
32+
* - instruction pointer, increases by 2 after instruction processed if not jumped
33+
* - past the last opcode: program halts
34+
* - operands: literal or combo
35+
*
36+
* combo operands:
37+
* - 0-3: literal value 0-3
38+
* - 4, 5, 6: register A, B, C
39+
* - 7: reserved
40+
*
41+
* instructions:
42+
* opcode instruction function
43+
* 0 adv division of A and 2^<combo operand>, truncated result in A
44+
* 1 bxl bitwise XOR of B and literal operand, result in B
45+
* 2 bst <combo operand> modulo 8, lowest 3 bits of result in B
46+
* 3 jnz if A=0: nothing, else: jumps to <literal operand>
47+
* 4 bxc bitwise XOR of B and C, result in B, reads but ignores operand
48+
* 5 out <combo operand> modulo 8, then output result (multiple output values separated by ',')
49+
* 6 bdv division of A and 2^<combo operand>, truncated result in B
50+
* 7 cdv division of A and 2^<combo operand>, truncated result in C
51+
*/
52+
private class ThreeBitComputer {
53+
54+
private val instructionStep = 2
55+
56+
private lateinit var program: List<Int>
57+
private var registerA by Delegates.notNull<Int>()
58+
private var registerB by Delegates.notNull<Int>()
59+
private var registerC by Delegates.notNull<Int>()
60+
private var instructionPointer = 0
61+
62+
private val output = mutableListOf<Int>()
63+
64+
fun init(input: List<String>) {
65+
program = input.readProgram()
66+
registerA = input.readRegister('A')
67+
registerB = input.readRegister('B')
68+
registerC = input.readRegister('C')
69+
instructionPointer = 0
70+
output.clear()
71+
}
72+
73+
private val instructions: List<(Int) -> Unit> = listOf(
74+
{ op -> registerA = (registerA / 2.0.pow(combo(op))).toInt(); next() }, // 0 adv
75+
{ op -> registerB = (registerB xor op) ; next() }, // 1 bxl
76+
{ op -> registerB = (combo(op) % 8) and 7 ; next() }, // 2 bst; 7=111 -> take only lowest 3 bits
77+
{ op -> if (registerA == 0) next() else instructionPointer = op }, // 3 jnz
78+
{ _ -> registerB = (registerB xor registerC) ; next() }, // 4 bxc
79+
{ op -> output.add((combo(op) % 8) and 7) ; next() }, // 5 out
80+
{ op -> registerB = (registerA / 2.0.pow(combo(op))).toInt(); next() }, // 6 bdv
81+
{ op -> registerC = (registerA / 2.0.pow(combo(op))).toInt(); next() }, // 7 cdv
82+
)
83+
84+
fun runProgram(input: List<String>): String {
85+
init(input)
86+
while (instructionPointer < program.size) {
87+
val instruction = program[instructionPointer]
88+
val op = program[instructionPointer + 1]
89+
instructions[instruction].invoke(op)
90+
}
91+
return output.joinToString(",")
92+
}
93+
94+
private fun combo(op: Int) = when (op) {
95+
in 1..3 -> op
96+
4 -> registerA
97+
5 -> registerB
98+
6 -> registerC
99+
else -> error("unknown combo operand code '$op'")
100+
}
101+
102+
private fun next() {
103+
instructionPointer += instructionStep
104+
}
105+
106+
private fun List<String>.readRegister(register: Char) =
107+
first { it.startsWith("Register $register: ") }
108+
.substringAfter("Register $register: ")
109+
.toInt()
110+
111+
private fun List<String>.readProgram() =
112+
first { it.startsWith("Program: ") }
113+
.substringAfter("Program: ")
114+
.split(",")
115+
.map(String::toInt)
116+
}

src/main/kotlin/Utils.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ fun printAndCheck(input: List<String>, block: (List<String>) -> Long, expected:
2626
printAndCheck(measureTimedValue { block(input) }, expected)
2727
}
2828

29+
fun printAndCheck(input: List<String>, block: (List<String>) -> String, expected: String) {
30+
printAndCheck(measureTimedValue { block(input) }, expected)
31+
}
32+
2933
suspend fun printAndCheck(input: List<String>, block: KSuspendFunction1<List<String>, Int>, expected: Int) {
3034
printAndCheck(measureTimedValue { block(input).toLong() }, expected.toLong())
3135
}
@@ -38,3 +42,8 @@ private fun printAndCheck(result: TimedValue<Long>, expected: Long) {
3842
println("result: ${result.value}, expected: $expected, took: ${result.duration}")
3943
check(result.value == expected)
4044
}
45+
46+
private fun printAndCheck(result: TimedValue<String>, expected: String) {
47+
println("result: ${result.value}, expected: $expected, took: ${result.duration}")
48+
check(result.value == expected)
49+
}

0 commit comments

Comments
 (0)