Skip to content
This repository has been archived by the owner on Dec 28, 2024. It is now read-only.

Commit

Permalink
Add solution day 24 part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
terminalnode committed Dec 25, 2024
1 parent 124d753 commit 8d56482
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 2 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | ⭐ ⭐ | | |
113 changes: 112 additions & 1 deletion solutions/day24/main.go
Original file line number Diff line number Diff line change
@@ -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
}
106 changes: 106 additions & 0 deletions solutions/day24/parse.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 8d56482

Please sign in to comment.