2021 - Day 24 [WIP]

This commit is contained in:
Dreaded_X 2021-12-25 02:46:30 +01:00
parent 100d0ab60a
commit 2053504809
8 changed files with 1120 additions and 0 deletions

9
2021/24/go.mod Normal file
View File

@ -0,0 +1,9 @@
module AoC/2021/24
require AoC/2021/common v0.0.0
require github.com/joho/godotenv v1.4.0 // indirect
replace AoC/2021/common v0.0.0 => ../common
go 1.17

2
2021/24/go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=

43
2021/24/helper.go Normal file
View File

@ -0,0 +1,43 @@
package main
import (
"fmt"
"strconv"
)
func parseRegister(r string) int {
switch r {
case "w":
return 0
case "x":
return 1
case "y":
return 2
case "z":
return 3
default:
panic("Unknown register")
}
}
func registerToString(r int) string {
switch r {
case 0:
return "w"
case 1:
return "x"
case 2:
return "y"
case 3:
return "z"
default:
return fmt.Sprintf("%d", r)
}
}
func isNumber(s string) bool {
_, err := strconv.Atoi(s)
return err == nil
}

331
2021/24/main.go Normal file
View File

@ -0,0 +1,331 @@
package main
import (
aoc "AoC/2021/common"
"bufio"
"fmt"
"strconv"
"strings"
)
type Operator byte
const (
INP Operator = iota
ADD
ADDR
MUL
MULR
DIV
DIVR
MOD
MODR
EQL
EQLR
)
func (op Operator) String() string {
switch op {
case INP:
return "INP"
case ADD, ADDR:
return "ADD"
case MUL, MULR:
return "MUL"
case DIV, DIVR:
return "DIV"
case MOD, MODR:
return "MOD"
case EQL, EQLR:
return "EQL"
default:
return fmt.Sprintf("%d", int(op))
}
}
type Instruction struct {
op Operator
a int
b int
}
func (i Instruction) String() string {
if i.op == INP {
return fmt.Sprintf("%v\t[%s]", i.op, registerToString(i.a))
}
if i.op % 2 == 0 {
return fmt.Sprintf("%v\t[%s],\t[%s]", i.op, registerToString(i.a), registerToString(i.b))
}
return fmt.Sprintf("%v\t[%s],\t %d", i.op, registerToString(i.a), i.b)
}
type Memory [4]int
func (m Memory) String() string {
return fmt.Sprintf("w: %d, x: %d, y: %d, z: %d", m[0], m[1], m[2], m[3])
}
type Stream struct {
stream []int
counter int
}
func (s *Stream) Get() int {
if s.counter == len(s.stream) {
panic("End of stream reached")
}
val := s.stream[s.counter]
s.counter++
return val
}
func NewStream(input string) Stream {
var s Stream
s.stream = make([]int, len(input))
for i, b := range input {
n, err := strconv.Atoi(string(b))
if err != nil {
panic(err)
}
if n == 0 {
panic("0 is not a valid input")
}
s.stream[i] = n
}
return s
}
type Program []Instruction
func (p Program) Execute(input string) Memory {
stream := NewStream(input)
var mem Memory
for _, i := range p {
switch i.op {
case INP:
mem[i.a] = stream.Get()
case ADD:
mem[i.a] += i.b
case ADDR:
mem[i.a] += mem[i.b]
case MUL:
mem[i.a] *= i.b
case MULR:
mem[i.a] *= mem[i.b]
case DIV:
mem[i.a] /= i.b
case DIVR:
mem[i.a] /= mem[i.b]
case MOD:
mem[i.a] %= i.b
case MODR:
mem[i.a] %= mem[i.b]
case EQL:
val := 0
if mem[i.a] == i.b {
val = 1
}
mem[i.a] = val
case EQLR:
val := 0
if mem[i.a] == mem[i.b] {
val = 1
}
mem[i.a] = val
default:
panic("Unknown operator")
}
// fmt.Println(mem)
}
return mem
}
func Compile(input *bufio.Scanner) Program {
var program Program
for input.Scan() {
line := input.Text()
inst := strings.Split(line, " ")
var instruction Instruction
// Parse the operator
switch inst[0] {
case "inp":
instruction.op = INP
case "add":
instruction.op = ADD
case "mul":
instruction.op = MUL
case "div":
instruction.op = DIV
case "mod":
instruction.op = MOD
case "eql":
instruction.op = EQL
default:
panic("Unknown instruction")
}
// Parse the first parameter, this is always a register
instruction.a = parseRegister(inst[1])
// All instructions, except INP, require a second parameter
if instruction.op != INP {
b := inst[2]
if val, err := strconv.Atoi(b); err == nil {
// If the parameter is a number we use that as the second parameter
instruction.b = val
} else {
// Otherwise the parameter is a register, so we parse it and set it to the register
instruction.b = parseRegister(b)
// We also increment the operator by one to indicate that the second parameter is a register
instruction.op++
}
}
program = append(program, instruction)
}
return program
}
func (p Program) Optimize() Program {
// previous := len(p)
for counter := 0; counter < 4; counter++ {
fmt.Printf("PRECOMP %d\n", counter)
p = p.precomp()
fmt.Printf("QUASI %d\n", counter)
p = p.quasi()
// if previous == len(p) {
// fmt.Println("Done optimizing")
// break
// }
// previous = len(p)
}
return p
}
func main() {
challenge := aoc.New(2021, 24)
// challenge.Test(`inp x
// mul x -1`, []int{-1, -1})
// challenge.Test(`inp z
// inp x
// mul z 3
// eql z x`, []int{-1, -1})
// challenge.Test(`inp w
// add z w
// mod z 2
// div w 2
// add y w
// mod y 2
// div w 2
// add x w
// mod x 2
// div w 2
// mod w 2`, []int{-1, -1})
challenge.Test(`inp w
mul x 0
add x z
mod x 26
div z 1
add x 14
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 1
mul y x
add z y`, []int{-1, -1})
challenge.Test(`inp w
mul x 0
add x z
mod x 26
div z 1
add x 14
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 1
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 15
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 7
mul y x
add z y`, []int{-1, -1})
// challenge.Test(``, []int{-1, -1})
challenge.Solution(1, func (input *bufio.Scanner) int {
program := Compile(input)
fmt.Println("ORIGINAL")
for _, i := range program {
fmt.Printf("%v\n", i)
}
fmt.Println(len(program))
fmt.Println("OPTIMIZED")
opti := program.Optimize()
for _, i := range opti {
fmt.Printf("%v\n", i)
}
fmt.Println(len(opti))
str := "25579246899999"
fmt.Println(program.Execute(str))
fmt.Println(opti.Execute(str))
// fmt.Println(mem)
return 0
})
challenge.Solution(2, func (input *bufio.Scanner) int {
return 0
})
}

300
2021/24/precomp.go Normal file
View File

@ -0,0 +1,300 @@
package main
type RegisterState struct {
value int
last int
known bool
}
// @TODO Apply DRY
// The function will go through all the instruction and precompute as much as possible
func (p Program) precomp() Program {
var np Program
var r [4]RegisterState
// Initalize the registers
for i := range r {
r[i].known = true
}
for _, i := range p {
switch i.op {
case INP:
r[i.a].known = false
np = append(np, i)
case ADD:
if r[i.a].known {
r[i.a].value += i.b
continue
}
np = append(np, i)
case ADDR:
if r[i.a].known && r[i.b].known {
r[i.a].value += r[i.b].value
continue
}
if r[i.a].known {
np = append(np, Instruction{MUL, i.a, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.a].value})
}
}
if r[i.b].known {
np = append(np, Instruction{MUL, i.b, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.b].value})
}
}
r[i.a].known = false
np = append(np, i)
case MUL:
if i.b == 0 {
r[i.a].value = 0
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if i.b == 1 {
continue
}
if r[i.a].known {
r[i.a].value *= i.b
continue
}
np = append(np, i)
case MULR:
if r[i.a].known && r[i.a].value == 0 {
r[i.a].value = 0
continue
}
if r[i.b].known && r[i.b].value == 0 {
r[i.a].value = 0
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if r[i.b].known && r[i.b].value == 1 {
continue
}
if r[i.a].known && r[i.b].known {
r[i.a].value *= r[i.b].value
continue
}
if r[i.a].known {
np = append(np, Instruction{MUL, i.a, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.a].value})
}
}
if r[i.b].known {
np = append(np, Instruction{MUL, i.b, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.b].value})
}
}
r[i.a].known = false
np = append(np, i)
case DIV:
if i.b == 1 {
continue
}
if r[i.a].known {
r[i.a].value /= i.b
continue
}
np = append(np, i)
case DIVR:
if r[i.a].known && r[i.a].value == 0 {
r[i.a].value = 0
continue
}
if r[i.b].known && r[i.b].value == 1 {
continue
}
if r[i.a].known && r[i.b].known {
r[i.a].value /= r[i.b].value
continue
}
if r[i.a].known {
np = append(np, Instruction{MUL, i.a, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.a].value})
}
}
if r[i.b].known {
np = append(np, Instruction{MUL, i.b, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.b].value})
}
}
r[i.a].known = false
np = append(np, i)
case MOD:
if i.b == -1 {
r[i.a].value = 0
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if i.b == 1 {
r[i.a].value = 0
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if r[i.a].known {
r[i.a].value %= i.b
continue
}
np = append(np, i)
case MODR:
if r[i.a].known && r[i.a].value == 0 {
r[i.a].value = 0
continue
}
if r[i.b].known && r[i.b].value == -1 {
r[i.a].value = 0
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if r[i.b].known && r[i.b].value == 1 {
r[i.a].value = 0
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if r[i.a].known && r[i.b].known {
r[i.a].value %= r[i.b].value
continue
}
if r[i.a].known {
np = append(np, Instruction{MUL, i.a, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.a].value})
}
}
if r[i.b].known {
np = append(np, Instruction{MUL, i.b, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.b].value})
}
}
r[i.a].known = false
np = append(np, i)
case EQL:
if r[i.a].known {
val := 0
if r[i.a].value == i.b {
val = 1
}
r[i.a].value = val
continue
}
np = append(np, i)
case EQLR:
if i.a == i.b {
r[i.a].value = 1
if !r[i.a].known {
r[i.a].value = 0
r[i.a].known = true
np = append(np, Instruction{MUL, i.a, 0})
}
continue
}
if r[i.a].known && r[i.b].known {
val := 0
if r[i.a].value == r[i.b].value {
val = 1
}
r[i.a].value = val
continue
}
if r[i.a].known {
np = append(np, Instruction{MUL, i.a, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.a].value})
}
}
if r[i.b].known {
np = append(np, Instruction{MUL, i.b, 0})
if r[i.b].value != 0 {
np = append(np, Instruction{ADD, i.b, r[i.b].value})
}
}
r[i.a].known = false
np = append(np, i)
}
}
// Emit all the non-zero known registers
for i := range r {
if r[i].known && r[i].value != 0 {
np = append(np, Instruction{ADD, i, r[i].value})
}
}
return np
}

140
2021/24/quasi.go Normal file
View File

@ -0,0 +1,140 @@
package main
import "fmt"
type RegisterQuasiState struct {
min int
max int
}
func minv(a int, b int) int {
if a < b {
return a
}
return b
}
func maxv(a int, b int) int {
if a > b {
return a
}
return b
}
// This function will try to eliminate eql statements
func (p Program) quasi() Program {
var np Program
var r [4]RegisterQuasiState
broken := false
for _, i := range p {
if !broken {
switch i.op {
case INP:
r[i.a].min = 1
r[i.a].max = 9
case ADD:
r[i.a].min += i.b
r[i.a].max += i.b
case ADDR:
r[i.a].min = r[i.a].min + r[i.b].min
r[i.a].max = r[i.a].max + r[i.b].max
case MUL:
r[i.a].min *= i.b
r[i.a].max *= i.b
if r[i.a].min > r[i.a].max {
r[i.a].min, r[i.a].max = r[i.a].max, r[i.a].min
}
case MULR:
min1 := r[i.a].min * r[i.b].min
min2 := r[i.a].min * r[i.b].max
max1 := r[i.a].max * r[i.b].min
max2 := r[i.a].max * r[i.b].max
r[i.a].min = minv(min1, minv(min2, minv(max1, max2)))
r[i.a].max = maxv(min1, maxv(min2, maxv(max1, max2)))
case EQL:
if i.b < r[i.a].min || i.b > r[i.a].max {
r[i.a].min = 0
r[i.a].max = 0
np = append(np, Instruction{MUL, i.a, 0})
fmt.Println("\tReplace EQL with mul 0")
continue
}
if r[i.a].min == r[i.a].max {
val := 0
if r[i.a].min == i.b {
val = 1
}
r[i.a].min = val
r[i.a].max = val
np = append(np, Instruction{MUL, i.a, 0})
np = append(np, Instruction{ADD, i.a, 1})
fmt.Println("\tReplace EQL with mul 0, add 1")
continue
}
r[i.a].min = 0
r[i.a].max = 1
case EQLR:
if r[i.b].max < r[i.a].min || r[i.b].min > r[i.a].max {
r[i.a].min = 0
r[i.a].max = 0
np = append(np, Instruction{MUL, i.a, 0})
fmt.Println("\tReplace EQLR with mul 0")
continue
}
if r[i.a].min == r[i.a].max && r[i.b].min == r[i.b].max {
val := 0
if r[i.a].min == r[i.b].min {
val = 1
}
r[i.a].min = val
r[i.a].max = val
np = append(np, Instruction{MUL, i.a, 0})
np = append(np, Instruction{ADD, i.a, 1})
fmt.Println("\tReplace EQLR with mul 0, add 1")
continue
}
r[i.a].min = 0
r[i.a].max = 1
case MOD:
r[i.a].min = 0
r[i.a].max = i.b
if r[i.a].min > r[i.a].max {
r[i.a].min, r[i.a].max = r[i.a].max, r[i.a].min
}
default:
fmt.Println("\tBROKE")
broken = true
}
}
np = append(np, i)
}
return np
}

2
2021/24/run.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
go run main.go helper.go precomp.go quasi.go

293
2021/24/scratch Normal file
View File

@ -0,0 +1,293 @@
PART1
inp w
add y w
add y 1
add z y
add x 1
PART2
INP [w]
ADD [y], [w]
ADD [y], 1
ADD [z], [y]
w = ? x = 1 y = ? z = ?
INP [w]
MUL [x], 0
ADD [x], [z]
MOD [x], 26
ADD [x], 15
MUL [x], 0
MUL [x], 0
ADD [x], 1
MUL [y], 0
ADD [y], 25
MUL [y], [x]
ADD [y], 1
MUL [z], [y]
MUL [y], 0
ADD [y], [w]
ADD [y], 7
MUL [y], [x]
ADD [z], [y]
ORIGINAL
inp w
mul x 0
add x z
mod x 26
div z 1
add x 14
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 1
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 15
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 7
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 15
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 13
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x -6
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 10
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 14
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 0
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x -4
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 13
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 15
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 11
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 15
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 6
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 1
add x 11
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 1
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x 0
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 7
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x 0
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 11
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x -3
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 14
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x -9
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 4
mul y x
add z y
inp w
mul x 0
add x z
mod x 26
div z 26
add x -9
eql x w
eql x 0
mul y 0
add y 25
mul y x
add y 1
mul z y
mul y 0
add y w
add y 10
mul y x
add z y