79 lines
1.7 KiB
Plaintext
79 lines
1.7 KiB
Plaintext
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"strconv"
|
|
)
|
|
|
|
func main() {
|
|
fmt.Println(day17(1))
|
|
fmt.Println(day17(2))
|
|
}
|
|
|
|
func day17(part int) string {
|
|
a, b, c, program := parseRegisters()
|
|
|
|
if part == 1 {
|
|
// part 1: run program and return comma-separated output
|
|
return fmt.Sprintln(runProgram(a, b, c, program))
|
|
}
|
|
|
|
// part 2: find lowest value of register A that makes the program output itself
|
|
a = 0 // the initial value of register A doesnt matter here, so we can reset it
|
|
for pos := len(program) - 1; pos >= 0; pos-- {
|
|
a <<= 3 // shift left by 3 bits
|
|
for !slices.Equal(runProgram(a, b, c, program), program[pos:]) {
|
|
a++
|
|
}
|
|
}
|
|
|
|
return strconv.Itoa(a) // return a string since part 1 needs a string
|
|
}
|
|
|
|
func parseRegisters() (int, int, int, []int) {
|
|
return 33940147, 0, 0, []int{2, 4, 1, 5, 7, 5, 1, 6, 4, 2, 5, 5, 0, 3, 3, 0}
|
|
}
|
|
|
|
func runProgram(a, b, c int, program []int) []int {
|
|
out := make([]int, 0)
|
|
// for each isntruction pointer
|
|
for ip := 0; ip < len(program); ip += 2 {
|
|
opcode, operand := program[ip], program[ip+1]
|
|
// Process combo operand
|
|
value := operand
|
|
switch operand {
|
|
case 4:
|
|
value = a
|
|
case 5:
|
|
value = b
|
|
case 6:
|
|
value = c
|
|
}
|
|
|
|
// Execute instruction
|
|
switch opcode {
|
|
case 0: // adv - divide A by 2^value
|
|
a >>= value
|
|
case 1: // bxl - XOR B with literal
|
|
b ^= operand
|
|
case 2: // bst - set B to value mod 8
|
|
b = value % 8
|
|
case 3: // jnz - jump if A is not zero
|
|
if a != 0 {
|
|
ip = operand - 2
|
|
}
|
|
case 4: // bxc - XOR B with C
|
|
b ^= c
|
|
case 5: // out - output value mod 8
|
|
out = append(out, value%8)
|
|
case 6: // bdv - divide A by 2^value, store in B
|
|
b = a >> value
|
|
case 7: // cdv - divide A by 2^value, store in C
|
|
c = a >> value
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|