generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2015D23.kt
99 lines (81 loc) · 4.07 KB
/
Y2015D23.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package aockt.y2015
import io.github.jadarma.aockt.core.Solution
object Y2015D23 : Solution {
/** Parses the input and returns the list of instructions. */
private fun parseInput(input: String): List<Instruction> =
input.lineSequence().map(Instruction.Companion::parse).toList()
/** A mutable state object that holds the registry memory of the computer. */
private class State(initialRegisters: Map<String, UInt> = emptyMap()) {
private val registers: MutableMap<String, UInt> = initialRegisters.toMutableMap()
// Convenience operators to add null-safety to indexing operations.
operator fun get(register: String) = registers.getOrPut(register) { 0.toUInt() }
operator fun set(register: String, value: UInt) = registers.put(register, value)
/**
* Convenience property for working with the instruction pointer as a signed integer, as is often the case
* when applying offsets. The actual unsigned version is accessible from `state["ip"]` as well.
*/
var ip: Int
get() = this["ip"].toInt()
set(value) {
this["ip"] = value.toUInt()
}
override fun toString() = registers.toString()
}
/** An instruction executable by the computer. */
private sealed class Instruction {
data class Half(val register: String) : Instruction()
data class Triple(val register: String) : Instruction()
data class Increment(val register: String) : Instruction()
data class Jump(val offset: Int) : Instruction()
data class JumpIfEven(val register: String, val offset: Int) : Instruction()
data class JumpIfOne(val register: String, val offset: Int) : Instruction()
/** Applies the logic of this instruction on the [state], mutating it. */
fun execute(state: State) {
when (this) {
is Half -> {
state[this.register] /= 2.toUInt()
state.ip++
}
is Triple -> {
state[this.register] *= 3.toUInt()
state.ip++
}
is Increment -> {
state[this.register]++
state.ip++
}
is Jump -> state.ip += offset
is JumpIfEven -> state.ip += if (state[this.register] % 2.toUInt() == 0.toUInt()) offset else 1
is JumpIfOne -> state.ip += if (state[this.register] == 1.toUInt()) offset else 1
}
}
companion object {
/** All valid instructions follow this syntax. */
private val syntax = Regex("""([a-z]+) ([+\-]\d+|[a-z]+)(?:, ([+\-]\d+|[a-z]+))*$""")
/** Parses the [input] into an [Instruction], or throws an [IllegalArgumentException] if it is invalid. */
fun parse(input: String): Instruction = runCatching {
val tokens = syntax.matchEntire(input)!!.groupValues.drop(1)
when (tokens[0]) {
"hlf" -> Half(tokens[1])
"tpl" -> Triple(tokens[1])
"inc" -> Increment(tokens[1])
"jmp" -> Jump(tokens[1].toInt())
"jie" -> JumpIfEven(tokens[1], tokens[2].toInt())
"jio" -> JumpIfOne(tokens[1], tokens[2].toInt())
else -> throw Exception("Invalid opcode.")
}
}.getOrElse { throw IllegalArgumentException("Invalid syntax.") }
}
}
/**
* Runs the program defined by the given [instructions] and returns the computer's [State] upon finishing
* execution. Note the [State] reference is the same, just mutated.
*/
private fun State.runProgram(instructions: List<Instruction>): State = apply {
while (ip in instructions.indices) {
instructions[ip].execute(this)
}
}
override fun partOne(input: String) = State().runProgram(parseInput(input))["b"]
override fun partTwo(input: String) = State(mapOf("a" to 1.toUInt())).runProgram(parseInput(input))["b"]
}