@@ -5,77 +5,28 @@ import (
5
5
"os"
6
6
"strconv"
7
7
"strings"
8
- "time"
9
8
)
10
9
11
- // const totalBlinks = 12
12
- // breaks between 11 and 12
13
- const totalBlinks = 40
14
-
15
- func blink (stones []Stone ) []Stone {
16
- newStones := []Stone {}
10
+ func blink (stones []int ) []int {
11
+ newStones := []int {}
17
12
for _ , stone := range stones {
18
- if stone . Val == 0 {
19
- newStones = append (newStones , Stone { 1 , stone . Blinks + 1 } )
13
+ if stone == 0 {
14
+ newStones = append (newStones , 1 )
20
15
continue
21
16
}
22
- strNum := strconv .Itoa (stone . Val )
17
+ strNum := strconv .Itoa (stone )
23
18
if len (strNum )% 2 == 0 {
24
19
l , _ := strconv .Atoi (strNum [:len (strNum )/ 2 ])
25
20
r , _ := strconv .Atoi (strNum [len (strNum )/ 2 :])
26
- newStones = append (newStones , Stone { l , stone . Blinks + 1 }, Stone { r , stone . Blinks + 1 } )
21
+ newStones = append (newStones , l , r )
27
22
continue
28
23
}
29
- newStones = append (newStones , Stone { stone . Val * 2024 , stone . Blinks + 1 } )
24
+ newStones = append (newStones , stone * 2024 )
30
25
}
31
26
32
27
return newStones
33
28
}
34
29
35
- func part1 () {
36
- //raw, _ := os.ReadFile("test.txt")
37
- raw , _ := os .ReadFile ("input.txt" )
38
- data := string (raw )
39
- stones := []Stone {}
40
- for _ , char := range strings .Split (data , " " ) {
41
- num , _ := strconv .Atoi (char )
42
- stones = append (stones , Stone {num , 0 })
43
- }
44
-
45
- for range totalBlinks {
46
- stones = blink (stones )
47
- }
48
- fmt .Println (len (stones ))
49
- }
50
-
51
- // process each number individually, then sum lengths
52
- // once a number has an even number of digits, it will always get split down into individual digits
53
- // ie 21 -> 2,1 (2 digits, 2 steps)
54
- // ie 2048 -> 20,48 -> 2,0,4,8 (4 digits, 3 steps)
55
- // ie 28676032 -> 2867,6032 -> 28,67,60,32 -> 2,8,6,7,6,0,3,2 (8 digits, 4 steps)
56
- // ie (16 digits, 5 steps)
57
- // non factors or 2 are more complicated
58
-
59
- // I think keep cache of single digit number, then use knowledge of factor of 2's to skip steps
60
- // 0 takes 4 blinks to get to 2,0,2,4
61
- // 1,2,3,4 behave same. take 3 blinks to get to digits of n*2024 (a 4 digit number)
62
- // 5-9 behave same, take 5 blinks to get to digits of n*2024 (a 5 digit number)
63
-
64
- // have struct stone {
65
- // val int
66
- // blinks int
67
- //}
68
- // I think write function that takes in an array of stones
69
- // loop over each stone, if is single digit or power of two, use cache, and update array with new stones with value and blinks
70
- // if blinks for stone is 75 skip
71
- // if all stones have blinks of 75, done
72
- // there is a way to not actually store the whole array, but my brain hurts
73
-
74
- type Stone struct {
75
- Val int
76
- Blinks int
77
- }
78
-
79
30
var stoneCache = map [int ][]int {
80
31
0 : []int {2 , 0 , 2 , 4 },
81
32
1 : []int {2 , 0 , 2 , 4 },
@@ -85,7 +36,7 @@ var stoneCache = map[int][]int{
85
36
5 : []int {2 , 0 , 4 , 8 , 2 , 8 , 8 , 0 },
86
37
6 : []int {2 , 4 , 5 , 7 , 9 , 4 , 5 , 6 },
87
38
7 : []int {2 , 8 , 6 , 7 , 6 , 0 , 3 , 2 },
88
- // 8: []int{3, 2, 7, 7, 2, 6, 8 },
39
+ 8 : []int {3 , 2 , 7 , 7 , 2 , 6 , 16192 },
89
40
9 : []int {3 , 6 , 8 , 6 , 9 , 1 , 8 , 4 },
90
41
}
91
42
@@ -98,99 +49,145 @@ var blinkCache = map[int]int{
98
49
5 : 5 ,
99
50
6 : 5 ,
100
51
7 : 5 ,
101
- // 8: 5,
52
+ 8 : 5 ,
102
53
9 : 5 ,
103
54
}
104
55
105
- func isPowOf2 (n int ) int {
106
- //added one corner case if n is zero it will also consider as power 2
107
- if n == 0 {
108
- return 1
56
+ // map[digit][blink][]numsAtBlink
57
+ // TODO: make array instead of map
58
+ func buildCache () map [int ]map [int ]int {
59
+ cache := map [int ]map [int ]int {}
60
+ cache [0 ] = map [int ]int {}
61
+ stones := []int {0 }
62
+ for i := range 5 {
63
+ if i == 0 {
64
+ //cache[0][i] = make([]int, 0)
65
+ cache [0 ][i ] = len (stones )
66
+ } else {
67
+ stones = blink (stones )
68
+ cache [0 ][i ] = len (stones )
69
+ }
70
+
71
+ }
72
+ for j := 1 ; j <= 4 ; j ++ {
73
+ cache [j ] = map [int ]int {}
74
+ stones = []int {j }
75
+ for i := range 4 {
76
+ if i != 0 {
77
+ stones = blink (stones )
78
+ }
79
+ cache [j ][i ] = len (stones )
80
+ }
81
+ }
82
+ for j := 5 ; j <= 7 ; j ++ {
83
+ cache [j ] = map [int ]int {}
84
+ stones = []int {j }
85
+ for i := range 6 {
86
+ if i != 0 {
87
+ stones = blink (stones )
88
+ }
89
+ cache [j ][i ] = len (stones )
90
+ }
109
91
}
110
- return n & (n - 1 )
111
- }
112
92
113
- func blink2 (stones []Stone , tot * int ) {
114
- for _ , stone := range stones {
115
- if stone .Blinks > totalBlinks {
116
- fmt .Println ("uh oh" )
93
+ cache [8 ] = map [int ]int {
94
+ 0 : 1 ,
95
+ 1 : 1 ,
96
+ 2 : 1 ,
97
+ 3 : 2 ,
98
+ 4 : 4 ,
99
+ 5 : 7 ,
100
+ }
101
+
102
+ for j := 9 ; j < 10 ; j ++ {
103
+ cache [j ] = map [int ]int {}
104
+ stones = []int {j }
105
+ for i := range 6 {
106
+ if i != 0 {
107
+ stones = blink (stones )
108
+ }
109
+ cache [j ][i ] = len (stones )
117
110
}
118
- if stone .Blinks == totalBlinks {
119
- continue
111
+ }
112
+ return cache
113
+ }
114
+
115
+ // num must be single digit
116
+ func blinkLength (num int , numBlinks int , cache * map [int ]map [int ]int ) int {
117
+ if numBlinks == 0 {
118
+ return 1
119
+ }
120
+ val , ok := (* cache )[num ][numBlinks ]
121
+ // in cache, base case
122
+ if ok {
123
+ return val
124
+ }
125
+
126
+ totLen := 0
127
+ // too big
128
+ if num >= 10 {
129
+ nextStones := blink ([]int {num })
130
+ for _ , nextStone := range nextStones {
131
+ nextLength := blinkLength (nextStone , numBlinks - 1 , cache )
132
+ totLen += nextLength
120
133
}
121
- strNum := strconv .Itoa (stone .Val )
122
- // use cache
123
- if len (strNum ) == 1 && stone .Val != 8 {
124
- next := stoneCache [stone .Val ]
125
- nextBlink := stone .Blinks + blinkCache [stone .Val ]
126
- nextStones := []Stone {}
127
- // if cache would go too far, then bruteforce
128
- if nextBlink > totalBlinks {
129
- // manually step until at max blinks
130
- nextStones = []Stone {stone }
131
- for range totalBlinks - stone .Blinks {
132
- nextStones = blink (nextStones )
133
- }
134
+ } else {
135
+ // not in cache, single digit
136
+ for _ , i := range stoneCache [num ] {
137
+ if i == 16192 {
138
+ //fmt.Println("stop")
139
+ nextBlinks := numBlinks - 4
140
+ nextLength := blinkLength (8 , nextBlinks , cache )
141
+ totLen += nextLength
142
+ (* cache )[8 ][nextBlinks ] = nextLength
134
143
} else {
135
- for _ , val := range next {
136
- nextStones = append (nextStones , Stone {val , nextBlink })
137
- }
144
+ nextBlinks := numBlinks - blinkCache [num ]
145
+ nextLength := blinkLength (i , nextBlinks , cache )
146
+ totLen += nextLength
147
+ (* cache )[i ][nextBlinks ] = nextLength
138
148
}
139
-
140
- * tot += len (nextStones ) - 1
141
- blink2 (nextStones , tot )
142
- continue
143
149
}
150
+ }
151
+ return totLen
152
+ }
144
153
145
- nextStones := blink ([]Stone {stone })
146
- * tot += len (nextStones ) - 1
147
- blink2 (nextStones , tot )
148
-
149
- //if isPowOf2(len(strNum)) == 0 {
150
- // blinks := int(math.Log2(float64(len(strNum))) + 1)
151
- // digits := strings.Split(strNum, "")
152
- // nextStones := []Stone{}
153
- // for _, digit := range digits {
154
- // // TODO: maybe have to handle 1000 -> becomes 1,0,0 not 1,0,0,0
155
- // // maybe just bruteforce this part
156
- // val, _ := strconv.Atoi(digit)
157
- // nextStones = append(nextStones, Stone{val, stone.Blinks + blinks})
158
- // }
159
- // *tot += len(nextStones) - 1
160
- // blink2(nextStones, tot)
161
- // continue
162
- //}
154
+ func part1 () {
155
+ //raw, _ := os.ReadFile("test.txt")
156
+ raw , _ := os .ReadFile ("input.txt" )
157
+ data := string (raw )
158
+ stones := []int {}
159
+ for _ , char := range strings .Split (data , " " ) {
160
+ num , _ := strconv .Atoi (char )
161
+ stones = append (stones , num )
162
+ }
163
163
164
+ cache := buildCache ()
165
+ tot := 0
166
+ for _ , stone := range stones {
167
+ tot += blinkLength (stone , 25 , & cache )
164
168
}
165
- return
169
+ fmt . Println ( tot )
166
170
}
167
171
168
172
func part2 () {
169
173
//raw, _ := os.ReadFile("test.txt")
170
174
raw , _ := os .ReadFile ("input.txt" )
171
175
data := string (raw )
172
- stones := []Stone {}
176
+ stones := []int {}
173
177
for _ , char := range strings .Split (data , " " ) {
174
178
num , _ := strconv .Atoi (char )
175
- stones = append (stones , Stone {num , 0 })
179
+ stones = append (stones , num )
180
+ }
181
+
182
+ cache := buildCache ()
183
+ tot := 0
184
+ for _ , stone := range stones {
185
+ tot += blinkLength (stone , 75 , & cache )
176
186
}
177
- //for i := range 6 {
178
- // nums = blink(nums)
179
- // fmt.Println(i + 1)
180
- // fmt.Println(nums)
181
- // fmt.Println("****")
182
- //}
183
- tot := len (stones )
184
- blink2 (stones , & tot )
185
187
fmt .Println (tot )
186
- //fmt.Println(len(nums))
187
188
}
188
189
189
190
func main () {
190
- start := time .Now ()
191
191
part1 ()
192
- fmt .Println (time .Since (start ))
193
- start = time .Now ()
194
192
part2 ()
195
- fmt .Println (time .Since (start ))
196
193
}
0 commit comments