-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay8.kt
115 lines (94 loc) · 3.85 KB
/
Day8.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package day8
import java.io.File
fun main() {
part2()
}
fun part1() {
val program = Program(getInputAsMapOfInstructions())
program.run()
println(program.accumulator)
}
fun part2() {
val haltingInstructionHook: (Instruction) -> Boolean = { ins: Instruction ->
!ins.isChecked && ins.operation in listOf("jmp", "nop")
}
val instructionConverter: (Instruction) -> Instruction = {
it.copy(operation = if(it.operation == "jmp") "nop" else "jmp")
}
var program = Program(getInputAsMapOfInstructions()).apply { run(haltingInstructionHook) }
while(program.state != ProgramState.COMPLETED) {
program = evalWithAlteredInstruction(program, instructionConverter)
?:program.apply {
markCurrentInstructionAsChecked()
run(haltingInstructionHook)
}
}
println(program.accumulator)
}
fun evalWithAlteredInstruction(program: Program, instructionConverter: (Instruction) -> Instruction): Program? =
program.deepCopy().apply {
replaceInstruction(program.currentLine, instructionConverter)
run()
}.takeIf { it.state == ProgramState.COMPLETED}
fun getInputAsMapOfInstructions() =
File("src/main/resources/day8_input.txt").readLines()
.mapIndexed { idx, line ->
idx to Instruction(line.substringBefore(" "), line.substringAfter(" ").toInt())
}.toMap().toMutableMap()
data class Instruction(val operation: String,
val argument: Int,
var isInvoked: Boolean = false,
var isChecked: Boolean = false)
enum class ProgramState { COMPLETED, INFINITE_LOOP, HALTED }
class Program(private val instructions: MutableMap<Int, Instruction>,
var state: ProgramState = ProgramState.HALTED,
var currentLine: Int = 0,
var accumulator: Int = 0) {
fun run(haltingInstructionHook: (Instruction) -> Boolean = { false }) {
while(currentLine < instructions.size) {
val ins = instructions[currentLine] ?: error("Invalid instruction at line $currentLine")
if(ins.isInvoked) {
state = ProgramState.INFINITE_LOOP
break
}
if(haltingInstructionHook.invoke(ins)) {
state = ProgramState.HALTED
break
}
invokeInstruction(ins)
}
if(currentLine == instructions.size) state = ProgramState.COMPLETED
}
fun deepCopy(): Program {
val newInstructions = instructions.map { it.key to it.value.copy() }.toMap().toMutableMap()
return Program(newInstructions, this.state, this.currentLine, this.accumulator)
}
fun replaceInstruction(replaceLine: Int, instructionConverter: (Instruction) -> Instruction) {
val instructionToUpdate = instructions[replaceLine]
if(instructionToUpdate != null) {
instructions[replaceLine] = instructionConverter.invoke(instructionToUpdate)
}
}
fun markCurrentInstructionAsChecked() {
instructions[currentLine]?.isChecked = true
}
private fun invokeInstruction(instruction: Instruction?): Instruction? {
if(instruction == null) error("null instruction encountered")
instruction.isInvoked = true
return when(instruction.operation) {
"nop" -> next()
"acc" -> { acc(instruction.argument); next() }
"jmp" -> jmp(instruction.argument)
else -> error("invalid instruction: $instruction")
}
}
private fun next(): Instruction? {
currentLine += 1
return instructions[currentLine]
}
private fun acc(amount: Int) { accumulator += amount }
private fun jmp(amount: Int): Instruction? {
currentLine += amount
return instructions[currentLine]
}
}