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) }