1
0
Fork 0
mirror of https://github.com/Luzifer/aoc2019.git synced 2024-10-18 11:14:19 +00:00

Add solution for Day 5

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2019-12-05 16:53:40 +01:00
parent 588a27e37d
commit 658829657b
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
8 changed files with 421 additions and 48 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/cover
day03_debug.png day03_debug.png

6
Makefile Normal file
View 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

View file

@ -2,61 +2,15 @@ package aoc2019
import ( import (
"io/ioutil" "io/ioutil"
"strconv"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
func parseDay02Intcode(code string) ([]int, error) { func parseDay02Intcode(code string) ([]int, error) { return parseIntcode(code) }
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 executeDay02Intcode(code []int) ([]int, error) { func executeDay02Intcode(code []int) ([]int, error) {
var ( return executeIntcode(code, nil, nil) // Day02 intcode may not contain I/O
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
} }
func solveDay2Part1(inFile string) (int, error) { func solveDay2Part1(inFile string) (int, error) {

72
day05.go Normal file
View 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
View 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
View 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
View 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
View 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)
}
}
}
}