package main import ( "errors" "fmt" "math" "slices" "strconv" "strings" "time" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { inp := h.StdinToStringSlice() part1(inp) fmt.Println() part2(inp) } func part1(inp []string) { vm := InitVM(inp) fmt.Println("# Part 1") fmt.Println(run(vm.RegA, vm.RegB, vm.RegC, vm.Prog)) } func part2(inp []string) { fmt.Println("# Part 2") vm := InitVM(inp) a, b, c, prog := 0, vm.RegB, vm.RegC, vm.Prog for pos := len(prog) - 1; pos >= 0; pos-- { a <<= 3 for !slices.Equal(run(a, b, c, prog), prog[pos:]) { a++ } } fmt.Println("Corrected A:", a) } func run(a, b, c int, program []int) []int { out := make([]int, 0) for ptr := 0; ptr < len(program); ptr += 2 { oc, oa := program[ptr], program[ptr+1] value := oa switch oa { case 4: value = a case 5: value = b case 6: value = c } switch oc { case 0: a >>= value case 1: b ^= oa case 2: b = value % 8 case 3: if a != 0 { ptr = oa - 2 } case 4: b ^= c case 5: out = append(out, value%8) case 6: b = a >> value case 7: c = a >> value } } return out } type VM struct { RegA, RegB, RegC int Prog []int ProgStr string ptr int Running bool StdOut []int StdOutStr string Debug bool ProcHistory map[string]bool Err error } func InitVM(inp []string) *VM { vm := &VM{ ProcHistory: make(map[string]bool), } fmt.Sscanf(inp[0], "Register A: %d", &vm.RegA) fmt.Sscanf(inp[1], "Register B: %d", &vm.RegB) fmt.Sscanf(inp[2], "Register C: %d", &vm.RegC) vm.ProgStr = inp[4][9:] prog := strings.Split(vm.ProgStr, ",") for i := range prog { p, _ := strconv.Atoi(prog[i]) vm.Prog = append(vm.Prog, p) } return vm } func (vm *VM) DetectLoop() { if _, ok := vm.ProcHistory[vm.String()]; ok { vm.Err = errors.New("loop detected") vm.Running = false } } func (vm *VM) Run() { do := func(op, operand int) { vm.PrintDebug(op, operand) switch op { case 0: // adv (divide A by 2^combo -> int -> A) vm.RegA = int(float64(vm.RegA) / math.Pow(2, float64(vm.GetVal(operand)))) case 1: // bxl (bitwise XOR of B and literal operand -> B) vm.RegB = int(vm.RegB ^ operand) case 2: // bst (combo % 8 -> B) vm.RegB = int(vm.GetVal(operand) % 8) case 3: // jnz (if A is = 0 do nothing, else jump ptr to literal operand) if vm.RegA != 0 { vm.ptr = operand } case 4: // bxc (bitwise XOR of B and C -> B) (reads operand but ignore it) vm.RegB = vm.RegB ^ vm.RegC case 5: // out (calc value of combo operand % 8, output that value) v := vm.GetVal(operand) % 8 vm.StdOut = append(vm.StdOut, v) if vm.StdOutStr == "" { vm.StdOutStr = fmt.Sprintf("%d", v) } else { vm.StdOutStr = fmt.Sprintf("%s,%d", vm.StdOut, v) } case 6: // bdv (exactly like 0, but store into B) vm.RegB = int(float64(vm.RegA) / math.Pow(2, float64(vm.GetVal(operand)))) case 7: // cdv (exactly like 0, but store into C) vm.RegC = int(float64(vm.RegA) / math.Pow(2, float64(vm.GetVal(operand)))) } } vm.Running = true for vm.Running { if vm.ptr >= int(len(vm.Prog)) { break } if vm.Debug { fmt.Printf("Do: %d %d\n", vm.Prog[vm.ptr], vm.GetVal(vm.Prog[vm.ptr+1])) } vm.DetectLoop() pPtr := vm.ptr do(vm.Prog[vm.ptr], vm.Prog[vm.ptr+1]) if vm.ptr == pPtr { vm.ptr = vm.ptr + 2 } if vm.Debug { fmt.Println() fmt.Println(vm) time.Sleep(time.Second) } } } func (vm *VM) GetVal(cmb int) int { switch cmb { case 4: return vm.RegA case 5: return vm.RegB case 6: return vm.RegC } return cmb } func (vm *VM) PrintDebug(op, b int) { if !vm.Debug { return } switch op { case 0: fmt.Printf("adv (0): %d / int(math.Pow(2, %d)) (A)\n", vm.RegA, vm.GetVal(b)) case 1: fmt.Printf("bxl (1): %d ^ %d = %d (B)\n", vm.RegB, vm.GetVal(b), (vm.RegB ^ b)) case 2: fmt.Printf("bst (2): %d %% 8 = %d (B)\n", vm.GetVal(b), vm.GetVal(b)%8) case 3: fmt.Printf("jnz (3) if %d != 0; ptr = %d\n", vm.RegA, b) case 4: fmt.Printf("bxc (4): %d ^ %d = %d (B)\n", vm.RegB, vm.RegC, (vm.RegB ^ vm.RegC)) case 5: fmt.Printf("out (5): Output %d\n", (vm.GetVal(b) % 8)) case 6: fmt.Printf("bdv (6): %d / int(math.Pow(2, %d)) (B)\n", vm.RegA, vm.GetVal(b)) case 7: fmt.Printf("cdv (7): %d / int(math.Pow(2, %d)) (C)\n", vm.RegA, vm.GetVal(b)) } } func (vm VM) String() string { var ret string ret = fmt.Sprintf("Register A: %d", vm.RegA) ret = fmt.Sprintf("%s\nRegister B: %d", ret, vm.RegB) ret = fmt.Sprintf("%s\nRegister C: %d", ret, vm.RegC) ret = fmt.Sprintf("%s\n\nProgram: ", ret) for i := range vm.Prog { if vm.ptr == int(i) { ret = fmt.Sprintf("%s[%d],", ret, vm.Prog[i]) } else { ret = fmt.Sprintf("%s%d,", ret, vm.Prog[i]) } } return ret[:len(ret)-1] }