package main import ( "bufio" "fmt" "os" "strings" "time" "github.com/fatih/color" ) type ArgumentType int const ( ArgumentTypeNone ArgumentType = iota ArgumentTypeRegister ArgumentTypeValue ) type Opcode struct { Name string A ArgumentType B ArgumentType Exe func(a, b int) int } var opcodes = []Opcode{ { Name: "banr", A: ArgumentTypeRegister, B: ArgumentTypeRegister, Exe: ban, }, { Name: "eqrr", A: ArgumentTypeRegister, B: ArgumentTypeRegister, Exe: eq, }, { Name: "setr", A: ArgumentTypeRegister, B: ArgumentTypeNone, Exe: set, }, { Name: "eqir", A: ArgumentTypeValue, B: ArgumentTypeRegister, Exe: eq, }, { Name: "bori", A: ArgumentTypeRegister, B: ArgumentTypeValue, Exe: bor, }, { Name: "muli", A: ArgumentTypeRegister, B: ArgumentTypeValue, Exe: mul, }, { Name: "bani", A: ArgumentTypeRegister, B: ArgumentTypeValue, Exe: ban, }, { Name: "borr", A: ArgumentTypeRegister, B: ArgumentTypeRegister, Exe: bor, }, { Name: "gtir", A: ArgumentTypeValue, B: ArgumentTypeRegister, Exe: gt, }, { Name: "gtrr", A: ArgumentTypeRegister, B: ArgumentTypeRegister, Exe: gt, }, { Name: "addi", A: ArgumentTypeRegister, B: ArgumentTypeValue, Exe: add, }, { Name: "gtri", A: ArgumentTypeRegister, B: ArgumentTypeValue, Exe: gt, }, { Name: "eqri", A: ArgumentTypeRegister, B: ArgumentTypeValue, Exe: eq, }, { Name: "addr", A: ArgumentTypeRegister, B: ArgumentTypeRegister, Exe: add, }, { Name: "mulr", A: ArgumentTypeRegister, B: ArgumentTypeRegister, Exe: mul, }, { Name: "seti", A: ArgumentTypeValue, B: ArgumentTypeNone, Exe: set, }, } func add(a, b int) int { return a + b } func mul(a, b int) int { return a * b } func ban(a, b int) int { return a & b } func bor(a, b int) int { return a | b } func set(a, b int) int { return a } func gt(a, b int) int { if a > b { return 1 } return 0 } func eq(a, b int) int { if a == b { return 1 } return 0 } func waitEnter() { fmt.Printf("Press ENTER to continue\n") bufio.NewReader(os.Stdin).ReadBytes('\n') } type Test struct { Before [4]int Op [4]int After [4]int } func sliceEqual(a, b []int) bool { if len(a) != len(b) { return false } for t := range a { if a[t] != b[t] { return false } } return true } type Puzzle struct { Regs [6]int ROM []int IP int IPreg int Ticks int LastResult [][6]int Isljmp []int // Instructions since last jump PrevIsljmp []int LoopCount int LastJumpFrom int LastJumpTo int PotentialLoopFrom int PotentialLoopTo int } func (p *Puzzle) RunProgram() { p.PrevIsljmp = make([]int, 0, 10) p.Isljmp = make([]int, 0, 10) p.LastResult = make([][6]int, len(p.ROM)/4) p.LastJumpFrom = -1 p.PrintState() waitEnter() start := time.Now() ticker := time.Tick(time.Second) seen := make(map[int]struct{}) ordered_seen := make([]int, 0, 1000) for { elapsed := time.Now().Sub(start) select { case <-ticker: fmt.Println(elapsed) default: } if p.IP < 0 || p.IP*4 >= len(p.ROM) { color.New(color.BgRed).Add(color.FgHiWhite).Println("PANIC! Access OOB @", p.IP, "after", p.Ticks, "iterations") return } if p.IP == 28 { if _, ok := seen[p.Regs[4]]; ok { p.PrintState() fmt.Println("DUPE!", p.Regs[4]) fmt.Println("Seen:", len(ordered_seen)) fmt.Println("First:", ordered_seen[0]) fmt.Println("Last:", ordered_seen[len(ordered_seen)-1]) return } seen[p.Regs[4]] = struct{}{} ordered_seen = append(ordered_seen, p.Regs[4]) } p.Regs[p.IPreg] = p.IP op := p.ROM[p.IP*4 : (p.IP*4)+4] o := opcodes[op[0]] a := op[1] b := op[2] c := op[3] if o.A == ArgumentTypeRegister { a = p.Regs[a] } if o.B == ArgumentTypeRegister { b = p.Regs[b] } // Detect loops if c == p.IPreg { p.LastJumpFrom = p.IP } else { p.Isljmp = append(p.Isljmp, op[0]) } p.Regs[c] = o.Exe(a, b) p.IP = p.Regs[p.IPreg] copy(p.LastResult[p.IP][:], p.Regs[:]) p.IP++ p.Ticks++ if c == p.IPreg { p.LastJumpTo = p.IP if p.IP >= p.PotentialLoopTo && p.IP <= p.PotentialLoopFrom { if p.IP == p.PotentialLoopTo { if sliceEqual(p.Isljmp, p.PrevIsljmp) { p.LoopCount++ } else { p.LoopCount = 0 } p.PrevIsljmp = p.Isljmp p.Isljmp = make([]int, 0, 10) } } else { p.PotentialLoopFrom = p.LastJumpFrom p.PotentialLoopTo = p.LastJumpTo p.PrevIsljmp = p.Isljmp p.Isljmp = make([]int, 0, 10) } } } } func (p *Puzzle) PrintState() { fmt.Printf("\033c") color.New(color.FgWhite).Printf("IP: ") color.New(color.FgHiWhite).Printf("%2d", p.IP) color.New(color.FgWhite).Printf("\tIPreg: ") color.New(color.FgHiCyan).Printf("r%d", p.IPreg) color.New(color.FgWhite).Printf("\tTicks: ") color.New(color.FgHiWhite).Printf("%d\n", p.Ticks) color.New(color.FgWhite).Printf("Registers: ") fmt.Printf("[%d, %d, %d, %d, %d, %d]\n", p.Regs[0], p.Regs[1], p.Regs[2], p.Regs[3], p.Regs[4], p.Regs[5]) color.New(color.FgWhite).Printf("%s\n", strings.Repeat("-", 93)) for t := 0; t < len(p.ROM); t += 4 { i := t / 4 o := opcodes[p.ROM[t]] gutterColor := color.New(color.FgWhite) normalColor := color.New(color.FgHiWhite) registerColor := color.New(color.FgHiCyan) if i == p.IP { gutterColor = color.New(color.FgHiWhite).Add(color.BgBlue) normalColor.Add(color.BgBlue) registerColor = color.New(color.FgHiCyan).Add(color.BgBlue) } gutterColor.Printf("%02d|", i) normalColor.Printf(" %s", o.Name) if o.A == ArgumentTypeRegister { registerColor.Printf(" % 6sr%d", " ", p.ROM[t+1]) } else { normalColor.Printf(" %8d", p.ROM[t+1]) } if o.B == ArgumentTypeRegister { registerColor.Printf(" % 6sr%d", " ", p.ROM[t+2]) } else { normalColor.Printf(" %8d", p.ROM[t+2]) } registerColor.Printf(" % 6sr%d", " ", p.ROM[t+3]) regs := make([]string, len(p.Regs)) for t, r := range p.LastResult[i] { if r > 100000000 { regs[t] += ">9999999" } else { regs[t] += fmt.Sprintf("%8d", r) } } gutterColor.Printf(" = [%s]", strings.Join(regs, ",")) if p.LastJumpFrom > -1 { if i >= p.PotentialLoopTo && i <= p.PotentialLoopFrom { if i == p.PotentialLoopTo { registerColor.Printf(" *") } if i > p.PotentialLoopTo && i < p.PotentialLoopFrom { registerColor.Printf(" |") } if i == p.PotentialLoopFrom { registerColor.Printf(" ^") } } if i == p.LastJumpTo { gutterColor.Printf(" *") } if (i > p.LastJumpTo && i < p.LastJumpFrom) || (i < p.LastJumpTo && i > p.LastJumpFrom) { gutterColor.Printf(" |") } if i == p.LastJumpFrom { if p.LastJumpFrom < p.LastJumpTo { gutterColor.Printf(" v") } else { gutterColor.Printf(" ^") } gutterColor.Printf(" jump") } if i == p.PotentialLoopTo { if p.LoopCount > 0 { registerColor.Printf(" loop") } } if i == p.PotentialLoopFrom { if p.LoopCount > 0 { registerColor.Printf(" iteration %d", p.LoopCount) } } /* if i == p.IP { if (p.IP > p.LastJumpTo && p.IP < p.LastJumpFrom) || (p.IP < p.LastJumpTo && p.IP > p.LastJumpFrom) { inst := make([]string, len(p.Isljmp)) for t, o := range p.Isljmp { inst[t] = opcodes[o].Name } gutterColor.Printf(" [%s]", strings.Join(inst, ",")) } } */ } fmt.Printf("\n") } } func main() { p := Puzzle{} input, err := os.Open("input.txt") if err != nil { panic(err.Error()) } scanner := bufio.NewScanner(input) for scanner.Scan() { var op string var a, b, c int fmt.Sscanf(scanner.Text(), "%s %d %d %d", &op, &a, &b, &c) if op == "#ip" { p.IPreg = a continue } for k, o := range opcodes { if o.Name == op { p.ROM = append(p.ROM, k, a, b, c) break } } } if err := scanner.Err(); err != nil { panic(err.Error()) } input.Close() p.RunProgram() }