From d7836f4e5945cbe3f2be8c688d2a3d36a55b4033 Mon Sep 17 00:00:00 2001 From: Parnic Date: Mon, 20 Jun 2022 08:54:52 -0500 Subject: [PATCH] Day 9 solution This day showed me that when the input instruction was introduced and said "write addresses will never be in immediate mode", that didn't mean "so don't bother handling modes for input addresses", it meant "handle the mode, but assert if it's immediate mode". It was super helpful that this program contained a bootstrap sequence to validate each instruction. Memory expansion came with a few caveats: obviously reads and writes needed to handle expanding the memory space, but a Reset also can no longer get away with simply copying the program into memory again because we need to ensure that any additional memory is cut off (or at least zeroed), so the quickest way to handle that in Go is to simply allocate a new buffer; I'd rather manipulate the existing buffer, but I'm having a hard time finding the best way to do that. And finally, make sure you reset your relativeBase when resetting the program...that one was ugly to track down. --- days/09.go | 42 ++++++++++++++++ inputs/09p.txt | 1 + inputs/09s1.txt | 1 + inputs/09s2.txt | 1 + inputs/09s3.txt | 1 + main.go | 1 + utilities/intcode.go | 114 ++++++++++++++++++++++++++++--------------- 7 files changed, 122 insertions(+), 39 deletions(-) create mode 100644 days/09.go create mode 100644 inputs/09p.txt create mode 100644 inputs/09s1.txt create mode 100644 inputs/09s2.txt create mode 100644 inputs/09s3.txt diff --git a/days/09.go b/days/09.go new file mode 100644 index 0000000..b80c82b --- /dev/null +++ b/days/09.go @@ -0,0 +1,42 @@ +package days + +import ( + "fmt" + + "parnic.com/aoc2019/utilities" +) + +type Day09 struct { + program utilities.IntcodeProgram +} + +func (d *Day09) Parse() { + d.program = utilities.LoadIntcodeProgram("09p") +} + +func (d Day09) Num() int { + return 9 +} + +func (d *Day09) Part1() string { + var code int64 + d.program.RunIn(func(inputStep int) int64 { + return 1 + }, func(val int64, state utilities.IntcodeProgramState) { + code = val + }) + + return fmt.Sprintf("BOOST keycode: %s%d%s", utilities.TextBold, code, utilities.TextReset) +} + +func (d *Day09) Part2() string { + var coordinates int64 + d.program.Reset() + d.program.RunIn(func(inputStep int) int64 { + return 2 + }, func(val int64, state utilities.IntcodeProgramState) { + coordinates = val + }) + + return fmt.Sprintf("Coordinates: %s%d%s", utilities.TextBold, coordinates, utilities.TextReset) +} diff --git a/inputs/09p.txt b/inputs/09p.txt new file mode 100644 index 0000000..17a281f --- /dev/null +++ b/inputs/09p.txt @@ -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,1102,252,1,1023,1102,36,1,1008,1102,24,1,1017,1101,25,0,1013,1102,479,1,1026,1101,0,259,1022,1102,1,38,1001,1102,1,713,1024,1101,0,708,1025,1102,1,22,1006,1101,0,32,1010,1101,476,0,1027,1102,1,516,1029,1102,1,34,1009,1101,0,23,1016,1102,1,37,1011,1102,525,1,1028,1101,0,35,1004,1102,31,1,1002,1102,39,1,1019,1102,28,1,1015,1102,1,1,1021,1101,0,30,1007,1101,0,27,1014,1101,21,0,1018,1101,0,29,1005,1102,26,1,1000,1102,1,0,1020,1101,0,20,1012,1101,33,0,1003,109,13,21108,40,40,6,1005,1019,199,4,187,1106,0,203,1001,64,1,64,1002,64,2,64,109,15,1205,-7,221,4,209,1001,64,1,64,1105,1,221,1002,64,2,64,109,-25,1208,-3,26,63,1005,63,243,4,227,1001,64,1,64,1106,0,243,1002,64,2,64,109,25,2105,1,-5,1001,64,1,64,1106,0,261,4,249,1002,64,2,64,109,-4,21108,41,42,-8,1005,1016,281,1001,64,1,64,1106,0,283,4,267,1002,64,2,64,109,-6,1206,2,301,4,289,1001,64,1,64,1105,1,301,1002,64,2,64,109,-4,21102,42,1,2,1008,1016,42,63,1005,63,323,4,307,1106,0,327,1001,64,1,64,1002,64,2,64,109,-7,2108,35,1,63,1005,63,343,1105,1,349,4,333,1001,64,1,64,1002,64,2,64,109,-13,1208,7,35,63,1005,63,369,1001,64,1,64,1106,0,371,4,355,1002,64,2,64,109,24,21102,43,1,-1,1008,1017,42,63,1005,63,391,1105,1,397,4,377,1001,64,1,64,1002,64,2,64,109,-13,2101,0,-4,63,1008,63,38,63,1005,63,419,4,403,1105,1,423,1001,64,1,64,1002,64,2,64,109,21,1206,-5,435,1106,0,441,4,429,1001,64,1,64,1002,64,2,64,109,-22,21101,44,0,10,1008,1014,44,63,1005,63,463,4,447,1105,1,467,1001,64,1,64,1002,64,2,64,109,25,2106,0,-2,1106,0,485,4,473,1001,64,1,64,1002,64,2,64,109,-19,2107,37,-2,63,1005,63,501,1106,0,507,4,491,1001,64,1,64,1002,64,2,64,109,8,2106,0,10,4,513,1001,64,1,64,1105,1,525,1002,64,2,64,109,-6,21107,45,46,0,1005,1012,547,4,531,1001,64,1,64,1105,1,547,1002,64,2,64,109,-5,1202,-1,1,63,1008,63,21,63,1005,63,567,1105,1,573,4,553,1001,64,1,64,1002,64,2,64,109,2,1207,-3,21,63,1005,63,589,1105,1,595,4,579,1001,64,1,64,1002,64,2,64,109,1,1201,-8,0,63,1008,63,34,63,1005,63,619,1001,64,1,64,1106,0,621,4,601,1002,64,2,64,109,-6,2102,1,-1,63,1008,63,33,63,1005,63,643,4,627,1105,1,647,1001,64,1,64,1002,64,2,64,109,10,21101,46,0,3,1008,1017,43,63,1005,63,667,1106,0,673,4,653,1001,64,1,64,1002,64,2,64,109,-13,2102,1,8,63,1008,63,35,63,1005,63,697,1001,64,1,64,1106,0,699,4,679,1002,64,2,64,109,23,2105,1,0,4,705,1105,1,717,1001,64,1,64,1002,64,2,64,109,-1,1205,-3,729,1106,0,735,4,723,1001,64,1,64,1002,64,2,64,109,-15,2101,0,0,63,1008,63,38,63,1005,63,755,1106,0,761,4,741,1001,64,1,64,1002,64,2,64,109,-2,2107,28,-1,63,1005,63,779,4,767,1106,0,783,1001,64,1,64,1002,64,2,64,109,-2,2108,35,0,63,1005,63,801,4,789,1105,1,805,1001,64,1,64,1002,64,2,64,109,1,1201,-5,0,63,1008,63,26,63,1005,63,831,4,811,1001,64,1,64,1105,1,831,1002,64,2,64,109,-5,1207,5,30,63,1005,63,849,4,837,1106,0,853,1001,64,1,64,1002,64,2,64,109,2,1202,-2,1,63,1008,63,26,63,1005,63,879,4,859,1001,64,1,64,1105,1,879,1002,64,2,64,109,15,21107,47,46,0,1005,1017,899,1001,64,1,64,1105,1,901,4,885,4,64,99,21102,1,27,1,21101,915,0,0,1106,0,922,21201,1,66416,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21102,942,1,0,1105,1,922,21202,1,1,-1,21201,-2,-3,1,21102,1,957,0,1105,1,922,22201,1,-1,-2,1105,1,968,22102,1,-2,-2,109,-3,2105,1,0 \ No newline at end of file diff --git a/inputs/09s1.txt b/inputs/09s1.txt new file mode 100644 index 0000000..521db73 --- /dev/null +++ b/inputs/09s1.txt @@ -0,0 +1 @@ +109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99 \ No newline at end of file diff --git a/inputs/09s2.txt b/inputs/09s2.txt new file mode 100644 index 0000000..ad18566 --- /dev/null +++ b/inputs/09s2.txt @@ -0,0 +1 @@ +1102,34915192,34915192,7,4,7,99,0 \ No newline at end of file diff --git a/inputs/09s3.txt b/inputs/09s3.txt new file mode 100644 index 0000000..78bddee --- /dev/null +++ b/inputs/09s3.txt @@ -0,0 +1 @@ +104,1125899906842624,99 \ No newline at end of file diff --git a/main.go b/main.go index d5bac69..8938fe1 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ var dayMap = []day{ &days.Day06{}, &days.Day07{}, &days.Day08{}, + &days.Day09{}, } func main() { diff --git a/utilities/intcode.go b/utilities/intcode.go index f13196d..efdeb44 100644 --- a/utilities/intcode.go +++ b/utilities/intcode.go @@ -7,23 +7,26 @@ import ( ) const ( - opAdd = 1 - opMultiply = 2 - opInput = 3 - opOutput = 4 - opJumpIfTrue = 5 - opJumpIfFalse = 6 - opLessThan = 7 - opEquals = 8 - opHalt = 99 + opAdd = 1 + opMultiply = 2 + opInput = 3 + opOutput = 4 + opJumpIfTrue = 5 + opJumpIfFalse = 6 + opLessThan = 7 + opEquals = 8 + opRelativeBase = 9 + opHalt = 99 modePosition = 0 modeImmediate = 1 + modeRelative = 2 ) type IntcodeProgram struct { - memory []int64 - program []int64 + memory []int64 + program []int64 + relativeBase int } type IntcodeProgramState struct { @@ -74,34 +77,60 @@ func (p *IntcodeProgram) Copy() IntcodeProgram { func (p *IntcodeProgram) init() { if p.memory == nil { p.memory = make([]int64, len(p.program)) - p.Reset() + copy(p.memory, p.program) } } func (p *IntcodeProgram) getParamValue(param, mode int) int64 { switch mode { case modePosition: - return p.memory[param] + return p.GetMemory(param) case modeImmediate: return int64(param) + + case modeRelative: + return p.GetMemory(param + p.relativeBase) } panic("unhandled param mode") } func (p *IntcodeProgram) GetMemory(idx int) int64 { + p.ensureMemoryCapacity(idx) return p.memory[idx] } func (p *IntcodeProgram) SetMemory(idx int, val int64) { p.init() + p.ensureMemoryCapacity(idx) p.memory[idx] = val } +func (p *IntcodeProgram) setMemory(idx int, val int64, mode int) { + if mode == modeImmediate { + panic("exception executing program - write parameter must never be in immediate mode") + } + if mode == modeRelative { + idx = idx + p.relativeBase + } + + p.SetMemory(idx, val) +} + +func (p *IntcodeProgram) ensureMemoryCapacity(address int) { + if len(p.memory) > address { + return + } + + p.memory = append(p.memory, make([]int64, address+1-len(p.memory))...) +} + func (p *IntcodeProgram) Reset() { + p.memory = nil p.init() copy(p.memory, p.program) + p.relativeBase = 0 } func (p *IntcodeProgram) Run() { @@ -113,7 +142,7 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut inputsRequested := 0 for instructionPointer := 0; instructionPointer < len(p.program); { - instruction := p.memory[instructionPointer] + instruction := p.GetMemory(instructionPointer) instructionPointer++ paramModes := [3]int{ @@ -130,37 +159,37 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut opcode := instruction % 100 switch opcode { case opAdd: - param1 := p.memory[instructionPointer] - param2 := p.memory[instructionPointer+1] - param3 := p.memory[instructionPointer+2] - p.memory[param3] = p.getParamValue(int(param1), paramModes[0]) + p.getParamValue(int(param2), paramModes[1]) + param1 := p.GetMemory(instructionPointer) + param2 := p.GetMemory(instructionPointer + 1) + param3 := p.GetMemory(instructionPointer + 2) + p.setMemory(int(param3), p.getParamValue(int(param1), paramModes[0])+p.getParamValue(int(param2), paramModes[1]), paramModes[2]) instructionPointer += 3 case opMultiply: - param1 := p.memory[instructionPointer] - param2 := p.memory[instructionPointer+1] - param3 := p.memory[instructionPointer+2] - p.memory[param3] = p.getParamValue(int(param1), paramModes[0]) * p.getParamValue(int(param2), paramModes[1]) + param1 := p.GetMemory(instructionPointer) + param2 := p.GetMemory(instructionPointer + 1) + param3 := p.GetMemory(instructionPointer + 2) + p.setMemory(int(param3), p.getParamValue(int(param1), paramModes[0])*p.getParamValue(int(param2), paramModes[1]), paramModes[2]) instructionPointer += 3 case opInput: inputsRequested++ - param1 := p.memory[instructionPointer] - p.memory[param1] = inputFunc(inputsRequested) + param1 := p.GetMemory(instructionPointer) + p.setMemory(int(param1), inputFunc(inputsRequested), paramModes[0]) instructionPointer += 1 case opOutput: - param1 := p.memory[instructionPointer] + param1 := p.GetMemory(instructionPointer) outputFunc(p.getParamValue(int(param1), paramModes[0]), p.makeState(instructionPointer)) instructionPointer += 1 case opJumpIfTrue: - param1 := p.memory[instructionPointer] - param2 := p.memory[instructionPointer+1] + param1 := p.GetMemory(instructionPointer) + param2 := p.GetMemory(instructionPointer + 1) if p.getParamValue(int(param1), paramModes[0]) != 0 { instructionPointer = int(p.getParamValue(int(param2), paramModes[1])) @@ -169,8 +198,8 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut } case opJumpIfFalse: - param1 := p.memory[instructionPointer] - param2 := p.memory[instructionPointer+1] + param1 := p.GetMemory(instructionPointer) + param2 := p.GetMemory(instructionPointer + 1) if p.getParamValue(int(param1), paramModes[0]) == 0 { instructionPointer = int(p.getParamValue(int(param2), paramModes[1])) @@ -179,31 +208,38 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut } case opLessThan: - param1 := p.memory[instructionPointer] - param2 := p.memory[instructionPointer+1] - param3 := p.memory[instructionPointer+2] + param1 := p.GetMemory(instructionPointer) + param2 := p.GetMemory(instructionPointer + 1) + param3 := p.GetMemory(instructionPointer + 2) if p.getParamValue(int(param1), paramModes[0]) < p.getParamValue(int(param2), paramModes[1]) { - p.memory[param3] = 1 + p.setMemory(int(param3), 1, paramModes[2]) } else { - p.memory[param3] = 0 + p.setMemory(int(param3), 0, paramModes[2]) } instructionPointer += 3 case opEquals: - param1 := p.memory[instructionPointer] - param2 := p.memory[instructionPointer+1] - param3 := p.memory[instructionPointer+2] + param1 := p.GetMemory(instructionPointer) + param2 := p.GetMemory(instructionPointer + 1) + param3 := p.GetMemory(instructionPointer + 2) if p.getParamValue(int(param1), paramModes[0]) == p.getParamValue(int(param2), paramModes[1]) { - p.memory[param3] = 1 + p.setMemory(int(param3), 1, paramModes[2]) } else { - p.memory[param3] = 0 + p.setMemory(int(param3), 0, paramModes[2]) } instructionPointer += 3 + case opRelativeBase: + param1 := p.GetMemory(instructionPointer) + + p.relativeBase += int(p.getParamValue(int(param1), paramModes[0])) + + instructionPointer += 1 + case opHalt: instructionPointer = len(p.program)