From 249e322a7fc1b1e0b5275f27a5b488c747d7c61b Mon Sep 17 00:00:00 2001 From: janreggie Date: Sun, 29 Mar 2020 16:11:45 +0800 Subject: [PATCH] Y2016D12 --- aoc2016/day12.go | 251 ++++++++++++++++++++++++++++++++++++++++++ aoc2016/day12_test.go | 22 ++++ aoc2016/init.go | 2 +- inputs/2016_12.txt | 23 ++++ 4 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 aoc2016/day12.go create mode 100644 aoc2016/day12_test.go create mode 100644 inputs/2016_12.txt diff --git a/aoc2016/day12.go b/aoc2016/day12.go new file mode 100644 index 0000000..cb9fb94 --- /dev/null +++ b/aoc2016/day12.go @@ -0,0 +1,251 @@ +package aoc2016 + +import ( + "bufio" + "fmt" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +// assembunnyComputer is a computer that could run an assembunny program. +// The zero value is ready to use. +type assembunnyComputer struct { + registers struct { + a int + b int + c int + d int + } + + // instructionPointer represents which instruction to use + instructionPointer int +} + +// Get gets the value of some register and some error +func (asm assembunnyComputer) Get(register string) (int, error) { + switch register { + case "a": + return asm.registers.a, nil + case "b": + return asm.registers.b, nil + case "c": + return asm.registers.c, nil + case "d": + return asm.registers.d, nil + } + return 0, fmt.Errorf("could not get invalid register %v", register) +} + +// Set sets the value of some register and returns an error +func (asm *assembunnyComputer) Set(register string, value int) error { + switch register { + case "a": + asm.registers.a = value + return nil + case "b": + asm.registers.b = value + return nil + case "c": + asm.registers.c = value + return nil + case "d": + asm.registers.d = value + return nil + } + return fmt.Errorf("could not set invalid register %v", register) +} + +// GetInstructionPointer gets the instruction pointer +func (asm *assembunnyComputer) GetInstructionPointer() int { + return asm.instructionPointer +} + +// SetInstructionPointer sets the instruction pointer +func (asm *assembunnyComputer) SetInstructionPointer(ip int) { + asm.instructionPointer = ip +} + +// inc increments register x by one and increments the instruction pointer. +func (asm *assembunnyComputer) inc(x string) error { + v, err := asm.Get(x) + if err != nil { + return errors.Wrapf(err, "could not increment register %v", x) + } + asm.instructionPointer++ + return asm.Set(x, v+1) +} + +// dec decrements register x by one and increments the instruction pointer. +func (asm *assembunnyComputer) dec(x string) error { + v, err := asm.Get(x) + if err != nil { + return errors.Wrapf(err, "could not decrement register %v", x) + } + asm.instructionPointer++ + return asm.Set(x, v-1) +} + +// cpy copies x into register y and increments the instruction pointer. +// x can be either an integer or the value of a register. +func (asm *assembunnyComputer) cpy(x, y string) error { + v, err := strconv.Atoi(x) + if err != nil { + // it's a register + v, err = asm.Get(x) + if err != nil { + return errors.Wrapf(err, "could not read register %v", x) + } + } + if err = asm.Set(y, v); err != nil { + return errors.Wrapf(err, "could not copy value %v to register %v", v, y) + } + asm.instructionPointer++ + return nil +} + +// jnz jumps to an instruction y away only if x is not zero. +// If x is zero, iterate instruction pointer by 1. +func (asm *assembunnyComputer) jnz(x, y string) error { + v, err := strconv.Atoi(x) + if err != nil { + // it's a register + v, err = asm.Get(x) + if err != nil { + return errors.Wrapf(err, "could not read register %v", x) + } + } + if v == 0 { + asm.instructionPointer++ + return nil + } + j, err := strconv.Atoi(y) + if err != nil { + return errors.Wrapf(err, "could not parse %v as int", y) + } + asm.instructionPointer += j + return nil +} + +// readInstruction reads an instruction. +func (asm *assembunnyComputer) readInstruction(instr string) error { + if len(instr) < 5 { // e.g., "inc a" + return fmt.Errorf("%v too short", instr) + } + switch instr[0:4] { + case "cpy ": + args := strings.Fields(strings.TrimPrefix(instr, "cpy ")) + if len(args) != 2 { + return fmt.Errorf("arguments %v invalid for cpy", args) + } + if err := asm.cpy(args[0], args[1]); err != nil { + return errors.Wrapf(err, "could not complete instruction %v", instr) + } + return nil + + case "inc ": + args := strings.Fields(strings.TrimPrefix(instr, "inc ")) + if len(args) != 1 { + return fmt.Errorf("arguments %v invalid for inc", args) + } + if err := asm.inc(args[0]); err != nil { + return errors.Wrapf(err, "could not complete instruction %v", instr) + } + return nil + + case "dec ": + args := strings.Fields(strings.TrimPrefix(instr, "dec ")) + if len(args) != 1 { + return fmt.Errorf("arguments %v invalid for dec", args) + } + if err := asm.dec(args[0]); err != nil { + return errors.Wrapf(err, "could not complete instruction %v", instr) + } + return nil + + case "jnz ": + args := strings.Fields(strings.TrimPrefix(instr, "jnz ")) + if len(args) != 2 { + return fmt.Errorf("arguments %v invalid for jnz", args) + } + if err := asm.jnz(args[0], args[1]); err != nil { + return errors.Wrapf(err, "could not complete instruction %v", instr) + } + return nil + } + + return fmt.Errorf("unknown instruction %v", instr) +} + +// Reset resets the registers and instruction pointer to zero. +func (asm *assembunnyComputer) Reset() { + asm.registers = struct { + a int + b int + c int + d int + }{0, 0, 0, 0} + asm.SetInstructionPointer(0) +} + +// ReadMemory reads from a tape of memory represented by a slice of instructions. +// It does not reset the registers or the instruction pointer. +func (asm *assembunnyComputer) ReadMemory(memory []string) error { + for asm.GetInstructionPointer() < len(memory) && asm.GetInstructionPointer() >= 0 { + instr := memory[asm.GetInstructionPointer()] + if err := asm.readInstruction(instr); err != nil { + return errors.Wrapf(err, "could not parse instruction %v at pointer %v", instr, asm.GetInstructionPointer()) + } + } + return nil +} + +// Day12 solves the twelvth day puzzle "Leonardo's Monorail". +// +// Input +// +// A list of instructions written in assembunny. +// The number of instructions do not exceed 50. +// For example: +// +// cpy 41 a +// inc a +// inc a +// dec a +// jnz a 2 +// dec a +func Day12(scanner *bufio.Scanner) (answer1, answer2 string, err error) { + var asm assembunnyComputer + memory := make([]string, 0) + for scanner.Scan() { + memory = append(memory, scanner.Text()) + } + if err = asm.ReadMemory(memory); err != nil { + err = errors.Wrapf(err, "could not read memory %v", memory) + return + } + valA, err := asm.Get("a") + if err != nil { + err = errors.Wrapf(err, "could not get reg a") + return + } + answer1 = strconv.Itoa(valA) + + // but something's up + asm.Reset() + if err = asm.Set("c", 1); err != nil { + err = errors.Wrapf(err, "could not set reg a") + return + } + if err = asm.ReadMemory(memory); err != nil { + err = errors.Wrapf(err, "could not read memory %v", memory) + return + } + if valA, err = asm.Get("a"); err != nil { + err = errors.Wrapf(err, "could not get reg a") + return + } + answer2 = strconv.Itoa(valA) + return +} diff --git a/aoc2016/day12_test.go b/aoc2016/day12_test.go new file mode 100644 index 0000000..fa2615e --- /dev/null +++ b/aoc2016/day12_test.go @@ -0,0 +1,22 @@ +package aoc2016 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_assembunnyComputer_ReadMemory(t *testing.T) { + assert := assert.New(t) + mem := []string{"cpy 41 a", + "inc a", + "inc a", + "dec a", + "jnz a 2", + "dec a", + } + var asm assembunnyComputer + err := asm.ReadMemory(mem) + assert.NoError(err, mem, err) + assert.Equal(asm.registers.a, 42) +} diff --git a/aoc2016/init.go b/aoc2016/init.go index 686b656..56852cb 100644 --- a/aoc2016/init.go +++ b/aoc2016/init.go @@ -23,7 +23,7 @@ var AllSolutions = []func(*bufio.Scanner) (string, string, error){ Unimplemented, // day 9 Unimplemented, // day 10 Unimplemented, // day 11 - Unimplemented, // day 12 + Day12, // day 12 Unimplemented, // day 13 Unimplemented, // day 14 Unimplemented, // day 15 diff --git a/inputs/2016_12.txt b/inputs/2016_12.txt new file mode 100644 index 0000000..bb2a2ff --- /dev/null +++ b/inputs/2016_12.txt @@ -0,0 +1,23 @@ +cpy 1 a +cpy 1 b +cpy 26 d +jnz c 2 +jnz 1 5 +cpy 7 c +inc d +dec c +jnz c -2 +cpy a c +inc a +dec b +jnz b -2 +cpy c b +dec d +jnz d -6 +cpy 17 c +cpy 18 d +inc a +dec d +jnz d -2 +dec c +jnz c -5