package main
import (
"bufio"
"fmt"
"os"
"time"
)
type ArgumentType int
const (
ArgumentTypeNone ArgumentType = iota
ArgumentTypeRegister
ArgumentTypeValue
)
type Opcode struct {
Name string
A ArgumentType
B ArgumentType
Exe func(a, b int) int
}
var opcodes = []Opcode{
{
Name: "banr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: ban,
},
{
Name: "eqrr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: eq,
},
{
Name: "setr",
A: ArgumentTypeRegister,
B: ArgumentTypeNone,
Exe: set,
},
{
Name: "eqir",
A: ArgumentTypeValue,
B: ArgumentTypeRegister,
Exe: eq,
},
{
Name: "bori",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: bor,
},
{
Name: "muli",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: mul,
},
{
Name: "bani",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: ban,
},
{
Name: "borr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: bor,
},
{
Name: "gtir",
A: ArgumentTypeValue,
B: ArgumentTypeRegister,
Exe: gt,
},
{
Name: "gtrr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: gt,
},
{
Name: "addi",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: add,
},
{
Name: "gtri",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: gt,
},
{
Name: "eqri",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: eq,
},
{
Name: "addr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: add,
},
{
Name: "mulr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: mul,
},
{
Name: "seti",
A: ArgumentTypeValue,
B: ArgumentTypeNone,
Exe: set,
},
}
func add(a, b int) int {
return a + b
}
func mul(a, b int) int {
return a * b
}
func ban(a, b int) int {
return a & b
}
func bor(a, b int) int {
return a | b
}
func set(a, b int) int {
return a
}
func gt(a, b int) int {
if a > b {
return 1
}
return 0
}
func eq(a, b int) int {
if a == b {
return 1
}
return 0
}
func waitEnter() {
bufio.NewReader(os.Stdin).ReadBytes('\n')
}
type Test struct {
Before [4]int
Op [4]int
After [4]int
}
func sliceEqual(a, b []int) bool {
if len(a) != len(b) {
return false
}
for t := range a {
if a[t] != b[t] {
return false
}
}
return true
}
type Puzzle struct {
Regs [6]int
ROM []int
IP int
IPreg int
}
func (p *Puzzle) RunProgram() {
oldRegs := [6]int{}
regs := [6]rune{}
ticks := 0
start := time.Now()
for {
if p.IP < 0 || p.IP*4 >= len(p.ROM) {
fmt.Println("PANIC! Access OOB @", p.IP, "after", ticks, "iterations")
return
}
copy(oldRegs[:], p.Regs[:])
p.Regs[p.IPreg] = p.IP
op := p.ROM[p.IP*4 : (p.IP*4)+4]
o := opcodes[op[0]]
a := op[1]
b := op[2]
c := op[3]
if o.A == ArgumentTypeRegister {
a = p.Regs[a]
}
if o.B == ArgumentTypeRegister {
b = p.Regs[b]
}
p.Regs[c] = o.Exe(a, b)
p.IP = p.Regs[p.IPreg]
p.IP++
ticks++
for t := range regs {
if oldRegs[t] == p.Regs[t] {
regs[t] = '.'
continue
}
regs[t] = '*'
}
if ticks%10000000 == 0 {
fmt.Println(time.Now().Sub(start), ticks)
}
//p.PrintRegs()
fmt.Printf("%3c %3c %3c %3c %3c %3c <- %d\n", regs[0], regs[1], regs[2], regs[3], regs[4], regs[5], ticks)
}
}
func (p *Puzzle) PrintRegs() {
fmt.Println("Regs:", p.Regs)
}
func main() {
// Program 1
if true {
fmt.Println("Program 1\n---")
p := Puzzle{}
input, err := os.Open("input.txt")
if err != nil {
panic(err.Error())
}
defer input.Close()
scanner := bufio.NewScanner(input)
for scanner.Scan() {
var op string
var a, b, c int
fmt.Sscanf(scanner.Text(), "%s %d %d %d", &op, &a, &b, &c)
if op == "#ip" {
p.IPreg = a
continue
}
for k, o := range opcodes {
if o.Name == op {
p.ROM = append(p.ROM, k, a, b, c)
break
}
}
fmt.Println(op, a, b, c)
}
if err := scanner.Err(); err != nil {
panic(err.Error())
}
fmt.Printf("\n")
p.RunProgram()
p.PrintRegs()
}
// Program 2
// NOTE: This runs for a looooong time, time to reverseengineer
if false {
fmt.Println("\nProgram 2\n---")
p := Puzzle{}
p.Regs[0] = 1
// Bruteforcing after lengthy debugging and trying to understanding what the assembly is doing
//p.Regs = [6]int{0, 0, 1, 9, 10551326, 10551325}
//p.Regs = [6]int{1, 0, 10551326, 9, 10551326, 10551326}
//p.Regs = [6]int{1, 0, 10551326, 9, 10551326, 10551325}
//p.IP = 9
input, err := os.Open("input.txt")
if err != nil {
panic(err.Error())
}
defer input.Close()
scanner := bufio.NewScanner(input)
for scanner.Scan() {
var op string
var a, b, c int
fmt.Sscanf(scanner.Text(), "%s %d %d %d", &op, &a, &b, &c)
if op == "#ip" {
p.IPreg = a
continue
}
for k, o := range opcodes {
if o.Name == op {
p.ROM = append(p.ROM, k, a, b, c)
break
}
}
fmt.Println(op, a, b, c)
}
if err := scanner.Err(); err != nil {
panic(err.Error())
}
fmt.Printf("\n")
p.RunProgram()
p.PrintRegs()
}
return
// Attempt to re-impl algo
var r0, r1, r2, r3, r4, r5 int
r0 = 1
r4 += 2
r4 *= r4
r4 *= 19
r4 *= 11
r1 += 4
r1 += 22
r1 += 2
r4 += r1
r1 = 27
r1 *= 28
r1 += 29
r1 *= 30
r1 *= 14
r1 *= 32
r4 += r1
r0 = 0
//start
r2 = 1
//start1
r5 = 1
//start3
tick := 0
for {
r1 = r2 * r5
if r1 == r4 {
//check
if r5 > r4 {
//skip
r2++
if r2 > r4 {
fmt.Println("DONE", r0, r1, r2, r3, r4, r5)
return
} else {
r5 = 1
}
}
} else {
r5++
}
if tick%10000 == 0 {
fmt.Println(tick, "-", r0, r1, r2, r3, r4, r5)
}
tick++
}
}