Day 9, also refactored the IntCode (or LongCode?) computer to a separate class and added the missing features

This commit is contained in:
2019-12-09 22:22:50 +01:00
parent 46e7df3308
commit c80252a682
5 changed files with 261 additions and 191 deletions

View File

@@ -0,0 +1,196 @@
package com.basdado.adventofcode
val FULL_OPCODES = mapOf(
Pair(1L, OpCode(1L, 3, ::add)),
Pair(2L, OpCode(2L, 3, ::multiply)),
Pair(3L, OpCode(3L, 1, ::saveInput)),
Pair(4L, OpCode(4L, 1, ::writeOutput)),
Pair(5L, OpCode(5L, 2, ::jumpIfTrue)),
Pair(6L, OpCode(6L, 2, ::jumpIfFalse)),
Pair(7L, OpCode(7L, 3, ::lessThan)),
Pair(8L, OpCode(8L, 3, ::equals)),
Pair(9L, OpCode(9L, 1, ::adjustRelativeBase))
)
fun loadProgram(inputPath: String, opCodes: Map<Long, OpCode>, input: LongArray): IntCodeProgram {
return IntCodeProgram(loadProgramData(inputPath), input, opCodes)
}
fun loadProgramData(inputPath: String): LongArray {
return line(inputPath).split(",").map { it.toLong() }.toLongArray()
}
class Instruction(
val paramModes: Array<ParamMode>,
val opcode: OpCode,
val pos: Int
) {
companion object {
fun parse(input: Long, pointer: Int, opcodes: Map<Long, OpCode>): Instruction {
val opcode = opcodes[input % 100] ?: throw IllegalArgumentException("Unknown opcode for operation $input")
val paramModes = Array<ParamMode>(opcode.paramCount) { ParamMode.POSITION }
for (i in 0 until opcode.paramCount) {
val paramMode = (input % pow10(i + 3)) / pow10(i + 2)
paramModes[i] = when (paramMode) {
0L -> ParamMode.POSITION
1L -> ParamMode.IMMEDIATE
2L -> ParamMode.RELATIVE
else -> throw IllegalStateException("Unknown parameter mode: $paramMode")
}
}
return Instruction(paramModes, opcode, pointer)
}
}
}
enum class ParamMode {
POSITION, IMMEDIATE, RELATIVE
}
class IntCodeProgram(val data: LongArray, var inputs: LongArray, val opCodes: Map<Long, OpCode> = FULL_OPCODES,
var pointer: Int = 0) {
private var inputPointer: Int = 0
var relativeBase: Long = 0
private val outputs = mutableListOf<Long>()
var done = false
fun getValue(instruction: Instruction, i: Int): Long {
val param = data[instruction.pos + i + 1]
return when(instruction.paramModes[i]) {
ParamMode.IMMEDIATE -> param
ParamMode.POSITION -> data[param.toInt()]
ParamMode.RELATIVE -> data[(relativeBase + param).toInt()]
}
}
fun setValue(instruction: Instruction, i: Int, x: Long) {
// Setters don't care about parameter modes, they are always "position" based
when(instruction.paramModes[i]) {
ParamMode.POSITION -> data[(data[instruction.pos + i + 1]).toInt()] = x
ParamMode.RELATIVE -> data[(relativeBase + data[instruction.pos + i + 1]).toInt()] = x
else -> {
println("Calling setter instruction to a parameter in ${instruction.paramModes[i]} mode?")
}
}
}
fun getNextInput(): Long {
return inputs[inputPointer++]
}
fun pushInput(input: Long) {
inputs += input
}
fun getOutputs(): List<Long> {
return outputs
}
fun execute() {
while (data[pointer] != 99L) {
executeUntilOutput()
}
done = true
}
fun executeUntilOutput(): Long? {
while (data[pointer] != 99L) {
val instruction = Instruction.parse(data[pointer], pointer, opCodes)
val operationResult = instruction.opcode.operation.invoke(instruction, this)
if (operationResult.moveToNextInstruction) {
pointer += instruction.opcode.paramCount + 1
}
if (operationResult.output != null) {
outputs.add(operationResult.output)
return operationResult.output
}
}
done = true
return null
}
}
fun pow10(n: Int): Int {
return when (n) {
0 -> 1
1 -> 10
2 -> 100
3 -> 1000
4 -> 10000
5 -> 100000
6 -> 1000000
7 -> 10000000
8 -> 100000000
9 -> 1000000000
else -> throw IllegalArgumentException("Int overflowing with pow10($n)")
}
}
fun add(instruction: Instruction, program: IntCodeProgram): OperationResult {
program.setValue(instruction, 2, program.getValue(instruction, 0) + program.getValue(instruction, 1))
return VOID
}
fun multiply(instruction: Instruction, program: IntCodeProgram): OperationResult {
program.setValue(instruction, 2, program.getValue(instruction, 0) * program.getValue(instruction, 1))
return VOID
}
fun saveInput(instruction: Instruction, program: IntCodeProgram): OperationResult {
program.setValue(instruction, 0, program.getNextInput())
return VOID
}
fun writeOutput(instruction: Instruction, program: IntCodeProgram): OperationResult {
return OperationResult(output = program.getValue(instruction, 0))
}
fun jumpIfTrue(instruction: Instruction, program: IntCodeProgram): OperationResult {
return if (program.getValue(instruction, 0) != 0L) {
program.pointer = program.getValue(instruction, 1).toInt()
OperationResult(moveToNextInstruction = false)
} else {
return VOID
}
}
fun jumpIfFalse(instruction: Instruction, program: IntCodeProgram): OperationResult {
return if (program.getValue(instruction, 0) == 0L) {
program.pointer = program.getValue(instruction, 1).toInt()
OperationResult(moveToNextInstruction = false)
} else {
VOID
}
}
fun lessThan(instruction: Instruction, program: IntCodeProgram): OperationResult {
program.setValue(instruction, 2,
if (program.getValue(instruction, 0) < program.getValue(instruction, 1)) 1 else 0)
return VOID
}
fun equals(instruction: Instruction, program: IntCodeProgram): OperationResult {
program.setValue(instruction, 2,
if (program.getValue(instruction, 0) == program.getValue(instruction, 1)) 1 else 0)
return VOID
}
fun adjustRelativeBase(instruction: Instruction, program: IntCodeProgram): OperationResult {
program.relativeBase += program.getValue(instruction, 0)
return VOID
}
data class OpCode(val code: Long, val paramCount: Int, val operation: (instruction: Instruction, intCodeProgram: IntCodeProgram) -> OperationResult)
data class OperationResult(val moveToNextInstruction: Boolean = true, val output: Long? = null)
private val VOID = OperationResult()

View File

@@ -1,7 +1,6 @@
package com.basdado.adventofcode.day5
import com.basdado.adventofcode.line
import java.util.*
import com.basdado.adventofcode.*
const val DAY5_INPUT_PATH = "/day/5/input.txt"
@@ -16,22 +15,22 @@ fun main() {
object Day5 {
private val puzzle1OpCodes = mapOf(
Pair(1, OpCode(1, 3, ::add)),
Pair(2, OpCode(2, 3, ::multiply)),
Pair(3, OpCode(3, 1, ::saveInput)),
Pair(4, OpCode(4, 1, ::writeOutput))
Pair(1L, OpCode(1L, 3, ::add)),
Pair(2L, OpCode(2L, 3, ::multiply)),
Pair(3L, OpCode(3L, 1, ::saveInput)),
Pair(4L, OpCode(4L, 1, ::writeOutput))
)
val puzzle2OpCodes = puzzle1OpCodes + mapOf(
Pair(5, OpCode(5, 2, ::jumpIfTrue)),
Pair(6, OpCode(6, 2, ::jumpIfFalse)),
Pair(7, OpCode(7, 3, ::lessThan)),
Pair(8, OpCode(8, 3, ::equals))
Pair(5L, OpCode(5, 2, ::jumpIfTrue)),
Pair(6L, OpCode(6, 2, ::jumpIfFalse)),
Pair(7L, OpCode(7, 3, ::lessThan)),
Pair(8L, OpCode(8, 3, ::equals))
)
fun puzzle1() {
val program = loadProgram(DAY5_INPUT_PATH, puzzle1OpCodes, intArrayOf(1))
val program = loadProgram(DAY5_INPUT_PATH, puzzle1OpCodes, longArrayOf(1))
program.execute()
@@ -40,168 +39,10 @@ object Day5 {
fun puzzle2() {
val program = loadProgram(DAY5_INPUT_PATH, puzzle2OpCodes, intArrayOf(5))
val program = loadProgram(DAY5_INPUT_PATH, puzzle2OpCodes, longArrayOf(5))
program.execute()
println(program.getOutputs().last())
}
fun loadProgram(inputPath: String, opCodes: Map<Int, OpCode>, input: IntArray): Program {
return Program(loadProgramData(inputPath), opCodes, input)
}
fun loadProgramData(inputPath: String): IntArray {
return line(inputPath).split(",").map { it.toInt() }.toIntArray()
}
class Instruction(
val paramModeImmediate: BooleanArray,
val opcode: OpCode,
val pos: Int
) {
companion object {
fun parse(input: Int, pointer: Int, opcodes: Map<Int, OpCode>): Instruction {
val opcode = opcodes[input % 100] ?: throw IllegalArgumentException("Unknown opcode for operation $input")
val paramModes = BooleanArray(opcode.paramCount)
for (i in 0 until opcode.paramCount) {
paramModes[i] = (input % pow10(i + 3)) / pow10(i + 2) == 1
}
return Instruction(paramModes, opcode, pointer)
}
}
}
class Program(val data: IntArray, val opCodes: Map<Int, OpCode>, var inputs: IntArray,
var pointer: Int = 0) {
private var inputPointer: Int = 0
private val outputs = mutableListOf<Int>()
var done = false
fun getValue(instruction: Instruction, i: Int): Int {
return if (instruction.paramModeImmediate[i]) {
data[instruction.pos + i + 1]
} else {
data[data[instruction.pos + i + 1]]
}
}
fun setValue(instruction: Instruction, i: Int, x: Int) {
// Setters don't care about parameter modes, they are always "position" based
data[data[instruction.pos + i + 1]] = x
}
fun getNextInput(): Int {
return inputs[inputPointer++]
}
fun pushInput(input: Int) {
inputs += input
}
fun getOutputs(): List<Int> {
return outputs
}
fun execute() {
while (data[pointer] != 99) {
val output = executeUntilOutput()
}
done = true
}
fun executeUntilOutput(): Int? {
while (data[pointer] != 99) {
val instruction = Instruction.parse(data[pointer], pointer, opCodes)
val operationResult = instruction.opcode.operation.invoke(instruction, this)
if (operationResult.moveToNextInstruction) {
pointer += instruction.opcode.paramCount + 1
}
if (operationResult.output != null) {
outputs.add(operationResult.output)
return operationResult.output
}
}
done = true
return null
}
}
fun pow10(n: Int): Int {
return when (n) {
0 -> 1
1 -> 10
2 -> 100
3 -> 1000
4 -> 10000
5 -> 100000
6 -> 1000000
7 -> 10000000
8 -> 100000000
9 -> 1000000000
else -> throw IllegalArgumentException("Int overflowing with pow10($n)")
}
}
private fun add(instruction: Instruction, program: Program): OperationResult {
program.setValue(instruction, 2, program.getValue(instruction, 0) + program.getValue(instruction, 1))
return VOID
}
private fun multiply(instruction: Instruction, program: Program): OperationResult {
program.setValue(instruction, 2, program.getValue(instruction, 0) * program.getValue(instruction, 1))
return VOID
}
private fun saveInput(instruction: Instruction, program: Program): OperationResult {
program.setValue(instruction, 0, program.getNextInput())
return VOID
}
private fun writeOutput(instruction: Instruction, program: Program): OperationResult {
return OperationResult(output = program.getValue(instruction, 0))
}
private fun jumpIfTrue(instruction: Instruction, program: Program): OperationResult {
return if (program.getValue(instruction, 0) != 0) {
program.pointer = program.getValue(instruction, 1)
OperationResult(moveToNextInstruction = false)
} else {
return VOID
}
}
private fun jumpIfFalse(instruction: Instruction, program: Program): OperationResult {
return if (program.getValue(instruction, 0) == 0) {
program.pointer = program.getValue(instruction, 1)
OperationResult(moveToNextInstruction = false)
} else {
VOID
}
}
private fun lessThan(instruction: Instruction, program: Program): OperationResult {
program.setValue(instruction, 2,
if (program.getValue(instruction, 0) < program.getValue(instruction, 1)) 1 else 0)
return VOID
}
private fun equals(instruction: Instruction, program: Program): OperationResult {
program.setValue(instruction, 2,
if (program.getValue(instruction, 0) == program.getValue(instruction, 1)) 1 else 0)
return VOID
}
data class OpCode(val code: Int, val paramCount: Int, val operation: (instruction: Instruction, program: Program) -> OperationResult)
data class OperationResult(val moveToNextInstruction: Boolean = true, val output: Int? = null)
private val VOID = OperationResult()
}

View File

@@ -1,6 +1,7 @@
package com.basdado.adventofcode.day7
import com.basdado.adventofcode.day5.Day5
import com.basdado.adventofcode.IntCodeProgram
import com.basdado.adventofcode.loadProgramData
const val DAY7_INPUT_PATH = "/day/7/input.txt"
@@ -13,45 +14,45 @@ object Day7 {
fun puzzle1() {
val programData = Day5.loadProgramData(DAY7_INPUT_PATH)
var maxOutput = Int.MIN_VALUE
val programData = loadProgramData(DAY7_INPUT_PATH)
var maxOutput = Long.MIN_VALUE
var maxOutputPhases = ""
for (A in 0..4) {
for (A in 0L..4) {
val programA = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(A, 0))
val programA = IntCodeProgram(programData.clone(), longArrayOf(A, 0))
programA.execute()
val outputA = programA.getOutputs().last()
for (B in 0..4) {
for (B in 0L..4) {
if (B == A) continue
val programB = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(B, outputA))
val programB = IntCodeProgram(programData.clone(), longArrayOf(B, outputA))
programB.execute()
val outputB = programB.getOutputs().last()
for (C in 0..4) {
for (C in 0L..4) {
if (C == A || C == B) continue
val programC = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(C, outputB))
val programC = IntCodeProgram(programData.clone(), longArrayOf(C, outputB))
programC.execute()
val outputC = programC.getOutputs().last()
for (D in 0..4) {
for (D in 0L..4) {
if (D == A || D == B || D == C) continue
val programD = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(D, outputC))
val programD = IntCodeProgram(programData.clone(), longArrayOf(D, outputC))
programD.execute()
val outputD = programD.getOutputs().last()
for (E in 0..4) {
for (E in 0L..4) {
if (E == A || E == B || E == C || E == D) continue
val programE = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(E, outputD))
val programE = IntCodeProgram(programData.clone(), longArrayOf(E, outputD))
programE.execute()
val outputE = programE.getOutputs().last()
// Beautiful
@@ -93,18 +94,18 @@ object Day7 {
return perms
}
fun executeFeedbackLoop(phaseSettings: IntArray): Int? {
fun executeFeedbackLoop(phaseSettings: IntArray): Long? {
val programData = Day5.loadProgramData(DAY7_INPUT_PATH)
val programData = loadProgramData(DAY7_INPUT_PATH)
val ampA = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(phaseSettings[0]))
val ampB = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(phaseSettings[1]))
val ampC = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(phaseSettings[2]))
val ampD = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(phaseSettings[3]))
val ampE = Day5.Program(programData.clone(), Day5.puzzle2OpCodes, intArrayOf(phaseSettings[4]))
val ampA = IntCodeProgram(programData.clone(), longArrayOf(phaseSettings[0].toLong()))
val ampB = IntCodeProgram(programData.clone(), longArrayOf(phaseSettings[1].toLong()))
val ampC = IntCodeProgram(programData.clone(), longArrayOf(phaseSettings[2].toLong()))
val ampD = IntCodeProgram(programData.clone(), longArrayOf(phaseSettings[3].toLong()))
val ampE = IntCodeProgram(programData.clone(), longArrayOf(phaseSettings[4].toLong()))
var ampAInput = 0
var ampAInput = 0L
var anyAmpRunning = true
while (anyAmpRunning) {
ampA.pushInput(ampAInput)

View File

@@ -0,0 +1,31 @@
package com.basdado.adventofcode.day9
import com.basdado.adventofcode.IntCodeProgram
import com.basdado.adventofcode.loadProgramData
const val DAY9_INPUT_PATH = "/day/9/input.txt"
fun main() {
Day9.puzzle1()
Day9.puzzle2()
}
object Day9 {
fun puzzle1() {
val programData = loadProgramData(DAY9_INPUT_PATH)
val program = IntCodeProgram(programData + LongArray(65536), longArrayOf(1))
program.execute()
println(program.getOutputs())
}
fun puzzle2() {
val programData = loadProgramData(DAY9_INPUT_PATH)
val program = IntCodeProgram(programData + LongArray(65536), longArrayOf(2))
program.execute()
println(program.getOutputs())
}
}

View File

@@ -0,0 +1 @@
1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,0,3,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1101,0,396,1029,1101,0,356,1023,1101,401,0,1028,1101,24,0,1008,1101,33,0,1019,1101,35,0,1010,1102,359,1,1022,1102,32,1,1001,1101,37,0,1004,1101,0,31,1009,1101,0,30,1003,1101,28,0,1002,1102,1,36,1014,1102,20,1,1012,1101,21,0,1000,1101,0,22,1015,1102,23,1,1013,1102,1,1,1021,1102,1,39,1007,1102,26,1,1017,1101,0,38,1016,1101,0,437,1024,1102,432,1,1025,1101,0,421,1026,1101,0,29,1005,1101,27,0,1011,1102,1,0,1020,1101,0,25,1018,1101,0,414,1027,1102,34,1,1006,109,6,2108,33,-3,63,1005,63,201,1001,64,1,64,1105,1,203,4,187,1002,64,2,64,109,14,21108,40,40,-6,1005,1014,221,4,209,1105,1,225,1001,64,1,64,1002,64,2,64,109,-21,2102,1,3,63,1008,63,28,63,1005,63,251,4,231,1001,64,1,64,1106,0,251,1002,64,2,64,109,12,2101,0,-3,63,1008,63,21,63,1005,63,275,1001,64,1,64,1105,1,277,4,257,1002,64,2,64,109,-10,1207,1,27,63,1005,63,293,1105,1,299,4,283,1001,64,1,64,1002,64,2,64,109,9,21108,41,42,3,1005,1013,315,1105,1,321,4,305,1001,64,1,64,1002,64,2,64,109,-12,1202,6,1,63,1008,63,37,63,1005,63,347,4,327,1001,64,1,64,1105,1,347,1002,64,2,64,109,29,2105,1,-4,1105,1,365,4,353,1001,64,1,64,1002,64,2,64,109,-17,2108,32,-9,63,1005,63,387,4,371,1001,64,1,64,1105,1,387,1002,64,2,64,109,17,2106,0,1,4,393,1105,1,405,1001,64,1,64,1002,64,2,64,109,1,2106,0,-1,1001,64,1,64,1106,0,423,4,411,1002,64,2,64,109,-13,2105,1,9,4,429,1106,0,441,1001,64,1,64,1002,64,2,64,109,3,21107,42,41,-1,1005,1017,461,1001,64,1,64,1106,0,463,4,447,1002,64,2,64,109,-4,21107,43,44,1,1005,1015,481,4,469,1106,0,485,1001,64,1,64,1002,64,2,64,109,-6,21101,44,0,6,1008,1014,47,63,1005,63,505,1106,0,511,4,491,1001,64,1,64,1002,64,2,64,109,-6,1208,-1,32,63,1005,63,529,4,517,1105,1,533,1001,64,1,64,1002,64,2,64,109,11,1205,7,545,1106,0,551,4,539,1001,64,1,64,1002,64,2,64,109,11,21102,45,1,-7,1008,1017,48,63,1005,63,575,1001,64,1,64,1106,0,577,4,557,1002,64,2,64,109,-8,1206,5,593,1001,64,1,64,1105,1,595,4,583,1002,64,2,64,109,7,1206,-3,609,4,601,1106,0,613,1001,64,1,64,1002,64,2,64,109,-10,2101,0,-6,63,1008,63,39,63,1005,63,635,4,619,1106,0,639,1001,64,1,64,1002,64,2,64,109,-9,1208,0,39,63,1005,63,655,1106,0,661,4,645,1001,64,1,64,1002,64,2,64,109,4,2107,25,0,63,1005,63,681,1001,64,1,64,1105,1,683,4,667,1002,64,2,64,109,-5,2107,31,-2,63,1005,63,701,4,689,1106,0,705,1001,64,1,64,1002,64,2,64,109,19,1205,-1,719,4,711,1105,1,723,1001,64,1,64,1002,64,2,64,109,-17,1201,3,0,63,1008,63,24,63,1005,63,745,4,729,1106,0,749,1001,64,1,64,1002,64,2,64,109,13,21102,46,1,-3,1008,1015,46,63,1005,63,771,4,755,1105,1,775,1001,64,1,64,1002,64,2,64,109,-13,1207,4,32,63,1005,63,793,4,781,1106,0,797,1001,64,1,64,1002,64,2,64,109,7,2102,1,-9,63,1008,63,27,63,1005,63,821,1001,64,1,64,1105,1,823,4,803,1002,64,2,64,109,-18,1201,8,0,63,1008,63,25,63,1005,63,847,1001,64,1,64,1106,0,849,4,829,1002,64,2,64,109,23,21101,47,0,2,1008,1019,47,63,1005,63,871,4,855,1106,0,875,1001,64,1,64,1002,64,2,64,109,-22,1202,5,1,63,1008,63,19,63,1005,63,899,1001,64,1,64,1106,0,901,4,881,4,64,99,21102,27,1,1,21102,1,915,0,1105,1,922,21201,1,25165,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,942,1,0,1105,1,922,22102,1,1,-1,21201,-2,-3,1,21101,0,957,0,1105,1,922,22201,1,-1,-2,1106,0,968,21201,-2,0,-2,109,-3,2105,1,0