adventofcode-2020/day8/part2/main.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)
}