Skip to content

Commit 8f0cdb5

Browse files
committed
Solution 2015-23 (Opening the Turing Lock)
1 parent 1e19ddf commit 8f0cdb5

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package de.ronny_h.aoc.year2015.day23
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.year2015.day23.Instruction.Companion.instructionOf
5+
import de.ronny_h.aoc.year2015.day23.Register.A
6+
import de.ronny_h.aoc.year2015.day23.Register.B
7+
8+
fun main() = OpeningTheTuringLock().run(184, 231)
9+
10+
class OpeningTheTuringLock : AdventOfCode<Int>(2015, 23) {
11+
override fun part1(input: List<String>): Int = Computer(input.parseProgram()).run()
12+
override fun part2(input: List<String>): Int = Computer(input.parseProgram(), 1u).run()
13+
}
14+
15+
class Computer(val program: List<Instruction>, initA: UInt = 0u) {
16+
private var instructionPointer = 0
17+
private val registers = mutableMapOf(A to initA, B to 0u)
18+
19+
fun run(): Int {
20+
while (instructionPointer in program.indices) {
21+
instructionPointer += program[instructionPointer].executeWith(registers)
22+
}
23+
return registers.getValue(B).toInt()
24+
}
25+
}
26+
27+
fun List<String>.parseProgram(): List<Instruction> = map {
28+
val split = it.split(' ', limit = 2)
29+
instructionOf(split.first(), split[1])
30+
}
31+
32+
enum class Register {
33+
A, B
34+
}
35+
36+
sealed interface Instruction {
37+
/**
38+
* Executes this instruction on the given registers (by modifying their values).
39+
*
40+
* @return The instruction pointer offset.
41+
*/
42+
fun executeWith(registers: MutableMap<Register, UInt>): Int
43+
44+
data class Half(val register: Register) : Instruction {
45+
constructor(parameter: String) : this(parseRegister(parameter))
46+
47+
override fun executeWith(registers: MutableMap<Register, UInt>): Int {
48+
registers[register] = registers.getValue(register) / 2u
49+
return 1
50+
}
51+
}
52+
53+
data class Triple(val register: Register) : Instruction {
54+
constructor(parameter: String) : this(parseRegister(parameter))
55+
56+
override fun executeWith(registers: MutableMap<Register, UInt>): Int {
57+
registers[register] = registers.getValue(register) * 3u
58+
return 1
59+
}
60+
}
61+
62+
data class Increment(val register: Register) : Instruction {
63+
constructor(parameter: String) : this(parseRegister(parameter))
64+
65+
override fun executeWith(registers: MutableMap<Register, UInt>): Int {
66+
registers[register] = registers.getValue(register) + 1u
67+
return 1
68+
}
69+
}
70+
71+
data class Jump(val offset: Int) : Instruction {
72+
constructor(parameter: String) : this(parseOffset(parameter))
73+
74+
override fun executeWith(registers: MutableMap<Register, UInt>): Int = offset
75+
}
76+
77+
data class JumpIfEven(val register: Register, val offset: Int) : Instruction {
78+
constructor(parameter: String) : this(
79+
parseRegister(parameter.split(", ").first()),
80+
parseOffset(parameter.split(", ").last()),
81+
)
82+
83+
override fun executeWith(registers: MutableMap<Register, UInt>): Int =
84+
if (registers.getValue(register) % 2u == 0u) {
85+
offset
86+
} else {
87+
1
88+
}
89+
}
90+
91+
data class JumpIfOne(val register: Register, val offset: Int) : Instruction {
92+
constructor(parameter: String) : this(
93+
parseRegister(parameter.split(", ").first()),
94+
parseOffset(parameter.split(", ").last()),
95+
)
96+
97+
override fun executeWith(registers: MutableMap<Register, UInt>): Int =
98+
if (registers.getValue(register) == 1u) {
99+
offset
100+
} else {
101+
1
102+
}
103+
}
104+
105+
companion object {
106+
fun instructionOf(keyword: String, parameters: String) = when (keyword) {
107+
"hlf" -> Half(parameters)
108+
"tpl" -> Triple(parameters)
109+
"inc" -> Increment(parameters)
110+
"jmp" -> Jump(parameters)
111+
"jie" -> JumpIfEven(parameters)
112+
"jio" -> JumpIfOne(parameters)
113+
else -> error("unknown keyword: $keyword")
114+
}
115+
116+
private fun parseRegister(parameter: String): Register {
117+
require(!parameter.contains(" "))
118+
require(!parameter.contains(","))
119+
return when (parameter) {
120+
"a" -> A
121+
"b" -> B
122+
else -> error("unknown parameter: $parameter")
123+
}
124+
}
125+
126+
private fun parseOffset(parameter: String): Int {
127+
require(!parameter.contains(" "))
128+
require(!parameter.contains(","))
129+
val sign = when (parameter.first()) {
130+
'+' -> 1
131+
'-' -> -1
132+
else -> error("+ or - expected, but got: $parameter")
133+
}
134+
return sign * parameter.substring(1).toInt()
135+
}
136+
}
137+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package de.ronny_h.aoc.year2015.day23
2+
3+
import de.ronny_h.aoc.extensions.asList
4+
import de.ronny_h.aoc.year2015.day23.Instruction.*
5+
import de.ronny_h.aoc.year2015.day23.Instruction.Companion.instructionOf
6+
import de.ronny_h.aoc.year2015.day23.Register.A
7+
import de.ronny_h.aoc.year2015.day23.Register.B
8+
import io.kotest.core.spec.style.StringSpec
9+
import io.kotest.data.forAll
10+
import io.kotest.data.row
11+
import io.kotest.matchers.shouldBe
12+
13+
class OpeningTheTuringLockTest : StringSpec({
14+
15+
"input can be parsed" {
16+
"""
17+
hlf a
18+
tpl b
19+
inc a
20+
jmp +2
21+
jie a, -2
22+
jio b, +2
23+
""".asList().parseProgram() shouldBe listOf(
24+
Half(A),
25+
Triple(B),
26+
Increment(A),
27+
Jump(2),
28+
JumpIfEven(A, -2),
29+
JumpIfOne(B, 2),
30+
)
31+
}
32+
33+
"hlf sets register to half its current value" {
34+
val registers = mutableMapOf(A to 4u)
35+
val offset = instructionOf("hlf", "a").executeWith(registers)
36+
37+
registers.getValue(A) shouldBe 2u
38+
offset shouldBe 1
39+
}
40+
41+
"tpl sets register to triple its current value" {
42+
val registers = mutableMapOf(A to 4u)
43+
val offset = instructionOf("tpl", "a").executeWith(registers)
44+
45+
registers.getValue(A) shouldBe 12u
46+
offset shouldBe 1
47+
}
48+
49+
"inc increments the register by 1" {
50+
val registers = mutableMapOf(A to 4u)
51+
val offset = instructionOf("inc", "a").executeWith(registers)
52+
53+
registers.getValue(A) shouldBe 5u
54+
offset shouldBe 1
55+
}
56+
57+
"jmp jumps by the given offset" {
58+
val registers = mutableMapOf(A to 4u)
59+
val offset = instructionOf("jmp", "-7").executeWith(registers)
60+
61+
registers.getValue(A) shouldBe 4u
62+
offset shouldBe -7
63+
}
64+
65+
"jie jumps only if the register is even" {
66+
forAll(
67+
row(mutableMapOf(A to 4u), -7, -7),
68+
row(mutableMapOf(A to 3u), -7, 1),
69+
) { registers, offset, expectedJump ->
70+
val oldA = registers.getValue(A)
71+
val jump = instructionOf("jie", "a, $offset").executeWith(registers)
72+
73+
registers.getValue(A) shouldBe oldA
74+
jump shouldBe expectedJump
75+
}
76+
}
77+
78+
"jio jumps only if the register is one" {
79+
forAll(
80+
row(mutableMapOf(A to 4u), -7, 1),
81+
row(mutableMapOf(A to 3u), -7, 1),
82+
row(mutableMapOf(A to 1u), -7, -7),
83+
) { registers, offset, expectedJump ->
84+
val oldA = registers.getValue(A)
85+
val jump = instructionOf("jio", "a, $offset").executeWith(registers)
86+
87+
registers.getValue(A) shouldBe oldA
88+
jump shouldBe expectedJump
89+
}
90+
}
91+
92+
"part 1: the example program sets b to 2" {
93+
val input = """
94+
inc b
95+
jio b, +2
96+
tpl b
97+
inc b
98+
""".asList()
99+
OpeningTheTuringLock().part1(input) shouldBe 2
100+
}
101+
102+
"part 2: the example program sets b to 1 when a is initialized with 1" {
103+
val input = """
104+
inc a
105+
jio a, +2
106+
inc b
107+
inc a
108+
""".asList()
109+
OpeningTheTuringLock().part2(input) shouldBe 1
110+
}
111+
})

0 commit comments

Comments
 (0)