Files
aoc2020/eight/day_eight.go
James Griffin a2cfad03b6 Day 8: Part 1 and 2
Signed-off-by: James Griffin <james@unsupervised.ca>
2020-12-08 20:44:39 -04:00

130 lines
2.3 KiB
Go

package eight
import (
"bufio"
"fmt"
"os"
)
type program struct {
instructions []instruction
accumulator int
preempted bool
}
type instruction struct {
operation string
argument int
executed bool
}
func (p *program) load(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
var op string
var value int
count, err := fmt.Sscanf(scanner.Text(), "%s %d", &op, &value)
if count != 2 || err != nil {
return fmt.Errorf("Unable to parse %s: %w", scanner.Text(), err)
}
p.instructions = append(p.instructions, instruction{
operation: op,
argument: value,
executed: false,
})
}
return nil
}
func (p *program) exec() int {
i := 0
for {
if i >= len(p.instructions) {
p.preempted = false
return p.accumulator
} else if i < 0 {
return p.accumulator
}
if p.instructions[i].executed {
break
}
p.instructions[i].executed = true
switch p.instructions[i].operation {
case "acc":
p.accumulator += p.instructions[i].argument
i++
case "jmp":
i += p.instructions[i].argument
default:
i++
}
}
return p.accumulator
}
func (p *program) reset() {
p.accumulator = 0
p.preempted = true
for i := 0; i < len(p.instructions); i++ {
p.instructions[i].executed = false
}
}
func (p *program) flipJmpNop(index int) {
if index < 0 || index > len(p.instructions) {
return
}
switch p.instructions[index].operation {
case "nop":
p.instructions[index].operation = "jmp"
case "jmp":
p.instructions[index].operation = "nop"
}
}
func (p *program) heal() int {
for i := 0; i < len(p.instructions); i++ {
p.reset()
p.flipJmpNop(i)
p.exec()
if !p.preempted {
return i
}
p.flipJmpNop(i)
}
return -1
}
// PartOne Find the value of the accumulator before any instruction is executed twice
func PartOne() string {
p := program{}
p.load("eight/handheld.code")
return fmt.Sprintf("The accumulator is at %d before the loop begins", p.exec())
}
// PartTwo Find the corrupt instruction, flip it, and execute to termination
func PartTwo() string {
p := program{}
p.load("eight/handheld.code")
p.exec()
index := p.heal()
return fmt.Sprintf("The accumulator is at %d after termination by correcting instruction %d", p.accumulator, index+1)
}