126 lines
2.6 KiB
Go
126 lines
2.6 KiB
Go
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)
|
|
}
|