Skip to content

Commit 8a03876

Browse files
committed
day 11, needs comments
1 parent b926850 commit 8a03876

File tree

2 files changed

+122
-125
lines changed

2 files changed

+122
-125
lines changed

11/main.go

+121-124
Original file line numberDiff line numberDiff line change
@@ -5,77 +5,28 @@ import (
55
"os"
66
"strconv"
77
"strings"
8-
"time"
98
)
109

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{}
1712
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)
2015
continue
2116
}
22-
strNum := strconv.Itoa(stone.Val)
17+
strNum := strconv.Itoa(stone)
2318
if len(strNum)%2 == 0 {
2419
l, _ := strconv.Atoi(strNum[:len(strNum)/2])
2520
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)
2722
continue
2823
}
29-
newStones = append(newStones, Stone{stone.Val * 2024, stone.Blinks + 1})
24+
newStones = append(newStones, stone*2024)
3025
}
3126

3227
return newStones
3328
}
3429

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-
7930
var stoneCache = map[int][]int{
8031
0: []int{2, 0, 2, 4},
8132
1: []int{2, 0, 2, 4},
@@ -85,7 +36,7 @@ var stoneCache = map[int][]int{
8536
5: []int{2, 0, 4, 8, 2, 8, 8, 0},
8637
6: []int{2, 4, 5, 7, 9, 4, 5, 6},
8738
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},
8940
9: []int{3, 6, 8, 6, 9, 1, 8, 4},
9041
}
9142

@@ -98,99 +49,145 @@ var blinkCache = map[int]int{
9849
5: 5,
9950
6: 5,
10051
7: 5,
101-
//8: 5,
52+
8: 5,
10253
9: 5,
10354
}
10455

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+
}
10991
}
110-
return n & (n - 1)
111-
}
11292

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)
117110
}
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
120133
}
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
134143
} 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
138148
}
139-
140-
*tot += len(nextStones) - 1
141-
blink2(nextStones, tot)
142-
continue
143149
}
150+
}
151+
return totLen
152+
}
144153

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+
}
163163

164+
cache := buildCache()
165+
tot := 0
166+
for _, stone := range stones {
167+
tot += blinkLength(stone, 25, &cache)
164168
}
165-
return
169+
fmt.Println(tot)
166170
}
167171

168172
func part2() {
169173
//raw, _ := os.ReadFile("test.txt")
170174
raw, _ := os.ReadFile("input.txt")
171175
data := string(raw)
172-
stones := []Stone{}
176+
stones := []int{}
173177
for _, char := range strings.Split(data, " ") {
174178
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)
176186
}
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)
185187
fmt.Println(tot)
186-
//fmt.Println(len(nums))
187188
}
188189

189190
func main() {
190-
start := time.Now()
191191
part1()
192-
fmt.Println(time.Since(start))
193-
start = time.Now()
194192
part2()
195-
fmt.Println(time.Since(start))
196193
}

11/test.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8
1+
125 17

0 commit comments

Comments
 (0)