package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Instr ...
type Instr string
// ...
const (
InstrAcc Instr = "acc"
InstrJmp = "jmp"
InstrNop = "nop"
)
// Op ...
type Op struct {
Instr Instr
Val int
}
func (o Op) String() string {
return fmt.Sprintf("%s %+4d", o.Instr, o.Val)
}
// ElfCPU ...
type ElfCPU struct {
PC int
Acc int
Ops []Op
}
// Reset ...
func (e *ElfCPU) Reset() {
e.PC = 0
e.Acc = 0
}
// Step ...
func (e *ElfCPU) Step() bool {
if e.PC >= len(e.Ops) {
return false
}
op := e.Ops[e.PC]
switch op.Instr {
case InstrAcc:
e.Acc += op.Val
e.PC++
case InstrJmp:
e.PC += op.Val
case InstrNop:
e.PC++
default:
e.PC++
}
return true
}
// runCPU ...
func runCPU(elf *ElfCPU) (int, bool) {
seenPCs := make(map[int]struct{})
for {
if _, ok := seenPCs[elf.PC]; ok {
return elf.Acc, false
}
seenPCs[elf.PC] = struct{}{}
if !elf.Step() {
break
}
}
return elf.Acc, true
}
// Task1 ...
func Task1(elf *ElfCPU) int {
result, _ := runCPU(elf)
return result
}
// Task2 ...
func Task2(elf *ElfCPU) int {
type step struct {
PC int
Op Op
}
seenPCs := make(map[int]struct{})
seenOps := make([]step, 0, 100)
for {
if _, ok := seenPCs[elf.PC]; ok {
break
}
seenPCs[elf.PC] = struct{}{}
seenOps = append(seenOps, step{
PC: elf.PC,
Op: elf.Ops[elf.PC],
})
if !elf.Step() {
break
}
}
ops := make([]Op, len(elf.Ops))
copy(ops, elf.Ops)
// Search backwards, change a jmp or nop, test, and start over.
for i := len(seenOps) - 1; i >= 0; i-- {
copy(elf.Ops, ops)
elf.Reset()
var try bool
if seenOps[i].Op.Instr == InstrJmp {
elf.Ops[seenOps[i].PC].Instr = InstrNop
try = true
} else if seenOps[i].Op.Instr == InstrNop {
elf.Ops[seenOps[i].PC].Instr = InstrJmp
try = true
}
if try {
fmt.Printf("modify: %s -> %s", seenOps[i].Op, elf.Ops[seenOps[i].PC])
if acc, ok := runCPU(elf); ok {
fmt.Printf(" FOUND!\n")
return acc
}
fmt.Printf(" no dice\n")
}
}
return -1
}
func main() {
file, _ := os.Open("input.txt")
scanner := bufio.NewScanner(file)
elf := ElfCPU{
Ops: make([]Op, 0, 100),
}
for scanner.Scan() {
parts := strings.Split(scanner.Text(), " ")
val, _ := strconv.Atoi(parts[1])
elf.Ops = append(elf.Ops, Op{
Instr: Instr(parts[0]),
Val: val,
})
}
file.Close()
elf.Reset()
result := Task1(&elf)
fmt.Printf("Task 1: %d\n", result)
elf.Reset()
result = Task2(&elf)
fmt.Printf("Task 2: %d\n", result)
}