package main import ( "fmt" "io" "os" ) type Instruction struct { Type string Argument int } func (i *Instruction) String() string { return fmt.Sprintf("%s %+0d", i.Type, i.Argument) } type Code []*Instruction type Processor struct { Accumulator int Index int Code Code ExecutedInstructions []bool } func (p *Processor) LoadCode(code Code) { p.Code = code p.ExecutedInstructions = make([]bool, len(code)) p.Accumulator = 0 p.Index = 0 } func (p *Processor) RunCurrent() (dupe bool) { if p.EndOfCode() { panic("tried to run code past end") } instruction := p.Code[p.Index] switch instruction.Type { case "acc": p.Accumulator += instruction.Argument p.Index++ case "jmp": p.Index += instruction.Argument case "nop": p.Index++ default: panic("invalid instruction found: " + instruction.Type) } if !p.EndOfCode() { dupe = p.ExecutedInstructions[p.Index] p.ExecutedInstructions[p.Index] = true } return } func (p *Processor) EndOfCode() bool { return p.Index >= len(p.Code) } func (p *Processor) HasCodeLoaded() bool { return p.Code != nil } func main() { f, err := os.Open("input") if err != nil { panic(err) } // "compile" code code := Code{} for { instruction := new(Instruction) _, err := fmt.Fscanf(f, "%s %d\n", &instruction.Type, &instruction.Argument) if err == io.EOF { break } if err != nil { panic(err) } code = append(code, instruction) } // go through code and replace exactly one jmp->nop or nop->jmp processor := new(Processor) patching: for patchIndex := 0; patchIndex < len(code) && (!processor.HasCodeLoaded() || !processor.EndOfCode()); patchIndex++ { patchedCode := make(Code, len(code)) copy(patchedCode, code) switch patchedCode[patchIndex].Type { case "jmp": patchedCode[patchIndex] = &Instruction{ Type: "nop", Argument: patchedCode[patchIndex].Argument, } case "nop": patchedCode[patchIndex] = &Instruction{ Type: "jmp", Argument: patchedCode[patchIndex].Argument, } default: //fmt.Printf("Skipping instruction %d\n", patchIndex) continue patching } fmt.Printf("Patched instruction %d from %s to %s\n", patchIndex, code[patchIndex], patchedCode[patchIndex]) processor.LoadCode(patchedCode) for !processor.EndOfCode() { dupe := processor.RunCurrent() if dupe { fmt.Printf("=> Detected dupe run, accumulator is %d\n", processor.Accumulator) continue patching } } // end of code reached! fmt.Printf("=> Code terminated with accumulator %d\n", processor.Accumulator) break } fmt.Println("End of code reached") //spew.Dump(processor) }