From 8d564828a15fcb25d626322f67ee3036f7ba756d Mon Sep 17 00:00:00 2001 From: Alexander Rundberg Date: Wed, 25 Dec 2024 13:01:08 +0100 Subject: [PATCH] Add solution day 24 part 1 --- README.md | 2 +- solutions/day24/main.go | 113 ++++++++++++++++++++++++++++++++++++++- solutions/day24/parse.go | 106 ++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 solutions/day24/parse.go diff --git a/README.md b/README.md index 6946128..b6d445f 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,6 @@ automatically rebuilt and redeployed every time the `common` module or their own | 08 | ⭐ ⭐ | 21 | | | 09 | ⭐ ⭐ | 22 | ⭐ ⭐ | | 10 | ⭐ ⭐ | 23 | ⭐ ⭐ | -| 11 | ⭐ ⭐ | 24 | | +| 11 | ⭐ ⭐ | 24 | ⭐ | | 12 | ⭐ ⭐ | 25 | | | 13 | ⭐ ⭐ | | | diff --git a/solutions/day24/main.go b/solutions/day24/main.go index ea450a5..39e34df 100644 --- a/solutions/day24/main.go +++ b/solutions/day24/main.go @@ -1,9 +1,120 @@ package main import ( + "fmt" "github.com/terminalnode/adventofcode2024/common" + "log" + "slices" + "strconv" + "strings" ) func main() { - common.Setup(24, nil, nil) + common.Setup(24, part1, nil) +} + +func part1( + input string, +) string { + r, wm, err := parse(input) + if err != nil { + return fmt.Sprintf("Failed to parse input: %v", err) + } + zNames := findZIndexes(r, wm) + depSet := make(map[name]bool) + for _, zName := range zNames { + depSet[zName] = true + } + + for len(depSet) > 0 { + newDepSet := make(map[name]bool) + for dep := range depSet { + depDeps, w := resolveDeps(r, wm, dep) + for _, depDep := range depDeps { + newDepSet[depDep] = true + } + + if len(depDeps) == 1 { + w.execute(r) + } + } + depSet = newDepSet + } + + zValues := make([]string, len(zNames)) + for i, zName := range zNames { + if r[zName] { + zValues[i] = "1" + } else { + zValues[i] = "0" + } + } + slices.Reverse(zValues) + zString := strings.Join(zValues, "") + out, err := strconv.ParseInt(zString, 2, 0) + if err != nil { + return fmt.Sprintf("Failed to read %s as binary: %v", zString, err) + } + + return fmt.Sprintf("Decimal output is %d (binary %s)", out, zString) +} + +func resolveDeps( + r registry, + wm wireMap, + target name, +) ([]name, wire) { + out := make([]name, 0, 3) + if _, ok := r[target]; ok { + return out, wire{} + } + out = append(out, target) + + w, ok := wm[target] + if !ok { + log.Println("WARNING: target unresolvable", target) + return out, w + } + + if _, ok := r[w.p1]; !ok { + out = append(out, w.p1) + } + if _, ok := r[w.p2]; !ok { + out = append(out, w.p2) + } + + return out, w +} + +func (w wire) execute( + r registry, +) { + p1 := r[w.p1] + p2 := r[w.p2] + switch w.op { + case AND: + r[w.out] = p1 && p2 + case OR: + r[w.out] = p1 || p2 + case XOR: + r[w.out] = (p1 || p2) && (p1 != p2) + } +} + +func findZIndexes( + r registry, + wm wireMap, +) []name { + out := make([]name, 0, 30) + for curr := 0; ; curr++ { + key := name(fmt.Sprintf("z%02d", curr)) + _, inR := r[key] + _, inWM := wm[key] + if !inR && !inWM { + break + } + out = append(out, key) + } + slices.Sort(out) + return out } diff --git a/solutions/day24/parse.go b/solutions/day24/parse.go new file mode 100644 index 0000000..b8df832 --- /dev/null +++ b/solutions/day24/parse.go @@ -0,0 +1,106 @@ +package main + +import ( + "fmt" + "strings" +) + +type name string +type registry = map[name]bool +type wireMap = map[name]wire + +type op int + +const ( + AND = op(iota) + OR + XOR +) + +type wire struct { + p1 name + op op + p2 name + out name +} + +func parse( + input string, +) (registry, wireMap, error) { + rawRegistry, rawWires, err := splitInput(input) + outRegistry := make(registry) + outWires := make(wireMap) + if err != nil { + return outRegistry, outWires, err + } + + for _, raw := range rawRegistry { + n, v, err := parseReg(raw) + if err != nil { + return outRegistry, outWires, err + } + outRegistry[n] = v + } + + for _, raw := range rawWires { + w, err := parseWire(raw) + if err != nil { + return outRegistry, outWires, err + } + outWires[w.out] = w + } + + return outRegistry, outWires, nil +} + +func splitInput( + input string, +) ([]string, []string, error) { + split := strings.Split(input, "\n\n") + if len(split) != 2 { + return []string{}, []string{}, fmt.Errorf("failed to split input, got %d parts", len(split)) + } + + return strings.Split(split[0], "\n"), strings.Split(split[1], "\n"), nil +} + +func parseReg( + raw string, +) (name, bool, error) { + split := strings.Split(raw, ": ") + if len(split) != 2 { + return "", false, fmt.Errorf("failed to split raw registry %s, got %d parts (%v)", raw, len(split), split) + } else if split[1] != "1" && split[1] != "0" { + return "", false, fmt.Errorf("failed to parse registry %s with initial value %s", raw, split[1]) + } + + return name(split[0]), split[1] == "1", nil +} + +func parseWire( + raw string, +) (wire, error) { + split := strings.Split(raw, " ") + if len(split) != 5 { + return wire{}, fmt.Errorf("failed to split raw wire %s, got %d parts (%v)", raw, len(split), split) + } + + p1 := name(split[0]) + rawOp := split[1] + p2 := name(split[2]) + out := name(split[4]) + + var realOp op + switch rawOp { + case "AND": + realOp = AND + case "OR": + realOp = OR + case "XOR": + realOp = XOR + default: + return wire{}, fmt.Errorf("failed to parse operation %s", rawOp) + } + + return wire{p1: p1, op: realOp, p2: p2, out: out}, nil +}