221 lines
4.7 KiB
Go
221 lines
4.7 KiB
Go
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]
|
|
}
|