@@ -4,13 +4,14 @@ import (
4
4
"fmt"
5
5
"github.com/terminalnode/adventofcode2024/common"
6
6
"log"
7
+ "math/bits"
7
8
"slices"
8
9
"strconv"
9
10
"strings"
10
11
)
11
12
12
13
func main () {
13
- common .Setup (24 , part1 , nil )
14
+ common .Setup (24 , part1 , part2 )
14
15
}
15
16
16
17
func part1 (
@@ -30,6 +31,93 @@ func part1(
30
31
return fmt .Sprintf ("Decimal output is %d (binary %s)" , out , strOut )
31
32
}
32
33
34
+ func part2 (
35
+ input string ,
36
+ ) string {
37
+ r , wm , err := parse (input )
38
+ if err != nil {
39
+ return fmt .Sprintf ("Failed to parse input: %v" , err )
40
+ }
41
+
42
+ zWrong := make ([]name , 0 )
43
+ carryWrong := make ([]name , 0 )
44
+
45
+ // Rules deduced by people who know how a ripple adder works and who have figured out the structure of the
46
+ // input. Most people just plopped it into a graphing tool or solved it with pen and paper it seems.
47
+ // I hated every second of this problem with a passion.
48
+ for out , w := range wm {
49
+ if out [0 ] == 'z' && out != "z45" && w .op != XOR {
50
+ zWrong = append (zWrong , out )
51
+ }
52
+
53
+ inputsNotXY := out [0 ] != 'z' &&
54
+ w .p1 [0 ] != 'x' && w .p1 [0 ] != 'y' &&
55
+ w .p2 [0 ] != 'x' && w .p2 [0 ] != 'y'
56
+ if inputsNotXY && out [0 ] != 'z' && w .op == XOR {
57
+ carryWrong = append (carryWrong , out )
58
+ }
59
+ }
60
+
61
+ pairs := make ([][2 ]name , 0 , 3 )
62
+ for _ , carry := range carryWrong {
63
+ zOutput := findFirstZ (carry , wm )
64
+ zNum , _ := strconv .Atoi (string (zOutput [1 :]))
65
+ pairs = append (pairs , [2 ]name {carry , name (fmt .Sprintf ("z%02d" , zNum - 1 ))})
66
+ }
67
+
68
+ xNames := findIndexes ('x' , r , wm )
69
+ yNames := findIndexes ('y' , r , wm )
70
+ zNames := findIndexes ('z' , r , wm )
71
+ resolveNames (r , wm , zNames , yNames , xNames )
72
+
73
+ xOut , _ , _ := toInt (r , xNames )
74
+ yOut , _ , _ := toInt (r , yNames )
75
+ zOut , _ , _ := toInt (r , zNames )
76
+
77
+ expected := xOut + yOut
78
+ wrongBits := zOut ^ expected
79
+ falseCarry := bits .TrailingZeros64 (uint64 (wrongBits ))
80
+
81
+ lastPair := make ([]name , 0 , 2 )
82
+ for out , w := range wm {
83
+ suffix := fmt .Sprintf ("%02d" , falseCarry )
84
+ if strings .HasSuffix (w .p1 , suffix ) &&
85
+ strings .HasSuffix (w .p2 , suffix ) {
86
+ lastPair = append (lastPair , out )
87
+ }
88
+ }
89
+
90
+ final := make ([]name , 0 , 6 )
91
+ final = append (final , zWrong ... )
92
+ final = append (final , carryWrong ... )
93
+ final = append (final , lastPair ... )
94
+ slices .Sort (final )
95
+
96
+ return strings .Join (final , "," )
97
+ }
98
+
99
+ func findFirstZ (
100
+ start name ,
101
+ wm wireMap ,
102
+ ) name {
103
+ visited := make (map [name ]bool )
104
+ current := start
105
+
106
+ for {
107
+ if current [0 ] == 'z' {
108
+ return current
109
+ }
110
+ visited [current ] = true
111
+
112
+ for _ , w := range wm {
113
+ if (w .p1 == current || w .p2 == current ) && ! visited [w .out ] {
114
+ current = w .out
115
+ break
116
+ }
117
+ }
118
+ }
119
+ }
120
+
33
121
func toInt (
34
122
r registry ,
35
123
names []name ,
@@ -66,18 +154,23 @@ func resolveNames(
66
154
}
67
155
}
68
156
69
- for len (depSet ) > 0 {
157
+ anyNew := true
158
+ for len (depSet ) > 0 && anyNew {
159
+ anyNew = false
70
160
newDepSet := make (map [name ]bool )
71
161
for dep := range depSet {
72
162
depDeps , w := resolveDeps (r , wm , dep )
73
163
for _ , depDep := range depDeps {
164
+ anyNew = anyNew || ! depSet [depDep ]
74
165
newDepSet [depDep ] = true
75
166
}
76
167
77
168
if len (depDeps ) == 1 {
78
169
w .execute (r )
79
170
}
80
171
}
172
+
173
+ anyNew = anyNew || len (depSet ) != len (newDepSet )
81
174
depSet = newDepSet
82
175
}
83
176
}
0 commit comments