mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-21 21:41:16 +00:00
Add solution for Day 5
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
588a27e37d
commit
658829657b
8 changed files with 421 additions and 48 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
/cover
|
||||
day03_debug.png
|
||||
|
|
6
Makefile
Normal file
6
Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
default: fulltest
|
||||
|
||||
fulltest:
|
||||
mkdir -p cover
|
||||
go test -cover -coverprofile ./cover/cover.profile -v *.go
|
||||
go tool cover -html ./cover/cover.profile -o ./cover/index.html
|
50
day02.go
50
day02.go
|
@ -2,61 +2,15 @@ package aoc2019
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func parseDay02Intcode(code string) ([]int, error) {
|
||||
parts := strings.Split(code, ",")
|
||||
|
||||
var out []int
|
||||
for _, n := range parts {
|
||||
v, err := strconv.Atoi(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, v)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
func parseDay02Intcode(code string) ([]int, error) { return parseIntcode(code) }
|
||||
|
||||
func executeDay02Intcode(code []int) ([]int, error) {
|
||||
var (
|
||||
pos int
|
||||
run = true
|
||||
)
|
||||
|
||||
for run {
|
||||
if pos >= len(code) {
|
||||
return nil, errors.Errorf("Code position out of bounds: %d (len=%d)", pos, len(code))
|
||||
}
|
||||
|
||||
switch code[pos] {
|
||||
case 1:
|
||||
// addition
|
||||
p1, p2, pt := code[pos+1], code[pos+2], code[pos+3]
|
||||
code[pt] = code[p1] + code[p2]
|
||||
|
||||
case 2:
|
||||
// multiplication
|
||||
p1, p2, pt := code[pos+1], code[pos+2], code[pos+3]
|
||||
code[pt] = code[p1] * code[p2]
|
||||
|
||||
case 99:
|
||||
// program finished
|
||||
run = false
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("Encountered invalid operation %d", code[pos])
|
||||
}
|
||||
|
||||
pos += 4
|
||||
}
|
||||
|
||||
return code, nil
|
||||
return executeIntcode(code, nil, nil) // Day02 intcode may not contain I/O
|
||||
}
|
||||
|
||||
func solveDay2Part1(inFile string) (int, error) {
|
||||
|
|
72
day05.go
Normal file
72
day05.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
package aoc2019
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func solveDay5FromFile(inFile string, diagProgram int) (int, error) {
|
||||
raw, err := ioutil.ReadFile(inFile)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to read input")
|
||||
}
|
||||
|
||||
code, err := parseIntcode(strings.TrimSpace(string(raw)))
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "Unable to parse Intcode")
|
||||
}
|
||||
|
||||
var (
|
||||
in = make(chan int, 1)
|
||||
out = make(chan int, len(code)) // There cannot be more outputs than number of code parts
|
||||
outputs []int
|
||||
)
|
||||
|
||||
/*
|
||||
* The TEST diagnostic program will start by requesting from the user
|
||||
* the ID of the system to test by running an input instruction - provide
|
||||
* it 1, the ID for the ship's air conditioner unit.
|
||||
*/
|
||||
in <- diagProgram
|
||||
|
||||
if _, err := executeIntcode(code, in, out); err != nil {
|
||||
return 0, errors.Wrap(err, "Program execution failed")
|
||||
}
|
||||
|
||||
for o := range out {
|
||||
outputs = append(outputs, o)
|
||||
}
|
||||
|
||||
if len(outputs) < 1 {
|
||||
return 0, errors.New("Program did not yield any output")
|
||||
}
|
||||
|
||||
for i, check := range outputs[:len(outputs)-1] {
|
||||
if check != 0 {
|
||||
return 0, errors.Errorf("Non-zero check result in position %d: %d", i, check)
|
||||
}
|
||||
}
|
||||
|
||||
return outputs[len(outputs)-1], nil
|
||||
}
|
||||
|
||||
func solveDay5Part1(inFile string) (int, error) {
|
||||
/*
|
||||
* The TEST diagnostic program will start by requesting from the user
|
||||
* the ID of the system to test by running an input instruction - provide
|
||||
* it 1, the ID for the ship's air conditioner unit.
|
||||
*/
|
||||
return solveDay5FromFile(inFile, 1)
|
||||
}
|
||||
|
||||
func solveDay5Part2(inFile string) (int, error) {
|
||||
/*
|
||||
* This time, when the TEST diagnostic program runs its input
|
||||
* instruction to get the ID of the system to test, provide it 5,
|
||||
* the ID for the ship's thermal radiator controller. This
|
||||
* diagnostic test suite only outputs one number, the diagnostic code.
|
||||
*/
|
||||
return solveDay5FromFile(inFile, 5)
|
||||
}
|
1
day05_input.txt
Normal file
1
day05_input.txt
Normal file
|
@ -0,0 +1 @@
|
|||
3,225,1,225,6,6,1100,1,238,225,104,0,1002,92,42,224,1001,224,-3444,224,4,224,102,8,223,223,101,4,224,224,1,224,223,223,1102,24,81,225,1101,89,36,224,101,-125,224,224,4,224,102,8,223,223,101,5,224,224,1,224,223,223,2,118,191,224,101,-880,224,224,4,224,1002,223,8,223,1001,224,7,224,1,224,223,223,1102,68,94,225,1101,85,91,225,1102,91,82,225,1102,85,77,224,101,-6545,224,224,4,224,1002,223,8,223,101,7,224,224,1,223,224,223,1101,84,20,225,102,41,36,224,101,-3321,224,224,4,224,1002,223,8,223,101,7,224,224,1,223,224,223,1,188,88,224,101,-183,224,224,4,224,1002,223,8,223,1001,224,7,224,1,224,223,223,1001,84,43,224,1001,224,-137,224,4,224,102,8,223,223,101,4,224,224,1,224,223,223,1102,71,92,225,1101,44,50,225,1102,29,47,225,101,7,195,224,101,-36,224,224,4,224,102,8,223,223,101,6,224,224,1,223,224,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,677,677,224,1002,223,2,223,1006,224,329,1001,223,1,223,1108,226,677,224,102,2,223,223,1006,224,344,101,1,223,223,1107,226,226,224,1002,223,2,223,1006,224,359,101,1,223,223,8,677,226,224,1002,223,2,223,1006,224,374,1001,223,1,223,1107,677,226,224,102,2,223,223,1005,224,389,1001,223,1,223,1008,677,677,224,1002,223,2,223,1006,224,404,1001,223,1,223,108,677,677,224,102,2,223,223,1005,224,419,1001,223,1,223,1107,226,677,224,102,2,223,223,1006,224,434,101,1,223,223,1008,226,226,224,1002,223,2,223,1006,224,449,1001,223,1,223,107,226,226,224,102,2,223,223,1006,224,464,1001,223,1,223,1007,677,226,224,1002,223,2,223,1006,224,479,1001,223,1,223,1108,226,226,224,102,2,223,223,1006,224,494,1001,223,1,223,8,226,226,224,1002,223,2,223,1005,224,509,1001,223,1,223,7,226,677,224,102,2,223,223,1005,224,524,101,1,223,223,1008,677,226,224,102,2,223,223,1005,224,539,101,1,223,223,107,226,677,224,1002,223,2,223,1006,224,554,1001,223,1,223,1108,677,226,224,102,2,223,223,1005,224,569,101,1,223,223,108,226,226,224,1002,223,2,223,1005,224,584,1001,223,1,223,7,677,226,224,1002,223,2,223,1005,224,599,1001,223,1,223,108,226,677,224,1002,223,2,223,1006,224,614,101,1,223,223,1007,677,677,224,1002,223,2,223,1006,224,629,101,1,223,223,7,677,677,224,102,2,223,223,1005,224,644,101,1,223,223,1007,226,226,224,1002,223,2,223,1006,224,659,1001,223,1,223,8,226,677,224,102,2,223,223,1005,224,674,1001,223,1,223,4,223,99,226
|
21
day05_test.go
Normal file
21
day05_test.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package aoc2019
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestCalculateDay5_Part1(t *testing.T) {
|
||||
result, err := solveDay5Part1("day05_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 5 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 5 Part 1: %d", result)
|
||||
}
|
||||
|
||||
func TestCalculateDay5_Part2(t *testing.T) {
|
||||
count, err := solveDay5Part2("day05_input.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Day 5 solver failed: %s", err)
|
||||
}
|
||||
|
||||
t.Logf("Solution Day 5 Part 2: %d", count)
|
||||
}
|
166
intcode.go
Normal file
166
intcode.go
Normal file
|
@ -0,0 +1,166 @@
|
|||
package aoc2019
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type opCodeFlag int
|
||||
|
||||
const (
|
||||
opCodeFlagPosition opCodeFlag = iota
|
||||
opCodeFlagImmediate
|
||||
)
|
||||
|
||||
type opCodeType int
|
||||
|
||||
const (
|
||||
opCodeTypeAddition opCodeType = 1 // Day 02
|
||||
opCodeTypeMultiplication opCodeType = 2 // Day 02
|
||||
opCodeTypeInput opCodeType = 3 // Day 05 P1
|
||||
opCodeTypeOutput opCodeType = 4 // Day 05 P1
|
||||
opCodeTypeJumpIfTrue opCodeType = 5 // Day 05 P2
|
||||
opCodeTypeJumpIfFalse opCodeType = 6 // Day 05 P2
|
||||
opCodeTypeLessThan opCodeType = 7 // Day 05 P2
|
||||
opCodeTypeEquals opCodeType = 8 // Day 05 P2
|
||||
opCodeTypeExit opCodeType = 99 // Day 02
|
||||
)
|
||||
|
||||
type opCode struct {
|
||||
Type opCodeType
|
||||
flags []opCodeFlag
|
||||
}
|
||||
|
||||
func (o opCode) GetFlag(param int) opCodeFlag {
|
||||
if param-1 >= len(o.flags) {
|
||||
return opCodeFlagPosition
|
||||
}
|
||||
return o.flags[param-1]
|
||||
}
|
||||
|
||||
func (o opCode) eq(in opCode) bool {
|
||||
return o.Type == in.Type && reflect.DeepEqual(o.flags, in.flags)
|
||||
}
|
||||
|
||||
func parseOpCode(in int) opCode {
|
||||
out := opCode{}
|
||||
|
||||
out.Type = opCodeType(in % 100)
|
||||
|
||||
var paramFactor = 100
|
||||
for {
|
||||
if in < paramFactor {
|
||||
break
|
||||
}
|
||||
|
||||
out.flags = append(out.flags, opCodeFlag((in % (paramFactor * 10) / paramFactor)))
|
||||
paramFactor *= 10
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func parseIntcode(code string) ([]int, error) {
|
||||
parts := strings.Split(code, ",")
|
||||
|
||||
var out []int
|
||||
for _, n := range parts {
|
||||
v, err := strconv.Atoi(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, v)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func executeIntcode(code []int, in, out chan int) ([]int, error) {
|
||||
var pos int
|
||||
|
||||
if out != nil {
|
||||
defer close(out)
|
||||
}
|
||||
|
||||
getParamValue := func(param int, op opCode) int {
|
||||
switch op.GetFlag(param) {
|
||||
|
||||
case opCodeFlagImmediate:
|
||||
return code[pos+param]
|
||||
|
||||
case opCodeFlagPosition:
|
||||
return code[code[pos+param]]
|
||||
|
||||
default:
|
||||
panic(errors.Errorf("Unexpected opCodeFlag %d", op.GetFlag(param)))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if pos >= len(code) {
|
||||
return nil, errors.Errorf("Code position out of bounds: %d (len=%d)", pos, len(code))
|
||||
}
|
||||
|
||||
// Position is expected to be an OpCode
|
||||
op := parseOpCode(code[pos])
|
||||
switch op.Type {
|
||||
|
||||
case opCodeTypeAddition: // p1 + p2 => p3
|
||||
code[code[pos+3]] = getParamValue(1, op) + getParamValue(2, op)
|
||||
pos += 4
|
||||
|
||||
case opCodeTypeMultiplication: // p1 * p2 => p3
|
||||
code[code[pos+3]] = getParamValue(1, op) * getParamValue(2, op)
|
||||
pos += 4
|
||||
|
||||
case opCodeTypeInput: // in => p1
|
||||
code[code[pos+1]] = <-in
|
||||
pos += 2
|
||||
|
||||
case opCodeTypeOutput: // p1 => out
|
||||
out <- getParamValue(1, op)
|
||||
pos += 2
|
||||
|
||||
case opCodeTypeJumpIfTrue: // p1 != 0 => jmp
|
||||
if getParamValue(1, op) != 0 {
|
||||
pos = getParamValue(2, op)
|
||||
continue
|
||||
}
|
||||
pos += 3
|
||||
|
||||
case opCodeTypeJumpIfFalse: // p1 == 0 => jmp
|
||||
if getParamValue(1, op) == 0 {
|
||||
pos = getParamValue(2, op)
|
||||
continue
|
||||
}
|
||||
pos += 3
|
||||
|
||||
case opCodeTypeLessThan: // p1 < p2 => p3
|
||||
var res int
|
||||
if getParamValue(1, op) < getParamValue(2, op) {
|
||||
res = 1
|
||||
}
|
||||
code[code[pos+3]] = res
|
||||
pos += 4
|
||||
|
||||
case opCodeTypeEquals: // p1 == p2 => p3
|
||||
var res int
|
||||
if getParamValue(1, op) == getParamValue(2, op) {
|
||||
res = 1
|
||||
}
|
||||
code[code[pos+3]] = res
|
||||
pos += 4
|
||||
|
||||
case opCodeTypeExit: // exit
|
||||
return code, nil
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("Encountered invalid operation %d (parsed %#v)", code[pos], op)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
152
intcode_test.go
Normal file
152
intcode_test.go
Normal file
|
@ -0,0 +1,152 @@
|
|||
package aoc2019
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestParseOpCode(t *testing.T) {
|
||||
for code, expOpCode := range map[int]opCode{
|
||||
1002: {Type: opCodeTypeMultiplication, flags: []opCodeFlag{opCodeFlagPosition, opCodeFlagImmediate}},
|
||||
1101: {Type: opCodeTypeAddition, flags: []opCodeFlag{opCodeFlagImmediate, opCodeFlagImmediate}},
|
||||
} {
|
||||
if op := parseOpCode(code); !op.eq(expOpCode) {
|
||||
t.Errorf("OpCode execution of code %d yield unexpected result: exp=%+v got=%+v", code, expOpCode, op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteIntcodeIO(t *testing.T) {
|
||||
code, _ := parseIntcode("3,0,4,0,99")
|
||||
|
||||
var (
|
||||
exp = 25
|
||||
in = make(chan int, 1)
|
||||
out = make(chan int, 1)
|
||||
)
|
||||
|
||||
in <- exp
|
||||
|
||||
if _, err := executeIntcode(code, in, out); err != nil {
|
||||
t.Fatalf("Intcode execution failed: %s", err)
|
||||
}
|
||||
|
||||
if r := <-out; r != exp {
|
||||
t.Errorf("Program yield unexpected result: exp=%d got=%d", exp, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteIntcodeImmediateFlag(t *testing.T) {
|
||||
// 102,4,7,0 = Multiply 4 by pos_7, store to pos_0
|
||||
// 4,0 = Output pos_0
|
||||
// 99 = Exit
|
||||
// 3 = pos_7
|
||||
code, _ := parseIntcode("102,4,7,0,4,0,99,3")
|
||||
|
||||
var (
|
||||
exp = 12
|
||||
out = make(chan int, 1)
|
||||
)
|
||||
|
||||
if _, err := executeIntcode(code, nil, out); err != nil {
|
||||
t.Fatalf("Intcode execution failed: %s", err)
|
||||
}
|
||||
|
||||
if r := <-out; r != exp {
|
||||
t.Errorf("Program yield unexpected result: exp=%d got=%d", exp, r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteIntcodeEquals(t *testing.T) {
|
||||
for mode, codeStr := range map[string]string{
|
||||
"position": "3,9,8,9,10,9,4,9,99,-1,8",
|
||||
"immediate": "3,3,1108,-1,8,3,4,3,99",
|
||||
} {
|
||||
|
||||
for input, exp := range map[int]int{
|
||||
1: 0,
|
||||
8: 1,
|
||||
20: 0,
|
||||
-8: 0,
|
||||
} {
|
||||
var (
|
||||
in = make(chan int, 1)
|
||||
out = make(chan int, 10)
|
||||
)
|
||||
|
||||
code, _ := parseIntcode(codeStr)
|
||||
in <- input
|
||||
|
||||
if _, err := executeIntcode(code, in, out); err != nil {
|
||||
t.Fatalf("Execute in mode %q with input %d caused an error: %s", mode, input, err)
|
||||
}
|
||||
|
||||
if r := <-out; r != exp {
|
||||
t.Errorf("Execute in mode %q with input %d yield unexpected result: exp=%d got=%d", mode, input, exp, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteIntcodeLessThan(t *testing.T) {
|
||||
for mode, codeStr := range map[string]string{
|
||||
"position": "3,9,7,9,10,9,4,9,99,-1,8",
|
||||
"immediate": "3,3,1107,-1,8,3,4,3,99",
|
||||
} {
|
||||
|
||||
for input, exp := range map[int]int{
|
||||
1: 1,
|
||||
8: 0,
|
||||
20: 0,
|
||||
-8: 1,
|
||||
} {
|
||||
var (
|
||||
in = make(chan int, 1)
|
||||
out = make(chan int, 10)
|
||||
)
|
||||
|
||||
code, _ := parseIntcode(codeStr)
|
||||
in <- input
|
||||
|
||||
if _, err := executeIntcode(code, in, out); err != nil {
|
||||
t.Fatalf("Execute in mode %q with input %d caused an error: %s", mode, input, err)
|
||||
}
|
||||
|
||||
if r := <-out; r != exp {
|
||||
t.Errorf("Execute in mode %q with input %d yield unexpected result: exp=%d got=%d", mode, input, exp, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteIntcodeJump(t *testing.T) {
|
||||
for mode, codeStr := range map[string]string{
|
||||
"position": "3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9",
|
||||
"immediate": "3,3,1105,-1,9,1101,0,0,12,4,12,99,1",
|
||||
} {
|
||||
|
||||
for input, exp := range map[int]int{
|
||||
5: 1,
|
||||
0: 0,
|
||||
} {
|
||||
var (
|
||||
in = make(chan int, 1)
|
||||
out = make(chan int, 10)
|
||||
)
|
||||
|
||||
code, _ := parseIntcode(codeStr)
|
||||
in <- input
|
||||
|
||||
if _, err := executeIntcode(code, in, out); err != nil {
|
||||
t.Fatalf("Execute in mode %q with input %d caused an error: %s", mode, input, err)
|
||||
}
|
||||
|
||||
if r := <-out; r != exp {
|
||||
t.Errorf("Execute in mode %q with input %d yield unexpected result: exp=%d got=%d", mode, input, exp, r)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue