diff --git a/2016/day23/main.go b/2016/day23/main.go index e14c32d..8324875 100644 --- a/2016/day23/main.go +++ b/2016/day23/main.go @@ -3,6 +3,7 @@ package main import ( "fmt" "os" + "strconv" "strings" "time" @@ -23,6 +24,7 @@ var instructions, lastInst []string var curr, cursor int var pause, step, skip bool var breakpoints []int +var eventChan chan termbox.Event func main() { var debug bool @@ -49,12 +51,13 @@ func main() { } defer termbox.Close() - eventChan := make(chan termbox.Event) + eventChan = make(chan termbox.Event) go readUserInput(eventChan) go sendNoneEvent(eventChan) curr = 0 var ins []string for curr < len(instructions) || done { + isBreak := isBreakpoint(curr) if debug { lastInst = ins } @@ -62,68 +65,20 @@ func main() { if len(ins) == 0 { break } - // Optimize multiplication - /* - > cpy b c - inc a - dec c - jnz c -2 - dec d - jnz d -5 - == add (b * d) to a - set c & d to 0 - curr += 5 - */ - if ins[0] == "cpy" { - if len(instructions) >= curr+6 { - 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]) - if ins1[0] == "inc" && ins2[0] == "dec" && ins3[0] == "jnz" && ins4[0] == "dec" && ins5[0] == "jnz" { - allGood := true - if allGood { - // Do the multiplication - // ins[1] * ins4[1] - // add that value to ins1[1] - // set ins[2] to 0 - // set ins4[1] to 0 - // Then add 5 to the pc - src1 := ins[1] - src2 := ins4[1] - dst := ins1[1] - if _, ok := regs[dst]; !ok { - // Invalid destination register - allGood = false - } - if _, ok := regs[src1]; !ok { - // Invalid source register - allGood = false - } - if _, ok := regs[src2]; !ok { - allGood = false - } - if allGood { - regs[dst] += (regs[src1] * regs[src2]) - curr += 6 - skip = true - } - } - } - } + if isMultOp(curr) { + curr = doMultOp(curr) + skip = true } if debug { - if isBreakpoint(curr + 1) { + if isBreak { pause = true step = false } - for pause && !step { + for pause && !step && !done { // print state and wait for user to step PrintState() - fmt.Println("Last Instruction:", lastInst) PrintInstructionState() - fmt.Println("(q): quit (space): pause/unpause (s): step (b): toggle breakpoint") + PrintDebugHelp() ev := <-eventChan if ev.Type == termbox.EventKey { switch { @@ -135,6 +90,8 @@ func main() { toggleBreakpoint(cursor) case ev.Ch == 's': step = true + case ev.Ch == 'u': + updateRegister() case ev.Key == termbox.KeyArrowUp: if cursor > 0 { cursor-- @@ -268,6 +225,7 @@ func PrintRegs() { } func PrintInstructionState() { + optim := color.New(color.FgGreen) for pi := range instructions { if pi == curr { fmt.Print(">") @@ -281,12 +239,28 @@ func PrintInstructionState() { } if cursor == pi { cursor := color.New(color.FgBlack).Add(color.BgWhite) - cursor.Println(instructions[pi]) + if isMultOp(pi) { + cursor.Println("+ " + instructions[pi] + " == optimized") + } else if mo, _ := isInMultOp(pi); mo { + cursor.Println("| " + instructions[pi]) + } else { + cursor.Println(instructions[pi]) + } } else { - fmt.Println(instructions[pi]) + if isMultOp(pi) { + optim.Println("+ " + instructions[pi] + " == optimized") + } else if mo, _ := isInMultOp(pi); mo { + optim.Println("| " + instructions[pi]) + } else { + fmt.Println(instructions[pi]) + } } } - fmt.Println(instructions[curr]) +} + +func PrintDebugHelp() { + fmt.Println("(s): step | (space): pause/unpause | (b): toggle breakpoint") + fmt.Println("(u): update register to value | (q): quit") } func readUserInput(e chan termbox.Event) { @@ -304,7 +278,111 @@ func sendNoneEvent(e chan termbox.Event) { } } +// isInMultOp returns true and the pc for the +// start of the multiplication operation if pos is +// located within it +func isInMultOp(pos int) (bool, int) { + // We need to check instructions[pos +- 5] to see if any + // are a multiplication op and return the pos for the (first) one + // that is + if isMultOp(pos) { + return true, pos + } + for i := 1; i < 6; i++ { + if isMultOp(pos - i) { + return true, (pos - i) + } + } + return false, pos +} + +// isMultOpStart return whether pos is the start of +// what can be optimized as multiplication +/* + > cpy b c + inc a + dec c + jnz c -2 + dec d + jnz d -5 + == add (b * d) to a + set c & d to 0 + curr += 5 +*/ +func isMultOp(pos int) bool { + if pos < 0 || pos > len(instructions)-1 { + return false + } + ins := strings.Fields(instructions[pos]) + if len(ins) < 3 { + return false + } + if ins[0] == "cpy" { + if len(instructions) >= pos+6 { + ins1 := strings.Fields(instructions[pos+1]) + ins2 := strings.Fields(instructions[pos+2]) + ins3 := strings.Fields(instructions[pos+3]) + ins4 := strings.Fields(instructions[pos+4]) + ins5 := strings.Fields(instructions[pos+5]) + if ins1[0] == "inc" && ins2[0] == "dec" && ins3[0] == "jnz" && ins4[0] == "dec" && ins5[0] == "jnz" { + allGood := true + if allGood { + // Do the multiplication + // ins[1] * ins4[1] + // add that value to ins1[1] + // set ins[2] to 0 + // set ins4[1] to 0 + // Then add 5 to the pc + src1 := ins[1] + src2 := ins4[1] + dst := ins1[1] + if _, ok := regs[dst]; !ok { + // Invalid destination register + allGood = false + } + if _, ok := regs[src1]; !ok { + // Invalid source register + allGood = false + } + if _, ok := regs[src2]; !ok { + allGood = false + } + return allGood + } + } + } + } + return false +} + +// doMultOp performs a multiplcation operation and returns the new pc +func doMultOp(pos int) int { + isOp, pos := isInMultOp(pos) + if !isOp { + // No optimization to do + return pos + } + ins := strings.Fields(instructions[pos]) + ins1 := strings.Fields(instructions[pos+1]) + ins4 := strings.Fields(instructions[pos+4]) + // Do the multiplication + // ins[1] * ins4[1] + // add that value to ins1[1] + // set ins[2] to 0 + // set ins4[1] to 0 + // Then add 5 to the pc + src1 := ins[1] + src2 := ins4[1] + dst := ins1[1] + regs[dst] += (regs[src1] * regs[src2]) + regs[ins[2]] = 0 + regs[ins4[1]] = 0 + pos += 6 + return pos +} + func toggleBreakpoint(pos int) { + _, pos = isInMultOp(pos) for i := 0; i < len(breakpoints); i++ { if breakpoints[i] == pos { breakpoints = append(breakpoints[:i], breakpoints[i+1:]...) @@ -315,6 +393,7 @@ func toggleBreakpoint(pos int) { } func isBreakpoint(pos int) bool { + _, pos = isInMultOp(pos) for i := 0; i < len(breakpoints); i++ { if breakpoints[i] == pos { return true @@ -322,3 +401,48 @@ func isBreakpoint(pos int) bool { } return false } + +func updateRegister() { + var updReg string + fmt.Println("Update which register? (a,b,c,d)") + ev := <-eventChan + if ev.Type == termbox.EventKey { + switch { + case ev.Ch == 'a': + updReg = "a" + case ev.Ch == 'b': + updReg = "b" + case ev.Ch == 'c': + updReg = "c" + case ev.Ch == 'd': + updReg = "d" + default: + fmt.Println("Invalid register (" + string(ev.Ch) + ")") + fmt.Println("Press any key to continue") + ev = <-eventChan + return + } + } + + fmt.Println("Enter new value (must be integer, end with enter)") + var newVal string + for ev.Key != termbox.KeyEnter { + ev = <-eventChan + if ev.Ch >= '0' && ev.Ch <= '9' { + newVal += string(ev.Ch) + fmt.Print(string(ev.Ch)) + } + } + fmt.Println("Setting Register (" + updReg + ") to " + newVal) + var v int + var e error + if v, e = strconv.Atoi(newVal); e != nil { + fmt.Println() + fmt.Println("Error parsing integer") + fmt.Println(e) + fmt.Println("Press any key to continue") + ev = <-eventChan + return + } + regs[updReg] = v +}