130 lines
2.3 KiB
Go
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)
|
|
}
|