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.
This commit is contained in:
2022-06-20 08:54:52 -05:00
parent b321fb87ec
commit d7836f4e59
7 changed files with 122 additions and 39 deletions

42
days/09.go Normal file
View File

@ -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)
}

1
inputs/09p.txt Normal file
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,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

1
inputs/09s1.txt Normal file
View File

@ -0,0 +1 @@
109,1,204,-1,1001,100,1,100,1008,100,16,101,1006,101,0,99

1
inputs/09s2.txt Normal file
View File

@ -0,0 +1 @@
1102,34915192,34915192,7,4,7,99,0

1
inputs/09s3.txt Normal file
View File

@ -0,0 +1 @@
104,1125899906842624,99

View File

@ -39,6 +39,7 @@ var dayMap = []day{
&days.Day06{}, &days.Day06{},
&days.Day07{}, &days.Day07{},
&days.Day08{}, &days.Day08{},
&days.Day09{},
} }
func main() { func main() {

View File

@ -7,23 +7,26 @@ import (
) )
const ( const (
opAdd = 1 opAdd = 1
opMultiply = 2 opMultiply = 2
opInput = 3 opInput = 3
opOutput = 4 opOutput = 4
opJumpIfTrue = 5 opJumpIfTrue = 5
opJumpIfFalse = 6 opJumpIfFalse = 6
opLessThan = 7 opLessThan = 7
opEquals = 8 opEquals = 8
opHalt = 99 opRelativeBase = 9
opHalt = 99
modePosition = 0 modePosition = 0
modeImmediate = 1 modeImmediate = 1
modeRelative = 2
) )
type IntcodeProgram struct { type IntcodeProgram struct {
memory []int64 memory []int64
program []int64 program []int64
relativeBase int
} }
type IntcodeProgramState struct { type IntcodeProgramState struct {
@ -74,34 +77,60 @@ func (p *IntcodeProgram) Copy() IntcodeProgram {
func (p *IntcodeProgram) init() { func (p *IntcodeProgram) init() {
if p.memory == nil { if p.memory == nil {
p.memory = make([]int64, len(p.program)) p.memory = make([]int64, len(p.program))
p.Reset() copy(p.memory, p.program)
} }
} }
func (p *IntcodeProgram) getParamValue(param, mode int) int64 { func (p *IntcodeProgram) getParamValue(param, mode int) int64 {
switch mode { switch mode {
case modePosition: case modePosition:
return p.memory[param] return p.GetMemory(param)
case modeImmediate: case modeImmediate:
return int64(param) return int64(param)
case modeRelative:
return p.GetMemory(param + p.relativeBase)
} }
panic("unhandled param mode") panic("unhandled param mode")
} }
func (p *IntcodeProgram) GetMemory(idx int) int64 { func (p *IntcodeProgram) GetMemory(idx int) int64 {
p.ensureMemoryCapacity(idx)
return p.memory[idx] return p.memory[idx]
} }
func (p *IntcodeProgram) SetMemory(idx int, val int64) { func (p *IntcodeProgram) SetMemory(idx int, val int64) {
p.init() p.init()
p.ensureMemoryCapacity(idx)
p.memory[idx] = val 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() { func (p *IntcodeProgram) Reset() {
p.memory = nil
p.init() p.init()
copy(p.memory, p.program) copy(p.memory, p.program)
p.relativeBase = 0
} }
func (p *IntcodeProgram) Run() { func (p *IntcodeProgram) Run() {
@ -113,7 +142,7 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut
inputsRequested := 0 inputsRequested := 0
for instructionPointer := 0; instructionPointer < len(p.program); { for instructionPointer := 0; instructionPointer < len(p.program); {
instruction := p.memory[instructionPointer] instruction := p.GetMemory(instructionPointer)
instructionPointer++ instructionPointer++
paramModes := [3]int{ paramModes := [3]int{
@ -130,37 +159,37 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut
opcode := instruction % 100 opcode := instruction % 100
switch opcode { switch opcode {
case opAdd: case opAdd:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
param2 := p.memory[instructionPointer+1] param2 := p.GetMemory(instructionPointer + 1)
param3 := p.memory[instructionPointer+2] param3 := p.GetMemory(instructionPointer + 2)
p.memory[param3] = p.getParamValue(int(param1), paramModes[0]) + p.getParamValue(int(param2), paramModes[1]) p.setMemory(int(param3), p.getParamValue(int(param1), paramModes[0])+p.getParamValue(int(param2), paramModes[1]), paramModes[2])
instructionPointer += 3 instructionPointer += 3
case opMultiply: case opMultiply:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
param2 := p.memory[instructionPointer+1] param2 := p.GetMemory(instructionPointer + 1)
param3 := p.memory[instructionPointer+2] param3 := p.GetMemory(instructionPointer + 2)
p.memory[param3] = p.getParamValue(int(param1), paramModes[0]) * p.getParamValue(int(param2), paramModes[1]) p.setMemory(int(param3), p.getParamValue(int(param1), paramModes[0])*p.getParamValue(int(param2), paramModes[1]), paramModes[2])
instructionPointer += 3 instructionPointer += 3
case opInput: case opInput:
inputsRequested++ inputsRequested++
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
p.memory[param1] = inputFunc(inputsRequested) p.setMemory(int(param1), inputFunc(inputsRequested), paramModes[0])
instructionPointer += 1 instructionPointer += 1
case opOutput: case opOutput:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
outputFunc(p.getParamValue(int(param1), paramModes[0]), p.makeState(instructionPointer)) outputFunc(p.getParamValue(int(param1), paramModes[0]), p.makeState(instructionPointer))
instructionPointer += 1 instructionPointer += 1
case opJumpIfTrue: case opJumpIfTrue:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
param2 := p.memory[instructionPointer+1] param2 := p.GetMemory(instructionPointer + 1)
if p.getParamValue(int(param1), paramModes[0]) != 0 { if p.getParamValue(int(param1), paramModes[0]) != 0 {
instructionPointer = int(p.getParamValue(int(param2), paramModes[1])) instructionPointer = int(p.getParamValue(int(param2), paramModes[1]))
@ -169,8 +198,8 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut
} }
case opJumpIfFalse: case opJumpIfFalse:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
param2 := p.memory[instructionPointer+1] param2 := p.GetMemory(instructionPointer + 1)
if p.getParamValue(int(param1), paramModes[0]) == 0 { if p.getParamValue(int(param1), paramModes[0]) == 0 {
instructionPointer = int(p.getParamValue(int(param2), paramModes[1])) instructionPointer = int(p.getParamValue(int(param2), paramModes[1]))
@ -179,31 +208,38 @@ func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOut
} }
case opLessThan: case opLessThan:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
param2 := p.memory[instructionPointer+1] param2 := p.GetMemory(instructionPointer + 1)
param3 := p.memory[instructionPointer+2] param3 := p.GetMemory(instructionPointer + 2)
if p.getParamValue(int(param1), paramModes[0]) < p.getParamValue(int(param2), paramModes[1]) { 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 { } else {
p.memory[param3] = 0 p.setMemory(int(param3), 0, paramModes[2])
} }
instructionPointer += 3 instructionPointer += 3
case opEquals: case opEquals:
param1 := p.memory[instructionPointer] param1 := p.GetMemory(instructionPointer)
param2 := p.memory[instructionPointer+1] param2 := p.GetMemory(instructionPointer + 1)
param3 := p.memory[instructionPointer+2] param3 := p.GetMemory(instructionPointer + 2)
if p.getParamValue(int(param1), paramModes[0]) == p.getParamValue(int(param2), paramModes[1]) { 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 { } else {
p.memory[param3] = 0 p.setMemory(int(param3), 0, paramModes[2])
} }
instructionPointer += 3 instructionPointer += 3
case opRelativeBase:
param1 := p.GetMemory(instructionPointer)
p.relativeBase += int(p.getParamValue(int(param1), paramModes[0]))
instructionPointer += 1
case opHalt: case opHalt:
instructionPointer = len(p.program) instructionPointer = len(p.program)