package main
import (
"bufio"
"fmt"
"os"
"strings"
"time"
"github.com/fatih/color"
)
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() {
fmt.Printf("Press ENTER to continue\n")
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
Ticks int
LastResult [][6]int
Isljmp []int // Instructions since last jump
PrevIsljmp []int
LoopCount int
LastJumpFrom int
LastJumpTo int
PotentialLoopFrom int
PotentialLoopTo int
}
func (p *Puzzle) RunProgram() {
p.PrevIsljmp = make([]int, 0, 10)
p.Isljmp = make([]int, 0, 10)
p.LastResult = make([][6]int, len(p.ROM)/4)
p.LastJumpFrom = -1
p.PrintState()
waitEnter()
start := time.Now()
ticker := time.Tick(time.Second)
seen := make(map[int]struct{})
ordered_seen := make([]int, 0, 1000)
for {
elapsed := time.Now().Sub(start)
select {
case <-ticker:
fmt.Println(elapsed)
default:
}
if p.IP < 0 || p.IP*4 >= len(p.ROM) {
color.New(color.BgRed).Add(color.FgHiWhite).Println("PANIC! Access OOB @", p.IP, "after", p.Ticks, "iterations")
return
}
if p.IP == 28 {
if _, ok := seen[p.Regs[4]]; ok {
p.PrintState()
fmt.Println("DUPE!", p.Regs[4])
fmt.Println("Seen:", len(ordered_seen))
fmt.Println("First:", ordered_seen[0])
fmt.Println("Last:", ordered_seen[len(ordered_seen)-1])
return
}
seen[p.Regs[4]] = struct{}{}
ordered_seen = append(ordered_seen, p.Regs[4])
}
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]
}
// Detect loops
if c == p.IPreg {
p.LastJumpFrom = p.IP
} else {
p.Isljmp = append(p.Isljmp, op[0])
}
p.Regs[c] = o.Exe(a, b)
p.IP = p.Regs[p.IPreg]
copy(p.LastResult[p.IP][:], p.Regs[:])
p.IP++
p.Ticks++
if c == p.IPreg {
p.LastJumpTo = p.IP
if p.IP >= p.PotentialLoopTo && p.IP <= p.PotentialLoopFrom {
if p.IP == p.PotentialLoopTo {
if sliceEqual(p.Isljmp, p.PrevIsljmp) {
p.LoopCount++
} else {
p.LoopCount = 0
}
p.PrevIsljmp = p.Isljmp
p.Isljmp = make([]int, 0, 10)
}
} else {
p.PotentialLoopFrom = p.LastJumpFrom
p.PotentialLoopTo = p.LastJumpTo
p.PrevIsljmp = p.Isljmp
p.Isljmp = make([]int, 0, 10)
}
}
}
}
func (p *Puzzle) PrintState() {
fmt.Printf("\033c")
color.New(color.FgWhite).Printf("IP: ")
color.New(color.FgHiWhite).Printf("%2d", p.IP)
color.New(color.FgWhite).Printf("\tIPreg: ")
color.New(color.FgHiCyan).Printf("r%d", p.IPreg)
color.New(color.FgWhite).Printf("\tTicks: ")
color.New(color.FgHiWhite).Printf("%d\n", p.Ticks)
color.New(color.FgWhite).Printf("Registers: ")
fmt.Printf("[%d, %d, %d, %d, %d, %d]\n", p.Regs[0], p.Regs[1], p.Regs[2], p.Regs[3], p.Regs[4], p.Regs[5])
color.New(color.FgWhite).Printf("%s\n", strings.Repeat("-", 93))
for t := 0; t < len(p.ROM); t += 4 {
i := t / 4
o := opcodes[p.ROM[t]]
gutterColor := color.New(color.FgWhite)
normalColor := color.New(color.FgHiWhite)
registerColor := color.New(color.FgHiCyan)
if i == p.IP {
gutterColor = color.New(color.FgHiWhite).Add(color.BgBlue)
normalColor.Add(color.BgBlue)
registerColor = color.New(color.FgHiCyan).Add(color.BgBlue)
}
gutterColor.Printf("%02d|", i)
normalColor.Printf(" %s", o.Name)
if o.A == ArgumentTypeRegister {
registerColor.Printf(" % 6sr%d", " ", p.ROM[t+1])
} else {
normalColor.Printf(" %8d", p.ROM[t+1])
}
if o.B == ArgumentTypeRegister {
registerColor.Printf(" % 6sr%d", " ", p.ROM[t+2])
} else {
normalColor.Printf(" %8d", p.ROM[t+2])
}
registerColor.Printf(" % 6sr%d", " ", p.ROM[t+3])
regs := make([]string, len(p.Regs))
for t, r := range p.LastResult[i] {
if r > 100000000 {
regs[t] += ">9999999"
} else {
regs[t] += fmt.Sprintf("%8d", r)
}
}
gutterColor.Printf(" = [%s]", strings.Join(regs, ","))
if p.LastJumpFrom > -1 {
if i >= p.PotentialLoopTo && i <= p.PotentialLoopFrom {
if i == p.PotentialLoopTo {
registerColor.Printf(" *")
}
if i > p.PotentialLoopTo && i < p.PotentialLoopFrom {
registerColor.Printf(" |")
}
if i == p.PotentialLoopFrom {
registerColor.Printf(" ^")
}
}
if i == p.LastJumpTo {
gutterColor.Printf(" *")
}
if (i > p.LastJumpTo && i < p.LastJumpFrom) || (i < p.LastJumpTo && i > p.LastJumpFrom) {
gutterColor.Printf(" |")
}
if i == p.LastJumpFrom {
if p.LastJumpFrom < p.LastJumpTo {
gutterColor.Printf(" v")
} else {
gutterColor.Printf(" ^")
}
gutterColor.Printf(" jump")
}
if i == p.PotentialLoopTo {
if p.LoopCount > 0 {
registerColor.Printf(" loop")
}
}
if i == p.PotentialLoopFrom {
if p.LoopCount > 0 {
registerColor.Printf(" iteration %d", p.LoopCount)
}
}
/*
if i == p.IP {
if (p.IP > p.LastJumpTo && p.IP < p.LastJumpFrom) || (p.IP < p.LastJumpTo && p.IP > p.LastJumpFrom) {
inst := make([]string, len(p.Isljmp))
for t, o := range p.Isljmp {
inst[t] = opcodes[o].Name
}
gutterColor.Printf(" [%s]", strings.Join(inst, ","))
}
}
*/
}
fmt.Printf("\n")
}
}
func main() {
p := Puzzle{}
input, err := os.Open("input.txt")
if err != nil {
panic(err.Error())
}
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
}
}
}
if err := scanner.Err(); err != nil {
panic(err.Error())
}
input.Close()
p.RunProgram()
}