Day 5 solution

This required an overhaul of the intcode machine to actually be its own type that could operate on its own memory and stuff. So I had to touch day 2 to make it adhere to the new API.

Feeling good about this foundation now. Until I get gobsmacked at some point later, which I expect to happen.
This commit is contained in:
2022-06-13 15:29:18 -05:00
parent bdd007bb4d
commit acef5fdc12
5 changed files with 390 additions and 34 deletions

View File

@ -18,19 +18,20 @@ func (d Day02) Num() int {
return 2 return 2
} }
func (d *Day02) getProgramWithParams(param1, param2 int64) utilities.IntcodeProgram { func (d *Day02) setParams(param1, param2 int64) {
program := make(utilities.IntcodeProgram, len(d.program)) d.program.Reset()
copy(program, d.program) d.program.SetMemory(1, param1)
program[1] = param1 d.program.SetMemory(2, param2)
program[2] = param2
return program
} }
func (d *Day02) Part1() string { func (d *Day02) Part1() string {
program := d.getProgramWithParams(12, 2) d.setParams(12, 2)
program.Run() d.program.Run()
return fmt.Sprintf("Position 0 = %s%d%s", utilities.TextBold, program[0], utilities.TextReset) if d.program.GetMemory(0) != 4138658 {
panic("")
}
return fmt.Sprintf("Position 0 = %s%d%s", utilities.TextBold, d.program.GetMemory(0), utilities.TextReset)
} }
func (d *Day02) Part2() string { func (d *Day02) Part2() string {
@ -41,10 +42,10 @@ func (d *Day02) Part2() string {
found := false found := false
for noun = 0; noun <= 99; noun++ { for noun = 0; noun <= 99; noun++ {
for verb = 0; verb <= 99; verb++ { for verb = 0; verb <= 99; verb++ {
program := d.getProgramWithParams(noun, verb) d.setParams(noun, verb)
program.Run() d.program.Run()
if program[0] == sentinel { if d.program.GetMemory(0) == sentinel {
found = true found = true
break break
} }
@ -58,6 +59,9 @@ func (d *Day02) Part2() string {
if !found { if !found {
panic("!found") panic("!found")
} }
if noun != 72 || verb != 64 {
panic("")
}
return fmt.Sprintf("%d created by noun=%d, verb=%d. 100 * noun + verb = %s%d%s", return fmt.Sprintf("%d created by noun=%d, verb=%d. 100 * noun + verb = %s%d%s",
sentinel, sentinel,

188
days/05.go Normal file
View File

@ -0,0 +1,188 @@
package days
import (
"fmt"
"parnic.com/aoc2019/utilities"
)
type Day05 struct {
program utilities.IntcodeProgram
}
func (d *Day05) Parse() {
d.program = utilities.LoadIntcodeProgram("05p")
d.test()
}
func (d Day05) Num() int {
return 5
}
func (d Day05) test() {
// Using position mode, consider whether the input is equal to 8; output 1 (if it is) or 0 (if it is not).
program := utilities.ParseIntcodeProgram("3,9,8,9,10,9,4,9,99,-1,8")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// Using position mode, consider whether the input is less than 8; output 1 (if it is) or 0 (if it is not).
program = utilities.ParseIntcodeProgram("3,9,7,9,10,9,4,9,99,-1,8")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
// Using immediate mode, consider whether the input is equal to 8; output 1 (if it is) or 0 (if it is not).
program = utilities.ParseIntcodeProgram("3,3,1108,-1,8,3,4,3,99")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// Using immediate mode, consider whether the input is less than 8; output 1 (if it is) or 0 (if it is not).
program = utilities.ParseIntcodeProgram("3,3,1107,-1,8,3,4,3,99")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
// jump tests that take an input, then output 0 if the input was zero or 1 if the input was non-zero
// position mode
program = utilities.ParseIntcodeProgram("3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// jump tests that take an input, then output 0 if the input was zero or 1 if the input was non-zero
// immediate mode
program = utilities.ParseIntcodeProgram("3,3,1105,-1,9,1101,0,0,12,4,12,99,1")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 0 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1 {
panic("")
}
})
// uses an input instruction to ask for a single number. The program will then output 999 if the input value is below 8, output 1000 if the input value is equal to 8, or output 1001 if the input value is greater than 8.
program = utilities.ParseIntcodeProgram("3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99")
program.RunIn(func(int) int64 {
return 0
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 999 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 8
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1000 {
panic("")
}
})
program.Reset()
program.RunIn(func(int) int64 {
return 9
}, func(val int64, state utilities.IntcodeProgramState) {
if val != 1001 {
panic("")
}
})
}
func (d *Day05) Part1() string {
diagCode := int64(-1)
d.program.RunIn(func(int) int64 {
return 1
}, func(val int64, state utilities.IntcodeProgramState) {
if state.IsHalting() {
diagCode = val
} else if val != 0 {
panic("test failed")
}
})
return fmt.Sprintf("Diagnostic code: %s%d%s", utilities.TextBold, diagCode, utilities.TextReset)
}
func (d *Day05) Part2() string {
d.program.Reset()
diagCode := int64(-1)
d.program.RunIn(func(int) int64 {
return 5
}, func(val int64, state utilities.IntcodeProgramState) {
if !state.IsHalting() {
panic("unexpected output received")
}
diagCode = val
})
return fmt.Sprintf("Diagnostic code: %s%d%s", utilities.TextBold, diagCode, utilities.TextReset)
}

1
inputs/05p.txt Normal file
View File

@ -0,0 +1 @@
3,225,1,225,6,6,1100,1,238,225,104,0,1102,78,40,225,1102,52,43,224,1001,224,-2236,224,4,224,102,8,223,223,101,4,224,224,1,224,223,223,1,191,61,224,1001,224,-131,224,4,224,102,8,223,223,101,4,224,224,1,223,224,223,1101,86,74,225,1102,14,76,225,1101,73,83,224,101,-156,224,224,4,224,102,8,223,223,101,6,224,224,1,224,223,223,1102,43,82,225,2,196,13,224,101,-6162,224,224,4,224,102,8,223,223,101,5,224,224,1,223,224,223,1001,161,51,224,101,-70,224,224,4,224,102,8,223,223,1001,224,1,224,1,224,223,223,102,52,187,224,1001,224,-832,224,4,224,102,8,223,223,101,1,224,224,1,224,223,223,1102,19,79,225,101,65,92,224,1001,224,-147,224,4,224,1002,223,8,223,101,4,224,224,1,223,224,223,1102,16,90,225,1102,45,44,225,1102,92,79,225,1002,65,34,224,101,-476,224,224,4,224,102,8,223,223,1001,224,5,224,1,224,223,223,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,107,226,226,224,1002,223,2,223,1005,224,329,1001,223,1,223,1007,226,226,224,102,2,223,223,1005,224,344,101,1,223,223,1008,226,226,224,102,2,223,223,1005,224,359,1001,223,1,223,8,226,677,224,102,2,223,223,1006,224,374,101,1,223,223,1107,226,677,224,1002,223,2,223,1006,224,389,101,1,223,223,1108,226,677,224,102,2,223,223,1005,224,404,101,1,223,223,107,677,677,224,102,2,223,223,1006,224,419,1001,223,1,223,7,677,226,224,102,2,223,223,1005,224,434,101,1,223,223,1007,677,677,224,102,2,223,223,1005,224,449,1001,223,1,223,108,226,677,224,102,2,223,223,1005,224,464,1001,223,1,223,108,226,226,224,102,2,223,223,1006,224,479,101,1,223,223,107,226,677,224,102,2,223,223,1006,224,494,1001,223,1,223,7,226,226,224,1002,223,2,223,1006,224,509,101,1,223,223,1108,677,226,224,102,2,223,223,1005,224,524,101,1,223,223,1107,677,226,224,102,2,223,223,1005,224,539,101,1,223,223,1008,677,226,224,102,2,223,223,1005,224,554,101,1,223,223,1008,677,677,224,1002,223,2,223,1006,224,569,101,1,223,223,1107,677,677,224,102,2,223,223,1006,224,584,1001,223,1,223,1108,226,226,224,1002,223,2,223,1006,224,599,101,1,223,223,7,226,677,224,102,2,223,223,1006,224,614,101,1,223,223,108,677,677,224,1002,223,2,223,1006,224,629,101,1,223,223,1007,677,226,224,102,2,223,223,1006,224,644,101,1,223,223,8,677,677,224,1002,223,2,223,1006,224,659,101,1,223,223,8,677,226,224,102,2,223,223,1005,224,674,101,1,223,223,4,223,99,226

View File

@ -35,6 +35,7 @@ var dayMap = []day{
&days.Day02{}, &days.Day02{},
&days.Day03{}, &days.Day03{},
&days.Day04{}, &days.Day04{},
&days.Day05{},
} }
func main() { func main() {

View File

@ -1,52 +1,214 @@
package utilities package utilities
import ( import (
"fmt"
"strconv" "strconv"
"strings" "strings"
) )
const ( const (
opAdd = 1 opAdd = 1
opMul = 2 opMultiply = 2
opEnd = 99 opInput = 3
opOutput = 4
opJumpIfTrue = 5
opJumpIfFalse = 6
opLessThan = 7
opEquals = 8
opHalt = 99
modePosition = 0
modeImmediate = 1
) )
type IntcodeProgram []int64 type IntcodeProgram struct {
memory []int64
program []int64
}
type IntcodeProgramState struct {
program *IntcodeProgram
CurrentInstruction int
NextInstruction int
}
func (s IntcodeProgramState) IsHalting() bool {
return s.program.GetMemory(s.NextInstruction) == opHalt
}
type ProvideInputFunc func(inputStep int) int64
type ReceiveOutputFunc func(val int64, state IntcodeProgramState)
func ParseIntcodeProgram(programStr string) IntcodeProgram { func ParseIntcodeProgram(programStr string) IntcodeProgram {
nums := strings.Split(programStr, ",") nums := strings.Split(programStr, ",")
program := make(IntcodeProgram, len(nums)) program := IntcodeProgram{
program: make([]int64, len(nums)),
}
for idx, num := range nums { for idx, num := range nums {
iNum, err := strconv.ParseInt(num, 10, 64) iNum, err := strconv.ParseInt(num, 10, 64)
if err != nil { if err != nil {
panic(err) panic(err)
} }
program[idx] = iNum program.program[idx] = iNum
} }
return program return program
} }
func (program IntcodeProgram) Run() { func (p *IntcodeProgram) makeState(instructionPointer int) IntcodeProgramState {
for instructionPointer := 0; instructionPointer < len(program); { return IntcodeProgramState{
opcode := program[instructionPointer] program: p,
CurrentInstruction: instructionPointer,
NextInstruction: instructionPointer + 1,
}
}
func (p *IntcodeProgram) Copy() IntcodeProgram {
ret := IntcodeProgram{
program: make([]int64, len(p.program)),
}
copy(ret.program, p.program)
return ret
}
func (p *IntcodeProgram) init() {
if p.memory == nil {
p.memory = make([]int64, len(p.program))
p.Reset()
}
}
func (p *IntcodeProgram) getParamValue(param, mode int) int64 {
switch mode {
case modePosition:
return p.memory[param]
case modeImmediate:
return int64(param)
}
panic("unhandled param mode")
}
func (p *IntcodeProgram) GetMemory(idx int) int64 {
return p.memory[idx]
}
func (p *IntcodeProgram) SetMemory(idx int, val int64) {
p.init()
p.memory[idx] = val
}
func (p *IntcodeProgram) Reset() {
p.init()
copy(p.memory, p.program)
}
func (p *IntcodeProgram) Run() {
p.RunIn(func(int) int64 { return 0 }, func(int64, IntcodeProgramState) {})
}
func (p *IntcodeProgram) RunIn(inputFunc ProvideInputFunc, outputFunc ReceiveOutputFunc) {
p.init()
inputsRequested := 0
for instructionPointer := 0; instructionPointer < len(p.program); {
instruction := p.memory[instructionPointer]
instructionPointer++
paramModes := [3]int{
modePosition,
modePosition,
modePosition,
}
modes := instruction / 100
for i := 0; modes > 0; i++ {
paramModes[i] = int(modes % 10)
modes = modes / 10
}
opcode := instruction % 100
switch opcode { switch opcode {
case opAdd: case opAdd:
param1 := program[instructionPointer+1] param1 := p.memory[instructionPointer]
param2 := program[instructionPointer+2] param2 := p.memory[instructionPointer+1]
param3 := program[instructionPointer+3] param3 := p.memory[instructionPointer+2]
program[param3] = program[param1] + program[param2] p.memory[param3] = p.getParamValue(int(param1), paramModes[0]) + p.getParamValue(int(param2), paramModes[1])
instructionPointer += 4 instructionPointer += 3
case opMul:
param1 := program[instructionPointer+1]
param2 := program[instructionPointer+2]
param3 := program[instructionPointer+3]
program[param3] = program[param1] * program[param2]
instructionPointer += 4 case opMultiply:
case opEnd: param1 := p.memory[instructionPointer]
instructionPointer = len(program) 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])
instructionPointer += 3
case opInput:
inputsRequested++
param1 := p.memory[instructionPointer]
p.memory[param1] = inputFunc(inputsRequested)
instructionPointer += 1
case opOutput:
param1 := p.memory[instructionPointer]
outputFunc(p.getParamValue(int(param1), paramModes[0]), p.makeState(instructionPointer))
instructionPointer += 1
case opJumpIfTrue:
param1 := p.memory[instructionPointer]
param2 := p.memory[instructionPointer+1]
if p.getParamValue(int(param1), paramModes[0]) != 0 {
instructionPointer = int(p.getParamValue(int(param2), paramModes[1]))
} else {
instructionPointer += 2
}
case opJumpIfFalse:
param1 := p.memory[instructionPointer]
param2 := p.memory[instructionPointer+1]
if p.getParamValue(int(param1), paramModes[0]) == 0 {
instructionPointer = int(p.getParamValue(int(param2), paramModes[1]))
} else {
instructionPointer += 2
}
case opLessThan:
param1 := p.memory[instructionPointer]
param2 := p.memory[instructionPointer+1]
param3 := p.memory[instructionPointer+2]
if p.getParamValue(int(param1), paramModes[0]) < p.getParamValue(int(param2), paramModes[1]) {
p.memory[param3] = 1
} else {
p.memory[param3] = 0
}
instructionPointer += 3
case opEquals:
param1 := p.memory[instructionPointer]
param2 := p.memory[instructionPointer+1]
param3 := p.memory[instructionPointer+2]
if p.getParamValue(int(param1), paramModes[0]) == p.getParamValue(int(param2), paramModes[1]) {
p.memory[param3] = 1
} else {
p.memory[param3] = 0
}
instructionPointer += 3
case opHalt:
instructionPointer = len(p.program)
default:
panic(fmt.Sprintf("exception executing program - unhandled opcode %d", opcode))
} }
} }
} }