package main import ( "fmt" "os" "strings" "time" termbox "github.com/nsf/termbox-go" "../../" ) var regs = map[string]int{ "a": 12, "b": 0, "c": 0, "d": 0, } var instructions []string var curr int var pause bool func main() { var debug bool var inpFn string var done bool if inpFn = aoc.GetArgNumber(1); inpFn == "" { done = true } if inpFn != "" { instructions = aoc.FileToStringSlice(inpFn) } if aoc.ArgIsSet("-d") { debug = true } if aoc.ArgIsSet("-p") { pause = true } err := termbox.Init() //tWidth, tHeight := termbox.Size() if err != nil { fmt.Println("Error initializing termbox") os.Exit(1) } defer termbox.Close() eventChan := make(chan termbox.Event) go readUserInput(eventChan) go sendNoneEvent(eventChan) curr = 0 var ins []string for curr < len(instructions) || done { if debug { lastInst := ins // print state and wait for user to step PrintState() fmt.Println("Last Instruction:", lastInst) //PrintInstructionState() fmt.Println("(q): quit (space): pause/unpause") ev := <-eventChan if ev.Type == termbox.EventKey { if ev.Key == termbox.KeySpace { pause = !pause } if ev.Ch == 'q' { break } } } ins = strings.Fields(instructions[curr]) curr++ // Optimize multiplication /* // TODO: > cpy b c inc a dec c jnz c -2 dec d jnz d -5 == mlt b d a and curr += 5 */ if ins[0] == "cpy" { if strings.HasPrefix(instructions[curr+1], "inc") && strings.HasPrefix(instructions[curr+2], "dec") && strings.HasPrefix(instructions[curr+3], "jnz") && strings.HasPrefix(instructions[curr+4], "dec") && strings.HasPrefix(instructions[curr+5], "jnz") { allGood := true // The right instruction set, make sure the arguments match up ins1 := strings.Fields(instructions[curr+1]) ins2 := strings.Fields(instructions[curr+2]) ins3 := strings.Fields(instructions[curr+3]) ins4 := strings.Fields(instructions[curr+4]) ins5 := strings.Fields(instructions[curr+5]) // Make sure all "c"s are correct if ins[2] != ins2[1] && ins[2] != ins3[1] { allGood = false } if allGood { // Make sure all "b"s are correct if ins[1] == ins1[1] || ins[1] == ins2[1] || ins[1] == ins4[1] { allGood = false } } if allGood { // Make sure all "d"s are correct if (ins[1] == ins4[1] || ins[2] == ins4[1]) || ins4[1] != ins5[1] { allGood = false } } if allGood { // Go ahead and change the instruction ins = []string{"mult", ins[1], ins4[1], ins1[1]} curr += 5 } } } switch ins[0] { case "jnz": // If we have a jnz c -2 it could be moving all of c into another register v, ok := regs[ins[1]] if !ok { v = aoc.Atoi(ins[1]) } var p int if p, ok = regs[ins[2]]; !ok { p = aoc.Atoi(ins[2]) } if v != 0 { // Subtract 1 from the jump because we incremented already curr += p - 1 } case "mlt": // a three arg instruction: mlt a b c // take a * b, store in c var ok bool src1 := ins[1] src2 := ins[2] dst := ins[3] if _, ok := regs[dst]; !ok { // invalid destination continue } var src1I, src2I int if src1I, ok = regs[src1]; !ok { src1I = aoc.Atoi(src1) } if src2I, ok = regs[src2]; !ok { src2I = aoc.Atoi(src2) } regs[dst] = src1I * src2I case "cpy": src := ins[1] dst := ins[2] // check if dst is a register if _, ok := regs[dst]; !ok { // Nope, move along continue } // check if the src is a register if v, ok := regs[src]; ok { regs[dst] = v } else { // It's not, must be an int regs[dst] = aoc.Atoi(src) } case "inc": if _, ok := regs[ins[1]]; !ok { continue } regs[ins[1]] = regs[ins[1]] + 1 case "dec": if _, ok := regs[ins[1]]; !ok { continue } regs[ins[1]] = regs[ins[1]] - 1 case "tgl": // tgl alters an instruction src := ins[1] var srcI int if v, ok := regs[src]; ok { srcI = v } else { srcI = aoc.Atoi(src) } srcI = curr + srcI if srcI < 0 || srcI > len(instructions)-1 { // Invalid instruction continue } altPts := strings.Fields(instructions[srcI-1]) if len(altPts) == 2 { // one argument if altPts[0] == "inc" { altPts[0] = "dec" } else { altPts[0] = "inc" } } else { // two arguments if altPts[0] == "jnz" { altPts[0] = "cpy" } else { altPts[0] = "jnz" } } instructions[srcI-1] = strings.Join(altPts, " ") } if debug { PrintState() } } PrintState() } // Fancy State Printing func PrintState() { fmt.Println(aoc.ClearScreen) PrintRegs() } func PrintRegs() { datLine := fmt.Sprint("\u2502 a:", regs["a"], " b:", regs["b"], " c:", regs["c"], " d:", regs["d"], " \u2502") fmt.Println("\u250C" + strings.Repeat("\u2500", len(datLine)-6) + "\u2510") fmt.Println(datLine) fmt.Println("\u2514" + strings.Repeat("\u2500", len(datLine)-6) + "\u2518") } func PrintInstructionState() { for pi := range instructions { if pi == curr { fmt.Print("> ") } else { fmt.Print(" ") } fmt.Println(instructions[pi]) } fmt.Println(instructions[curr]) } func readUserInput(e chan termbox.Event) { for { e <- termbox.PollEvent() } } func sendNoneEvent(e chan termbox.Event) { for { time.Sleep(time.Second / 32) if !pause { e <- termbox.Event{Type: termbox.EventNone} } } }