mirror of
https://github.com/Threnklyn/advent-of-code-go.git
synced 2026-05-19 03:23:27 +02:00
260 lines
4.6 KiB
Go
260 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
_ "embed"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/alexchao26/advent-of-code-go/cast"
|
|
"github.com/alexchao26/advent-of-code-go/util"
|
|
)
|
|
|
|
//go:embed input.txt
|
|
var input string
|
|
|
|
func init() {
|
|
// do this in init (not main) so test file has same input
|
|
input = strings.TrimRight(input, "\n")
|
|
if len(input) == 0 {
|
|
panic("empty input.txt file")
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
var part int
|
|
flag.IntVar(&part, "part", 1, "part 1 or 2")
|
|
flag.Parse()
|
|
fmt.Println("Running part", part)
|
|
|
|
if part == 1 {
|
|
ans := part1(input)
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
fmt.Println("Output:", ans)
|
|
} else {
|
|
ans := part2(input)
|
|
// drg,gvw,jbp,jgc,qjb,z15,z22,z35
|
|
util.CopyToClipboard(fmt.Sprintf("%v", ans))
|
|
fmt.Println("Output:", ans)
|
|
}
|
|
}
|
|
|
|
func part1(input string) int {
|
|
calc := parseInput(input)
|
|
res := 0
|
|
|
|
cache := map[string]int{}
|
|
|
|
for k, v := range calc.wires {
|
|
cache[k] = v
|
|
}
|
|
|
|
for k := range calc.outputToSource {
|
|
if k[0] != 'z' {
|
|
continue
|
|
}
|
|
|
|
val, err := getWireVal(k, calc, cache, 0)
|
|
|
|
if err != nil {
|
|
log.Fatalf("Did not expect error\n")
|
|
}
|
|
|
|
if val == 0 {
|
|
continue
|
|
}
|
|
|
|
i := cast.ToInt(k[1:])
|
|
|
|
res += 1 << int(i)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func part2(input string) string {
|
|
calc := parseInput(input)
|
|
|
|
swaps := map[string]bool{}
|
|
ans := map[string]bool{}
|
|
|
|
fmt.Println("this will take a while to run")
|
|
backtrack(0, calc, swaps, ans)
|
|
|
|
wires := []string{}
|
|
for wire := range ans {
|
|
wires = append(wires, wire)
|
|
}
|
|
|
|
sort.Strings(wires)
|
|
|
|
return strings.Join(wires, ",")
|
|
}
|
|
|
|
func getWireVal(wire string, calc Calculator, cache map[string]int, depth int) (int, error) {
|
|
if val, ok := cache[wire]; ok {
|
|
return val, nil
|
|
}
|
|
|
|
if depth > 200 {
|
|
return -1, fmt.Errorf("likely a loop, exit")
|
|
}
|
|
|
|
op, ok := calc.outputToSource[wire]
|
|
|
|
if !ok {
|
|
return -1, fmt.Errorf("cannot get value for wire %v", wire)
|
|
}
|
|
|
|
v1, _ := getWireVal(op.inputs[0], calc, cache, depth+1)
|
|
v2, _ := getWireVal(op.inputs[1], calc, cache, depth+1)
|
|
|
|
val := 0
|
|
if op.op == "AND" {
|
|
val = v1 & v2
|
|
} else if op.op == "OR" {
|
|
val = v1 | v2
|
|
} else if op.op == "XOR" {
|
|
val = v1 ^ v2
|
|
}
|
|
|
|
cache[wire] = val
|
|
|
|
return val, nil
|
|
}
|
|
|
|
func getFirstWrong(calc Calculator) (int, map[string]int, error) {
|
|
cRes := map[string]int{}
|
|
bRes := 100
|
|
|
|
for range 10 {
|
|
cache := map[string]int{}
|
|
|
|
carry := 0
|
|
for b := range 45 {
|
|
xStr := fmt.Sprintf("x%02d", b)
|
|
yStr := fmt.Sprintf("y%02d", b)
|
|
zStr := fmt.Sprintf("z%02d", b)
|
|
|
|
cache[xStr] = rand.Intn(2)
|
|
cache[yStr] = rand.Intn(2)
|
|
|
|
x := cache[xStr]
|
|
y := cache[yStr]
|
|
|
|
zCalc, err := getWireVal(zStr, calc, cache, 0)
|
|
|
|
if err != nil {
|
|
return -1, nil, err
|
|
}
|
|
|
|
zExp := (x + y + carry) % 2
|
|
carry = (x + y + carry) / 2
|
|
|
|
if zCalc != zExp && b < bRes {
|
|
bRes = b
|
|
cRes = cache
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if bRes == 100 {
|
|
bRes = -1
|
|
}
|
|
|
|
return bRes, cRes, nil
|
|
}
|
|
|
|
func checkCalc(calc Calculator) bool {
|
|
bErr, _, err := getFirstWrong(calc)
|
|
|
|
return bErr == -1 && err == nil
|
|
}
|
|
|
|
func backtrack(b int, calc Calculator, swaps map[string]bool, ans map[string]bool) {
|
|
if len(ans) > 0 {
|
|
return
|
|
}
|
|
// fmt.Printf("Checking %v with swaps\n%v\n", b, swaps)
|
|
if len(swaps) == 8 {
|
|
if checkCalc(calc) {
|
|
for k := range swaps {
|
|
ans[k] = true
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
bErr, conns, err := getFirstWrong(calc)
|
|
|
|
if bErr < b || err != nil {
|
|
return
|
|
}
|
|
|
|
conns2 := map[string]Connections{}
|
|
|
|
for k, v := range calc.outputToSource {
|
|
conns2[k] = v
|
|
}
|
|
|
|
for c1 := range conns {
|
|
if swaps[c1] || c1[0] == 'x' || c1[0] == 'y' {
|
|
continue
|
|
}
|
|
|
|
for c2 := range conns2 {
|
|
if c2 == c1 || swaps[c2] || c2[0] == 'x' || c2[0] == 'y' {
|
|
continue
|
|
}
|
|
|
|
calc.outputToSource[c1], calc.outputToSource[c2] = calc.outputToSource[c2], calc.outputToSource[c1]
|
|
|
|
swaps[c1] = true
|
|
swaps[c2] = true
|
|
|
|
backtrack(bErr+1, calc, swaps, ans)
|
|
|
|
delete(swaps, c2)
|
|
delete(swaps, c1)
|
|
|
|
calc.outputToSource[c1], calc.outputToSource[c2] = calc.outputToSource[c2], calc.outputToSource[c1]
|
|
}
|
|
}
|
|
}
|
|
|
|
type Connections struct {
|
|
inputs []string
|
|
op string
|
|
}
|
|
|
|
type Calculator struct {
|
|
outputToSource map[string]Connections
|
|
wires map[string]int
|
|
}
|
|
|
|
func parseInput(input string) Calculator {
|
|
parts := strings.Split(input, "\n\n")
|
|
|
|
wires := map[string]int{}
|
|
for _, line := range strings.Split(parts[0], "\n") {
|
|
wires[line[:3]] = cast.ToInt(line[5:])
|
|
}
|
|
|
|
outputToSource := map[string]Connections{}
|
|
for _, line := range strings.Split(parts[1], "\n") {
|
|
lineParts := strings.Split(line, " ")
|
|
in1, op, in2, out := lineParts[0], lineParts[1], lineParts[2], lineParts[4]
|
|
outputToSource[out] = Connections{
|
|
inputs: []string{in1, in2},
|
|
op: op,
|
|
}
|
|
}
|
|
|
|
return Calculator{outputToSource, wires}
|
|
}
|