diff --git a/2021/24/go.mod b/2021/24/go.mod new file mode 100644 index 0000000..44e1c8f --- /dev/null +++ b/2021/24/go.mod @@ -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 diff --git a/2021/24/go.sum b/2021/24/go.sum new file mode 100644 index 0000000..8c9f290 --- /dev/null +++ b/2021/24/go.sum @@ -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= diff --git a/2021/24/helper.go b/2021/24/helper.go new file mode 100644 index 0000000..1c50bf7 --- /dev/null +++ b/2021/24/helper.go @@ -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 +} + diff --git a/2021/24/main.go b/2021/24/main.go new file mode 100644 index 0000000..44e9305 --- /dev/null +++ b/2021/24/main.go @@ -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 + }) +} diff --git a/2021/24/precomp.go b/2021/24/precomp.go new file mode 100644 index 0000000..09b4b9c --- /dev/null +++ b/2021/24/precomp.go @@ -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 +} diff --git a/2021/24/quasi.go b/2021/24/quasi.go new file mode 100644 index 0000000..4a42321 --- /dev/null +++ b/2021/24/quasi.go @@ -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 +} diff --git a/2021/24/run.sh b/2021/24/run.sh new file mode 100755 index 0000000..88115c1 --- /dev/null +++ b/2021/24/run.sh @@ -0,0 +1,2 @@ +#!/bin/bash +go run main.go helper.go precomp.go quasi.go diff --git a/2021/24/scratch b/2021/24/scratch new file mode 100644 index 0000000..a0ac571 --- /dev/null +++ b/2021/24/scratch @@ -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 + +