-
Notifications
You must be signed in to change notification settings - Fork 1
/
day18notes.txt
279 lines (223 loc) · 6.39 KB
/
day18notes.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# Part 1
Binary trees. The "exploding" and "splitting" rules are probably some disguised
version of some real algorithm, but I dont't recognize it.
Thinking of storage for these trees. First off, I _think_ the numbers inside will
never be more than… let's see, whenever a number gets to 10 it gets split, which
replaces it with two smaller numbers; and when a pair is too deep it explodes,
which increases up to two other numbers in the tree.
Several explosions may occur before splits, so for example this number:
[[9,[9,[9, [9,9] ]]],[[[[9,9],9],9],9]]
turns successively into:
[[9,[9,[18,0]]],[[[ [18,9] ,9],9],9]]
[[9,[9,[18,18]]],[[[0,18],9],9]]
before it starts splitting those 18s.
(thinking that a regexp may well do some of the work, not that it would be
any use to me in UXN…)
It looks like the worst case for explosion — in fact, the largest possible
snailfish number, I think — is a full tree of height 4?
[
[
[
[
[9,9],
[9,9]
],
[
[9,9],
[9,9]
]
],
[
[
[9,9],
[9,9]
],
[
[9,9],
[9,9]
]
]
],
[
[
[
[9,9],
[9,9]
],
[
[9,9],
[9,9]
]
],
[
[
[9,9],
[9,9]
],
[
[9,9],
[9,9]
]
]
]
]
This can legally be obtained by the addition of its halves, which are already reduced.
Exploding this yields:
[
[
[
[
0,
[18,9]
],
[
[9,9],
[9,9]
]
],
[
[
[9,9],
[9,9]
],
[
[9,9],
[9,9]
]
]
],
…
]
then:
[
[
[
[
18,
0
],
[
[18,9],
[9,9]
]
],
…
],
…
]
then:
[
[
[
[
18,
18
],
[
0,
[18,9]
]
],
…
],
…
]
then:
[
[
[
[
18,
18
],
[
18,
0
]
],
[
[
[18,9],
[9,9]
],
[
[9,9],
[9,9]
]
]
],
…
]
And so on, but it doesn't look like any number ever gets above 18.
I'll go ahead and use a short to store numbers, just to be safe.
"Exploding pairs will always consist of two regular numbers" guarantees that
input numbers are already reduced?
Anyway, storage. The technique of storing a complete binary tree at fixed
positions in an array, such that children are always found at indices 2i+1 and
2i+2, is seducive. But does it fit the operations I need to do?
- replacing a parent node with a leaf node: easy, this means marking the node
as a leaf and its children as empty;
- replacing a leaf node with a parent node: if the child nodes are already
allocated (i.e. 2i <= length of array) then just fill them, otherwise the
array needs to double in size to add a new level, with all nodes initialized
as unused.
- joining two trees: write a root node, then concatenate the input trees.
- scanning left and right for a leaf node: not simple, because as the tree is
stored in breadth-first order, we would have to skip nodes.
If we focus on the explode operation which needs to scan left and right, a
representation that stores the tree in preorder, i.e. what we do when
writing it as a bracketed expression, looks ideal.
With 0x00 announcing a leaf (literal) and 0x01 announcing a parent, [1,2] could
be (with parentheses to clarify, they wouldn't be stored as anything):
0x01 ( 0x00 0x0001 ) ( 0x00 0x0002 )
[[1,2],3]:
0x01 ( 0x01 ( 0x00 0x0001 ) ( 0x00 0x0001 ) ( 0x00 0x0003 ) )
and so on.
Scanning for values to increase is easy… or is it? We do have to distinguish
between parent/leaf flags and values themselves, so we can't just blindly
move a pointer and increase the value found.
What if we store parent nodes as 0x8000, and leaf nodes as their value?
[[1,2],3] becomes:
0x8000 ( 0x8000 0x0001 0x0002 ) 0x0003
Ah, this looks better, now to explode a pair we just need to scan by
increments of 2 until we reach a leaf node or the end of the array.
Example:
[[[[[9,8],1],2],3],4]
is
0x8000 0x8000 0x8000 0x8000 0x8000 0x0009 0x0008 0x0001 0x0002 0x0003 0x0004
^^^^^^
- Scanning from the left and keeping track of the depth, we'll get to the
fifth 0x0000.
- We can immediately find its left child at the next index (remember that
exploding pairs are always made up of two numbers), and scan backwards into
the array to find a leaf to add it to.
- In this case we find none.
- At the next index we find the right child, and again, we scan right
skipping 0x8000 values until we find a number or the end of the array.
- We add 0x0008 to 0x0001.
- Then we need to replace that pair with a leaf zero. We can
replace the 0x8000 with 0x0000, then all we have to do is shift the
rest of the array 4 bytes to the left, so we get:
0x8000 0x8000 0x8000 0x8000 0x0000 0x0009 0x0002 0x0003 0x0004
Reading this back, it does look like [[[[0,9],2],3],4] which is the expected
result.
What of splitting a number? This involves replacing it with 0x8000, shifting
the rest of the array 4 bytes to the right, then filling in the leaf nodes.
Shifting the array to the right may be a little more involved than shifting
to the left, but I think I can do it without too much headache.
Let's get on with it, then.
OK, parsing and printing done. This ended up simpler than I expected, thanks to
recursion.
I've finally got reduction then addition completely done. All that remains
is to compute a magnitude.
I was getting ready to go to 64-bit integers, but it turns out the final
sum from my input is:
[[[[7,6],[6,7]],[[7,7],[7,0]]],[[[7,8],[8,7]],[[7,8],[8,8]]]]
I can't really be sure, but this looks to be in the same ballpark as the last
sample sum:
[[[[6,6],[7,6]],[[7,7],[7,0]]],[[[7,7],[7,7]],[[7,8],[9,9]]]]
Whose magnitude is a modest 4140.
So I'll just start with 16-bit and see how it goes.
And the result (4235) is right! Wow, that was a long part 1.
# Part 2
Let's quickly run through all sums and see. It's only 100x100=10,000 sums.