OSDN Git Service

Update docs.
[joypy/Thun.git] / docs / Advent_of_Code_2017_December_2nd.rst
1
2 Advent of Code 2017
3 ===================
4
5 December 2nd
6 ------------
7
8 For each row, determine the difference between the largest value and the
9 smallest value; the checksum is the sum of all of these differences.
10
11 For example, given the following spreadsheet:
12
13 ::
14
15     5 1 9 5
16     7 5 3
17     2 4 6 8
18
19 -  The first row's largest and smallest values are 9 and 1, and their
20    difference is 8.
21 -  The second row's largest and smallest values are 7 and 3, and their
22    difference is 4.
23 -  The third row's difference is 6.
24
25 In this example, the spreadsheet's checksum would be 8 + 4 + 6 = 18.
26
27 .. code:: ipython2
28
29     from notebook_preamble import J, V, define
30
31 I'll assume the input is a Joy sequence of sequences of integers.
32
33 ::
34
35     [[5 1 9 5]
36      [7 5 3]
37      [2 4 6 8]]
38
39 So, obviously, the initial form will be a ``step`` function:
40
41 ::
42
43     AoC2017.2 == 0 swap [F +] step
44
45 This function ``F`` must get the ``max`` and ``min`` of a row of numbers
46 and subtract. We can define a helper function ``maxmin`` which does
47 this:
48
49 .. code:: ipython2
50
51     define('maxmin == [max] [min] cleave')
52
53 .. code:: ipython2
54
55     J('[1 2 3] maxmin')
56
57
58 .. parsed-literal::
59
60     3 1
61
62
63 Then ``F`` just does that then subtracts the min from the max:
64
65 ::
66
67     F == maxmin -
68
69 So:
70
71 .. code:: ipython2
72
73     define('AoC2017.2 == [maxmin - +] step_zero')
74
75 .. code:: ipython2
76
77     J('''
78     
79     [[5 1 9 5]
80      [7 5 3]
81      [2 4 6 8]] AoC2017.2
82     
83     ''')
84
85
86 .. parsed-literal::
87
88     18
89
90
91 ...find the only two numbers in each row where one evenly divides the
92 other - that is, where the result of the division operation is a whole
93 number. They would like you to find those numbers on each line, divide
94 them, and add up each line's result.
95
96 For example, given the following spreadsheet:
97
98 ::
99
100     5 9 2 8
101     9 4 7 3
102     3 8 6 5
103
104 -  In the first row, the only two numbers that evenly divide are 8 and
105    2; the result of this division is 4.
106 -  In the second row, the two numbers are 9 and 3; the result is 3.
107 -  In the third row, the result is 2.
108
109 In this example, the sum of the results would be 4 + 3 + 2 = 9.
110
111 What is the sum of each row's result in your puzzle input?
112
113 .. code:: ipython2
114
115     J('[5 9 2 8] sort reverse')
116
117
118 .. parsed-literal::
119
120     [9 8 5 2]
121
122
123 .. code:: ipython2
124
125     J('[9 8 5 2] uncons [swap [divmod] cons] dupdip')
126
127
128 .. parsed-literal::
129
130     [8 5 2] [9 divmod] [8 5 2]
131
132
133 ::
134
135     [9 8 5 2] uncons [swap [divmod] cons F] dupdip G
136       [8 5 2]            [9 divmod]      F [8 5 2] G
137
138 .. code:: ipython2
139
140     V('[8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip')
141
142
143 .. parsed-literal::
144
145                                           . [8 5 2] [9 divmod] [uncons swap] dip dup [i not] dip
146                                   [8 5 2] . [9 divmod] [uncons swap] dip dup [i not] dip
147                        [8 5 2] [9 divmod] . [uncons swap] dip dup [i not] dip
148          [8 5 2] [9 divmod] [uncons swap] . dip dup [i not] dip
149                                   [8 5 2] . uncons swap [9 divmod] dup [i not] dip
150                                   8 [5 2] . swap [9 divmod] dup [i not] dip
151                                   [5 2] 8 . [9 divmod] dup [i not] dip
152                        [5 2] 8 [9 divmod] . dup [i not] dip
153             [5 2] 8 [9 divmod] [9 divmod] . [i not] dip
154     [5 2] 8 [9 divmod] [9 divmod] [i not] . dip
155                        [5 2] 8 [9 divmod] . i not [9 divmod]
156                                   [5 2] 8 . 9 divmod not [9 divmod]
157                                 [5 2] 8 9 . divmod not [9 divmod]
158                                 [5 2] 1 1 . not [9 divmod]
159                             [5 2] 1 False . [9 divmod]
160                  [5 2] 1 False [9 divmod] . 
161
162
163 Tricky
164 ------
165
166 Let's think.
167
168 Given a *sorted* sequence (from highest to lowest) we want to \* for
169 head, tail in sequence \* for term in tail: \* check if the head % term
170 == 0 \* if so compute head / term and terminate loop \* else continue
171
172 So we want a ``loop`` I think
173 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
174
175 ::
176
177     [a b c d] True [Q] loop
178     [a b c d] Q    [Q] loop
179
180 ``Q`` should either leave the result and False, or the ``rest`` and
181 True.
182
183 ::
184
185        [a b c d] Q
186     -----------------
187         result 0
188
189        [a b c d] Q
190     -----------------
191         [b c d] 1
192
193 This suggests that ``Q`` should start with:
194
195 ::
196
197     [a b c d] uncons dup roll<
198     [b c d] [b c d] a
199
200 Now we just have to ``pop`` it if we don't need it.
201
202 ::
203
204     [b c d] [b c d] a [P] [T] [cons] app2 popdd [E] primrec
205     [b c d] [b c d] [a P] [a T]                 [E] primrec
206
207 --------------
208
209 ::
210
211     w/ Q == [% not] [T] [F] primrec
212
213             [a b c d] uncons
214             a [b c d] tuck
215     [b c d] a [b c d] uncons
216     [b c d] a b [c d] roll>
217     [b c d] [c d] a b Q
218     [b c d] [c d] a b [% not] [T] [F] primrec
219
220     [b c d] [c d] a b T
221     [b c d] [c d] a b / roll> popop 0
222
223     [b c d] [c d] a b F                   Q
224     [b c d] [c d] a b pop swap uncons ... Q
225     [b c d] [c d] a       swap uncons ... Q
226     [b c d] a [c d]            uncons ... Q
227     [b c d] a c [d]                   roll> Q
228     [b c d] [d] a c Q
229
230     Q == [% not] [/ roll> popop 0] [pop swap uncons roll>] primrec
231
232     uncons tuck uncons roll> Q
233
234 .. code:: ipython2
235
236     J('[8 5 3 2] 9 [swap] [% not] [cons] app2 popdd')
237
238
239 .. parsed-literal::
240
241     [8 5 3 2] [9 swap] [9 % not]
242
243
244 --------------
245
246 ::
247
248             [a b c d] uncons
249             a [b c d] tuck
250     [b c d] a [b c d] [not] [popop 1] [Q] ifte
251
252     [b c d] a [] popop 1
253     [b c d] 1
254
255     [b c d] a [b c d] Q 
256
257
258        a [...] Q
259     ---------------
260        result 0
261
262        a [...] Q
263     ---------------
264            1
265
266
267     w/ Q == [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
268
269
270
271     a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
272     a [b c d]  first % not
273     a b % not
274     a%b not
275     bool(a%b)
276
277     a [b c d] [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
278     a [b c d]                first / 0
279     a b / 0
280     a/b 0
281
282     a [b c d] [first % not] [first / 0] [rest [not] [popop 1]]   [ifte]
283     a [b c d]                            rest [not] [popop 1] [Q] ifte
284     a [c d]                                   [not] [popop 1] [Q] ifte
285     a [c d]                                   [not] [popop 1] [Q] ifte
286
287     a [c d] [not] [popop 1] [Q] ifte
288     a [c d]  not
289
290     a [] popop 1
291     1
292
293     a [c d] Q
294
295
296     uncons tuck [first % not] [first / 0] [rest [not] [popop 1]] [ifte]
297
298 I finally sat down with a piece of paper and blocked it out.
299 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
300
301 First, I made a function ``G`` that expects a number and a sequence of
302 candidates and return the result or zero:
303
304 ::
305
306        n [...] G
307     ---------------
308         result
309
310        n [...] G
311     ---------------
312            0
313
314 It's a recursive function that conditionally executes the recursive part
315 of its recursive branch
316
317 ::
318
319     [Pg] [E] [R1 [Pi] [T]] [ifte] genrec
320
321 The recursive branch is the else-part of the inner ``ifte``:
322
323 ::
324
325     G == [Pg] [E] [R1 [Pi] [T]]   [ifte] genrec
326       == [Pg] [E] [R1 [Pi] [T] [G] ifte] ifte
327
328 But this is in hindsight. Going forward I derived:
329
330 ::
331
332     G == [first % not]
333          [first /]
334          [rest [not] [popop 0]]
335          [ifte] genrec
336
337 The predicate detects if the ``n`` can be evenly divided by the
338 ``first`` item in the list. If so, the then-part returns the result.
339 Otherwise, we have:
340
341 ::
342
343     n [m ...] rest [not] [popop 0] [G] ifte
344     n [...]        [not] [popop 0] [G] ifte
345
346 This ``ifte`` guards against empty sequences and returns zero in that
347 case, otherwise it executes ``G``.
348
349 .. code:: ipython2
350
351     define('G == [first % not] [first /] [rest [not] [popop 0]] [ifte] genrec')
352
353 Now we need a word that uses ``G`` on each (head, tail) pair of a
354 sequence until it finds a (non-zero) result. It's going to be designed
355 to work on a stack that has some candidate ``n``, a sequence of possible
356 divisors, and a result that is zero to signal to continue (a non-zero
357 value implies that it is the discovered result):
358
359 ::
360
361        n [...] p find-result
362     ---------------------------
363               result
364
365 It applies ``G`` using ``nullary`` because if it fails with one
366 candidate it needs the list to get the next one (the list is otherwise
367 consumed by ``G``.)
368
369 ::
370
371     find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec
372
373     n [...] p [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec
374
375 The base-case is trivial, return the (non-zero) result. The recursive
376 branch...
377
378 ::
379
380     n [...] p roll< popop uncons [G] nullary find-result
381     [...] p n       popop uncons [G] nullary find-result
382     [...]                 uncons [G] nullary find-result
383     m [..]                       [G] nullary find-result
384     m [..] p                                 find-result
385
386 The puzzle states that the input is well-formed, meaning that we can
387 expect a result before the row sequence empties and so do not need to
388 guard the ``uncons``.
389
390 .. code:: ipython2
391
392     define('find-result == [0 >] [roll> popop] [roll< popop uncons [G] nullary] primrec')
393
394 .. code:: ipython2
395
396     J('[11 9 8 7 3 2] 0 tuck find-result')
397
398
399 .. parsed-literal::
400
401     3.0
402
403
404 In order to get the thing started, we need to ``sort`` the list in
405 descending order, then prime the ``find-result`` function with a dummy
406 candidate value and zero ("continue") flag.
407
408 .. code:: ipython2
409
410     define('prep-row == sort reverse 0 tuck')
411
412 Now we can define our program.
413
414 .. code:: ipython2
415
416     define('AoC20017.2.extra == [prep-row find-result +] step_zero')
417
418 .. code:: ipython2
419
420     J('''
421     
422     [[5 9 2 8]
423      [9 4 7 3]
424      [3 8 6 5]] AoC20017.2.extra
425     
426     ''')
427
428
429 .. parsed-literal::
430
431     9.0
432