package main
import (
"bufio"
"fmt"
"os"
)
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: "addr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: add,
},
{
Name: "addi",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: add,
},
{
Name: "mulr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: mul,
},
{
Name: "muli",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: mul,
},
{
Name: "banr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: ban,
},
{
Name: "bani",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: ban,
},
{
Name: "borr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: bor,
},
{
Name: "bori",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: bor,
},
{
Name: "setr",
A: ArgumentTypeRegister,
B: ArgumentTypeNone,
Exe: set,
},
{
Name: "seti",
A: ArgumentTypeValue,
B: ArgumentTypeNone,
Exe: set,
},
{
Name: "gtir",
A: ArgumentTypeValue,
B: ArgumentTypeRegister,
Exe: gt,
},
{
Name: "gtri",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: gt,
},
{
Name: "gtrr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: gt,
},
{
Name: "eqir",
A: ArgumentTypeValue,
B: ArgumentTypeRegister,
Exe: eq,
},
{
Name: "eqri",
A: ArgumentTypeRegister,
B: ArgumentTypeValue,
Exe: eq,
},
{
Name: "eqrr",
A: ArgumentTypeRegister,
B: ArgumentTypeRegister,
Exe: eq,
},
}
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 {
Tests []Test
Potentials [16][]string
Instructions [16]*Opcode
Registers [4]int
Program []int
PC int
}
func (p *Puzzle) IdentifyPotentials() {
total := 0
for t, test := range p.Tests {
potentials := 0
fmt.Printf("Test #%d\n\tI: %+v\n\tOP:%+v\n\tT: %+v\n", t, test.Before, test.Op, test.After)
for _, op := range opcodes {
registers := [4]int{}
copy(registers[:], test.Before[:])
a := test.Op[1]
b := test.Op[2]
c := test.Op[3]
if op.A == ArgumentTypeRegister {
a = registers[a]
}
if op.B == ArgumentTypeRegister {
b = registers[b]
}
registers[c] = op.Exe(a, b)
fmt.Printf("\t%s -> A:%d B:%d C:%d R:%+v", op.Name, a, b, c, registers)
if sliceEqual(registers[:], test.After[:]) {
fmt.Printf(" OK")
potentials++
o := test.Op[0]
if p.Potentials[o] == nil {
p.Potentials[o] = make([]string, 0, 10)
}
found := false
for _, i := range p.Potentials[o] {
if i == op.Name {
found = true
break
}
}
if !found {
p.Potentials[o] = append(p.Potentials[test.Op[0]], op.Name)
}
}
fmt.Printf("\n")
}
fmt.Println("Potentials:", potentials)
if potentials >= 3 {
total++
}
}
fmt.Println("\nTotal samples with more >=3 potentials:", total)
for t, i := range p.Potentials {
fmt.Printf("%d -> %+v\n", t, i)
}
}
func (p *Puzzle) IdentifyInstructions() {
for {
found := false
for j, i := range p.Potentials {
if len(i) != 1 {
continue
}
op := i[0]
for u := range opcodes {
if opcodes[u].Name == op {
p.Instructions[j] = &opcodes[u]
break
}
}
for h, u := range p.Potentials {
for t, v := range u {
if v == op {
p.Potentials[h] = append(p.Potentials[h][:t], p.Potentials[h][t+1:]...)
break
}
}
}
found = true
break
}
if !found {
break
}
}
fmt.Println("\nIdentified:")
for t, i := range p.Instructions {
fmt.Printf("%02d -> %s\n", t, i.Name)
}
}
func (p *Puzzle) RunProgram() {
for p.PC = 0; p.PC < len(p.Program); p.PC += 4 {
op := p.Program[p.PC : p.PC+4]
o := p.Instructions[op[0]]
a := op[1]
b := op[2]
c := op[3]
if o.A == ArgumentTypeRegister {
a = p.Registers[a]
}
if o.B == ArgumentTypeRegister {
b = p.Registers[b]
}
p.Registers[c] = o.Exe(a, b)
}
}
func (p *Puzzle) PrintRegisters() {
fmt.Println("Registers:", p.Registers)
}
func main() {
p := Puzzle{}
{
input, err := os.Open("input1.txt")
if err != nil {
panic(err.Error())
}
defer input.Close()
scanner := bufio.NewScanner(input)
for scanner.Scan() {
test := Test{}
if err := scanner.Err(); err != nil {
panic(err.Error())
}
fmt.Sscanf(scanner.Text(), "Before: [%d, %d, %d, %d]", &test.Before[0], &test.Before[1], &test.Before[2], &test.Before[3])
scanner.Scan()
if err := scanner.Err(); err != nil {
panic(err.Error())
}
fmt.Sscanf(scanner.Text(), "%d %d %d %d", &test.Op[0], &test.Op[1], &test.Op[2], &test.Op[3])
scanner.Scan()
if err := scanner.Err(); err != nil {
panic(err.Error())
}
fmt.Sscanf(scanner.Text(), "After: [%d, %d, %d, %d]", &test.After[0], &test.After[1], &test.After[2], &test.After[3])
p.Tests = append(p.Tests, test)
scanner.Scan()
if err := scanner.Err(); err != nil {
panic(err.Error())
}
}
if err := scanner.Err(); err != nil {
panic(err.Error())
}
}
{
input, err := os.Open("input2.txt")
if err != nil {
panic(err.Error())
}
defer input.Close()
scanner := bufio.NewScanner(input)
for scanner.Scan() {
op := [4]int{}
fmt.Sscanf(scanner.Text(), "%d %d %d %d", &op[0], &op[1], &op[2], &op[3])
p.Program = append(p.Program, op[:]...)
}
if err := scanner.Err(); err != nil {
panic(err.Error())
}
}
p.IdentifyPotentials()
p.IdentifyInstructions()
p.RunProgram()
p.PrintRegisters()
}