This repository has been archived by the owner on Nov 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
feed.xml
2777 lines (2681 loc) · 657 KB
/
feed.xml
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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="UTF-16"?>
<!--This file generated by build script at ./Build.hs in solutions repository-->
<rss version="2.0">
<channel>
<title>Justin Le's Advent of Code 2020 Reflections</title>
<description>Reflections for my Advent of Code solutions as I try to solve them all in fun ways using Haskell!</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md</link>
<copyright>Copyright 2020 Justin Le</copyright>
<language>en-us</language>
<lastBuildDate>Wed, 1 Dec 2021 18:34:59 EST</lastBuildDate>
<managingEditor>[email protected]</managingEditor>
<pubDate>Wed, 1 Dec 2021 18:34:59 EST</pubDate>
<webMaster>[email protected]</webMaster>
<generator>Shake + Template</generator>
<item>
<title>Day 25</title>
<description><h2 id="day-25">Day 25</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day25.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/25">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day25.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day25.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day25.md">Standalone Reflection Page</a></em></p>
<p>Merry Christmas everyone, it’s December 25th :D</p>
<p>The Christmas Problem is usually supposed to be a quick and concise one, since Eric wants people to spend the holiday with their family. This one is a bit obscured in the jargon, but once you sort through it, the solution ends up being pretty tidy :)</p>
<p>In the end you are exponentiating the number 7 by a given number of times (the loop count) to get the number you see. So you’re solving <code>7^x = &lt;your number&gt;</code>…so that’s basically a logarithm!</p>
<p>The <em><a href="https://hackage.haskell.org/package/arithmoi">arithmoi</a></em> library (which I previously used in problems like Day 13) offers a nice discrete logarithm function, so that’s really all we need to use:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Magic</span> <span class="ot">=</span> <span class="dv">20201227</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot">magicGroup ::</span> <span class="dt">CyclicGroup</span> <span class="dt">Integer</span> <span class="dt">Magic</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="dt">Just</span> magicGroup <span class="ot">=</span> cyclicGroup</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ot">primBase ::</span> <span class="dt">PrimitiveRoot</span> <span class="dt">Magic</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="dt">Just</span> primBase <span class="ot">=</span> isPrimitiveRoot magicGroup <span class="dv">7</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ot">findSecret ::</span> <span class="dt">Mod</span> <span class="dt">Magic</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Natural</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>findSecret <span class="ot">=</span> <span class="fu">fmap</span> (discreteLogarithm magicGroup primBase)</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> isMultElement</span></code></pre></div>
<p>And so our final solution is just (after converting the input numbers to the <code>Mod Magic</code> data type)…</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">day25 ::</span> <span class="dt">Mod</span> <span class="dt">Magic</span> <span class="ot">-&gt;</span> <span class="dt">Mod</span> <span class="dt">Magic</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Integer</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>day52 x y <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> secret <span class="ot">&lt;-</span> findSecret x</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> <span class="op">.</span> getVal <span class="op">$</span> y <span class="op">^%</span> secret <span class="co">-- exponentiate by the loop count</span></span></code></pre></div>
<p>Merry Christmas to everyone, and happy New Years too. Thank you for reading these reflections, and I hope they have been helpful in some way :) Special thanks to Eric Wastl too for such a great event as always. Until next year!</p>
<h3 id="day-25-benchmarks">Day 25 Benchmarks</h3>
<pre><code>&gt;&gt; Day 25a
benchmarking...
time 1.997 ms (1.971 ms .. 2.023 ms)
0.998 R² (0.997 R² .. 0.999 R²)
mean 2.042 ms (2.019 ms .. 2.066 ms)
std dev 73.99 μs (63.56 μs .. 100.2 μs)
variance introduced by outliers: 22% (moderately inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-25</link>
<pubDate>Fri, 25 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 24</title>
<description><h2 id="day-24">Day 24</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day24.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/24">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day24.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day24.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day24.md">Standalone Reflection Page</a></em></p>
<p>Day 24 brings us our third cellular automata puzzle of the year! :D The other ones were <a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day11.md">Day 11</a> and <a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day17.md">Day 17</a>. In fact, I was able to mostly copy and paste my stepper code for Day 17 :)</p>
<p>The main twist here is that we’d have to use hexy stepping and hexy neighbors. My initial solve used the <em><a href="https://hackage.haskell.org/package/grid">grid</a></em> library to get the hexy steps neighbors, but I did go back and <a href="https://github.com/mhwombat/grid/wiki/Implementation%3A-Hexagonal-tiles">implement the tiling myself</a> because it wasn’t too bad :)</p>
<p>For part 1, it can be nice to have some intermediate data types</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">HexDirection</span> <span class="ot">=</span> <span class="dt">West</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Northwest</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Northeast</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">East</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Southeast</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Southwest</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="ot">toDirs ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> [<span class="dt">HexDirection</span>]</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>toDirs <span class="ot">=</span> \<span class="kw">case</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> [] <span class="ot">-&gt;</span> <span class="dt">Just</span> []</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> <span class="ch">&#39;w&#39;</span><span class="op">:</span>ds <span class="ot">-&gt;</span> (<span class="dt">West</span><span class="op">:</span>) <span class="op">&lt;$&gt;</span> toDirs ds</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="ch">&#39;e&#39;</span><span class="op">:</span>ds <span class="ot">-&gt;</span> (<span class="dt">East</span><span class="op">:</span>) <span class="op">&lt;$&gt;</span> toDirs ds</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="ch">&#39;n&#39;</span><span class="op">:</span><span class="ch">&#39;e&#39;</span><span class="op">:</span>ds <span class="ot">-&gt;</span> (<span class="dt">Northeast</span><span class="op">:</span>) <span class="op">&lt;$&gt;</span> toDirs ds</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="ch">&#39;n&#39;</span><span class="op">:</span><span class="ch">&#39;w&#39;</span><span class="op">:</span>ds <span class="ot">-&gt;</span> (<span class="dt">Northwest</span><span class="op">:</span>) <span class="op">&lt;$&gt;</span> toDirs ds</span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a> <span class="ch">&#39;s&#39;</span><span class="op">:</span><span class="ch">&#39;e&#39;</span><span class="op">:</span>ds <span class="ot">-&gt;</span> (<span class="dt">Southeast</span><span class="op">:</span>) <span class="op">&lt;$&gt;</span> toDirs ds</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> <span class="ch">&#39;s&#39;</span><span class="op">:</span><span class="ch">&#39;w&#39;</span><span class="op">:</span>ds <span class="ot">-&gt;</span> (<span class="dt">Southwest</span><span class="op">:</span>) <span class="op">&lt;$&gt;</span> toDirs ds</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> _ <span class="ot">-&gt;</span> <span class="dt">Nothing</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="ot">hexOffset ::</span> <span class="dt">HexDirection</span> <span class="ot">-&gt;</span> <span class="dt">Point</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>hexOffset <span class="ot">=</span> \<span class="kw">case</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">West</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> (<span class="op">-</span><span class="dv">1</span>) <span class="dv">0</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> <span class="dt">Northwest</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> (<span class="op">-</span><span class="dv">1</span>) <span class="dv">1</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">Northeast</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> <span class="dv">0</span> <span class="dv">1</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">East</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> <span class="dv">1</span> <span class="dv">0</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> <span class="dt">Southeast</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> <span class="dv">1</span> (<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> <span class="dt">Southwest</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> <span class="dv">0</span> (<span class="op">-</span><span class="dv">1</span>)</span></code></pre></div>
<p>So we can parse into a list of <code>[HexDirection]</code> paths, and then we can get our starting points by xoring all of the final points:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Bits</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="ot">initialize ::</span> [[<span class="dt">HexDirection</span>]] <span class="ot">-&gt;</span> <span class="dt">Set</span> <span class="dt">Point</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>initialize <span class="ot">=</span> M.keysSet <span class="op">.</span> M.filter <span class="fu">id</span> <span class="op">.</span> M.fromListWith xor</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> <span class="fu">map</span> (\steps <span class="ot">-&gt;</span> (<span class="fu">sum</span> (<span class="fu">map</span> hexOffset steps), <span class="dt">True</span>))</span></code></pre></div>
<p>And this gives us the set of all active points, which we can use to answer part one. But now, on to the simulation!</p>
<p>First, we can expand the neighbors of a given point in our hexy coords:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">neighbors ::</span> <span class="dt">Point</span> <span class="ot">-&gt;</span> <span class="dt">Set</span> <span class="dt">Point</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>neighbors (<span class="dt">V2</span> x y) <span class="ot">=</span> S.fromDistinctAscList</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> [ <span class="dt">V2</span> (x<span class="op">-</span><span class="dv">1</span>) y</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> , <span class="dt">V2</span> (x<span class="op">-</span><span class="dv">1</span>) (y<span class="op">+</span><span class="dv">1</span>)</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> , <span class="dt">V2</span> x (y<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> , <span class="dt">V2</span> x (y<span class="op">+</span><span class="dv">1</span>)</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> , <span class="dt">V2</span> (x<span class="op">+</span><span class="dv">1</span>) (y<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> , <span class="dt">V2</span> (x<span class="op">+</span><span class="dv">1</span>) y</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>And our step function looks more or less the same as day 17:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">step ::</span> <span class="dt">Set</span> <span class="dt">Point</span> <span class="ot">-&gt;</span> <span class="dt">Set</span> <span class="dt">Point</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>step ps <span class="ot">=</span> stayAlive <span class="op">&lt;&gt;</span> comeAlive</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="ot"> neighborCounts ::</span> <span class="dt">Map</span> <span class="dt">Point</span> <span class="dt">Int</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> neighborCounts <span class="ot">=</span> M.unionsWith (<span class="op">+</span>)</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> [ M.fromSet (<span class="fu">const</span> <span class="dv">1</span>) (neighbors p)</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> p <span class="ot">&lt;-</span> S.toList ps</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> stayAlive <span class="ot">=</span> M.keysSet <span class="op">.</span> M.filter (\n <span class="ot">-&gt;</span> n <span class="op">==</span> <span class="dv">1</span> <span class="op">||</span> n <span class="op">==</span> <span class="dv">2</span>) <span class="op">$</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> neighborCounts <span class="ot">`M.restrictKeys`</span> ps</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> comeAlive <span class="ot">=</span> M.keysSet <span class="op">.</span> M.filter (<span class="op">==</span> <span class="dv">2</span>) <span class="op">$</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> neighborCounts <span class="ot">`M.withoutKeys`</span> ps</span></code></pre></div>
<p>First we collect a <code>Map Point Int</code> of each point to how many live neighbors it has. Then the <em>live</em> points (<code>neighborCounts `M.restrictKeys` ps</code>) are filtered for only the ones with 1 or 2 live neighbors, and the <em>dead</em> points (<code>neighborCounts `M.withoutKeys` ps</code>) are filtered for only the ones with 2 live neighbors. And the resulting new set of live points is <code>stayAlive &lt;&gt; comeAlive</code>.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">part1 ::</span> [[<span class="dt">HexDirection</span>]] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>part1 <span class="ot">=</span> S.size <span class="op">.</span> initialize</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="ot">part2 ::</span> [[<span class="dt">HexDirection</span>]] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>part2 paths <span class="ot">=</span> S.size (<span class="fu">iterate</span> step pts <span class="op">!!!</span> <span class="dv">100</span>)</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> pts <span class="ot">=</span> initialize paths</span></code></pre></div>
<h3 id="day-24-benchmarks">Day 24 Benchmarks</h3>
<pre><code>&gt;&gt; Day 24a
benchmarking...
time 2.597 ms (2.551 ms .. 2.639 ms)
0.996 R² (0.993 R² .. 0.998 R²)
mean 2.579 ms (2.545 ms .. 2.614 ms)
std dev 111.4 μs (82.30 μs .. 141.5 μs)
variance introduced by outliers: 28% (moderately inflated)
&gt;&gt; Day 24b
benchmarking...
time 272.1 ms (247.2 ms .. 296.5 ms)
0.996 R² (0.996 R² .. 1.000 R²)
mean 273.7 ms (264.8 ms .. 286.5 ms)
std dev 13.87 ms (1.266 ms .. 18.02 ms)
variance introduced by outliers: 16% (moderately inflated)</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-24</link>
<pubDate>Thu, 24 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 23</title>
<description><h2 id="day-23">Day 23</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day23.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/23">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day23.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day23.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day23.md">Standalone Reflection Page</a></em></p>
<p>Day 23 – this one definitely stumped me a while, and it was the first one to take me more than 24 hours!</p>
<p>Part 1 was straightforward enough with the circular <a href="https://hackage.haskell.org/package/pointedlist">Pointed List</a>, and was pretty fun indeed. But the main problem with extrapolating this to part 2 was the crucial “slow part”: finding the index of the “preceding” cup. Using a circular pointed list makes it very fast to do things like take 3 cups and insert 3 cups where you want them, but the tough thing is finding <em>where</em> you want to re-insert them: if you pick up cup #3, then where is cup #2? My circular pointed list (and my later mutable vector based one, among other attempts) all suffered from that same problem: re-arranging the cups is fast, but I couldn’t figure out a way to know where to place them without doing a full linear search. And this was tractable for 10 cups, but pretty much impossible for 1 million cups – especially since the location of the ‘preceding cup’ soon became very far from the current cup (it goes to the full 500k pretty quickly!)</p>
<p>In frustration, I implemented a mutable circularly linked list library…but found the same problem: I could easily take and insert, but no easy way to find out where the preceding cup was without doing an item-by-item traversal.</p>
<p>The breakthrough finally came when I thought about attaching a pointer to the preceding cup’s cell to each linked list cell — a “backdoor” pointer that skips across the circularly linked list. This should be doable because the structure of “preceding cup” is fixed – it won’t ever change, and so this pointer should also be fixed as well as you shuffle everything over it. I had the visual imagery of “pulling” the three taken cups up back “through” the backdoor pointer, and everything seemed very efficient, since the main inefficiency (finding the preceding cup) was fixed.</p>
<p>Unfortunately I am not skilled enough in pointer manipulation and other imperative programming intricacies to be able to implement this in a nice way. So I stepped back and thought about just “reifying” this pointer structure into an array of indices (pointers), where the addresses were indices.</p>
<p>Each cell would have to contain:</p>
<ol type="1">
<li>The index of the cup to the right</li>
<li>The index of the preceding cup</li>
</ol>
<p>Only…#2 doesn’t need to actually be a part of the cell, because it’s fixed and never mutates. So we only need to have each cell hold #1, and use some sort of scheme to get #2.</p>
<p>And then that’s when it hit me — if I simply stored Cup #1 at index 0, Cup #2 at index 1, Cup #3 at index 2, etc…then #2 is simply “the previous index”! So in the end we only need an array of indices, where each index corresponds to that cup. The “preceding-cup” structure is fixed, and we only need to update the “cup to the right” pointers!</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Finite</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Vector.Mutable.Sized</span> <span class="kw">as</span> <span class="dt">MV</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Vector.Sized</span> <span class="kw">as</span> <span class="dt">V</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">CrabState</span> n s <span class="ot">=</span> <span class="dt">MV.MVector</span> n s (<span class="dt">Finite</span> n)</span></code></pre></div>
<p>Our data structure will be a million-sized mutable vector where index <code>i</code> stores the index (cup number, essentially) of the cup labeled <code>i</code> (technically, <code>i+1</code>). We can use <code>Finite n</code> (<code>Finite 1000000</code> in our case) for our index size because it is constrained to be between 0 and 999999, and subtracting past 0 wraps back up to 999999 like we’d want it to.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>step</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="kw">forall</span> n m s<span class="op">.</span> (<span class="dt">KnownNat</span> n, <span class="dt">PrimMonad</span> m, <span class="dt">PrimState</span> m <span class="op">~</span> s)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">CrabState</span> n s</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Finite</span> n <span class="co">-- ^ current pointer</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">Finite</span> n) <span class="co">-- ^ next pointer</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>step cs lab <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="co">-- pull out the next three cups, and the cup fourth to the right</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> (gs<span class="op">@</span>[g1,_,g3],lab&#39;) <span class="ot">&lt;-</span> pull3 lab</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="co">-- update the &quot;cup-to-the-right&quot; of the pointer cup</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a> MV.write cs lab lab&#39;</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="co">-- find the first valid &quot;preceding cup&quot;</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> target <span class="ot">=</span> <span class="fu">until</span> (<span class="ot">`notElem`</span> gs) (<span class="fu">subtract</span> <span class="dv">1</span>) (lab <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> <span class="co">-- what cup is to the right of the target cup?</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> aftertarg <span class="ot">&lt;-</span> MV.read cs target</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> <span class="co">-- pointer shuffling: the target cup should point to the pulled cups</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> MV.write cs target g1</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> <span class="co">-- .. and the final pulled cup should point to where the target cup pointed to originally</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> MV.write cs g3 aftertarg</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> lab&#39;</span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a><span class="ot"> pull3 ::</span> <span class="dt">Finite</span> n <span class="ot">-&gt;</span> m ([<span class="dt">Finite</span> n], <span class="dt">Finite</span> n)</span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a> pull3 i0 <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a> i1 <span class="ot">&lt;-</span> MV.read cs i0</span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a> i2 <span class="ot">&lt;-</span> MV.read cs i1</span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a> i3 <span class="ot">&lt;-</span> MV.read cs i2</span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a> i4 <span class="ot">&lt;-</span> MV.read cs i3</span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> ([i1,i2,i3],i4)</span></code></pre></div>
<p>Now we just need to initialize from a fully allocated vector by writing at each index the value of the previous cell:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>initialize</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="kw">forall</span> n m s<span class="op">.</span> (<span class="dt">KnownNat</span> n, <span class="dt">PrimMonad</span> m, <span class="dt">PrimState</span> m <span class="op">~</span> s)</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">V.Vector</span> n (<span class="dt">Finite</span> n) <span class="co">-- ^ vector, organized left-to-right</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m (<span class="dt">Finite</span> n, <span class="dt">CrabState</span> n s) <span class="co">-- ^ initial pointer</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>initialize v0 <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> cs <span class="ot">&lt;-</span> MV.new</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> for_ finites <span class="op">$</span> \i <span class="ot">-&gt;</span> <span class="co">-- iterate over each index</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> MV.write cs (v0 <span class="op">V.!</span> (i <span class="op">-</span> <span class="dv">1</span>)) (v0 <span class="op">V.!</span> i)</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> i0 <span class="ot">=</span> v0 <span class="ot">`V.index`</span> <span class="dv">0</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (i0, cs)</span></code></pre></div>
<p>And now a function to mutate our crab state a given number of points, from an initial pointer index:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">run ::</span> (<span class="dt">KnownNat</span> n, <span class="dt">PrimMonad</span> m, <span class="dt">PrimState</span> m <span class="op">~</span> s)</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Int</span> <span class="co">-- ^ number of steps</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Finite</span> n <span class="co">-- ^ initial index</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CrabState</span> n s</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m ()</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>run n i0 cs <span class="ot">=</span> go <span class="dv">0</span> i0</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> go m i</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> m <span class="op">==</span> n <span class="ot">=</span> <span class="fu">pure</span> ()</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> go (m <span class="op">+</span> <span class="dv">1</span>) <span class="op">=&lt;&lt;</span> step cs i</span></code></pre></div>
<p>And maybe some functions to read out the actual answers:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>numbersFrom1</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Int</span> <span class="co">-- ^ how many numbers to pull</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">CrabState</span> n s</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> m [<span class="dt">Finite</span> n]</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>numbersFrom1 n cs <span class="ot">=</span> go <span class="dv">0</span> <span class="dv">0</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> go m i</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> m <span class="op">==</span> n <span class="ot">=</span> <span class="fu">pure</span> []</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> nxt <span class="ot">&lt;-</span> MV.read cs i</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> (nxt<span class="op">:</span>) <span class="op">&lt;$&gt;</span> go (m<span class="op">+</span><span class="dv">1</span>) nxt</span></code></pre></div>
<p>And we have our full pipeline, remembering that we have to subtract 1 to get the index of a cup from the cup number:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot">part1 ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> [<span class="dt">Int</span>]</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>part1 cs0 <span class="ot">=</span> runST <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> cs <span class="ot">&lt;-</span> initialize v0</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> run <span class="dv">100</span> <span class="dv">0</span> cs</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> (<span class="op">+</span> <span class="dv">1</span>) <span class="op">.</span> <span class="fu">fromIntegral</span> <span class="op">&lt;$&gt;</span> numbersFrom1 <span class="dv">9</span> cs</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="ot"> v0 ::</span> <span class="dt">V.Vector</span> <span class="dv">10</span> (<span class="dt">Finite</span> <span class="dv">10</span>)</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> v0 <span class="ot">=</span> V.fromList <span class="op">$</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">fromIntegral</span> <span class="op">.</span> <span class="fu">subtract</span> <span class="dv">1</span> <span class="op">&lt;$&gt;</span> cs0</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a><span class="ot">part2 ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>part2 cs0 <span class="ot">=</span> runST <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> cs <span class="ot">&lt;-</span> initialize v0</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a> run <span class="dv">10000000</span> <span class="dv">0</span> cs</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> [x,y] <span class="ot">&lt;-</span> (<span class="op">+</span> <span class="dv">1</span>) <span class="op">.</span> <span class="fu">fromIntegral</span> <span class="op">&lt;$&gt;</span> numbersFrom1 <span class="dv">2</span> cs</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> (x <span class="op">*</span> y)</span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="ot"> v0 ::</span> <span class="dt">V.Vector</span> <span class="dv">1000000</span> (<span class="dt">Finite</span> <span class="dv">1000000</span>)</span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> v0 <span class="ot">=</span> V.fromList <span class="op">$</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> (<span class="fu">fromIntegral</span> <span class="op">.</span> <span class="fu">subtract</span> <span class="dv">1</span> <span class="op">&lt;$&gt;</span> cs0)</span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a> <span class="op">++</span> [<span class="dv">9</span><span class="op">..</span>]</span></code></pre></div>
<p>Overall, a very fun puzzle that required a bunch of interesting data structure and representation breakthroughs to tackle :)</p>
<h3 id="day-23-benchmarks">Day 23 Benchmarks</h3>
<pre><code>&gt;&gt; Day 23a
benchmarking...
time 4.469 μs (4.420 μs .. 4.544 μs)
0.997 R² (0.993 R² .. 1.000 R²)
mean 4.452 μs (4.424 μs .. 4.542 μs)
std dev 181.5 ns (39.87 ns .. 343.3 ns)
variance introduced by outliers: 53% (severely inflated)
* parsing and formatting times excluded
&gt;&gt; Day 23b
benchmarking...
time 194.3 ms (190.4 ms .. 196.6 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 195.4 ms (194.3 ms .. 198.1 ms)
std dev 2.172 ms (125.3 μs .. 3.023 ms)
variance introduced by outliers: 14% (moderately inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-23</link>
<pubDate>Wed, 23 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 22</title>
<description><h2 id="day-22">Day 22</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day22.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/22">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day22.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day22.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day22.md">Standalone Reflection Page</a></em></p>
<p>This one can be a fun exercise in explicit/direct tail recursion :) It’s a straightforward implementation of an “imperative” algorithm, but we actually gain a lot from implementing our imperative algorithm in a purely functional setting, and can write something that runs faster than we might write in a language with implicit mutation. Immutability can be an optimization, since our data structures are designed around sharing and avoiding deep clones, so storing references and caches to old values are extremely cheap. I explain more about this at the end, but it’s nice that we can get the advantages of imperative programming without most of the drawbacks of implicit mutation slowing down our code.</p>
<p>This problem is also a nice showcase of Haskell’s standard “queue” data type, <code>Seq</code> from <em><a href="https://hackage.haskell.org/package/containers/docs/Data-Sequence.html">Data.Sequence</a></em>, with O(1) pushing and popping from both ends.</p>
<p>I decided to write a function that I could use to parameterize on for both parts.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Sequence</span> (<span class="dt">Seq</span>(..))</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Sequence.NonEmpty</span> (<span class="dt">NESeq</span>(..))</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Sequence</span> <span class="kw">as</span> <span class="dt">Seq</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Deck</span> <span class="ot">=</span> <span class="dt">Seq</span> <span class="dt">Int</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">NEDeck</span> <span class="ot">=</span> <span class="dt">NESeq</span> <span class="dt">Int</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Player</span> <span class="ot">=</span> <span class="dt">P1</span> <span class="op">|</span> <span class="dt">P2</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>playGameWith</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">NEDeck</span> <span class="ot">-&gt;</span> <span class="dt">NEDeck</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Player</span>) <span class="co">-- ^ handler</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="co">-- ^ p1 starting deck</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="co">-- ^ p2 starting deck</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> (<span class="dt">Player</span>, <span class="dt">Deck</span>) <span class="co">-- ^ winner and deck</span></span></code></pre></div>
<p>The handler function will let us specify how to handle the situation when both decks are non-empty (represented by <em><a href="https://hackage.haskell.org/package/nonempty-containers/docs/Data-Sequence-NonEmpty.html">Data.Sequence.NonEmpty</a></em>). If returns <code>Nothing</code>, we defer to the higher-card-wins <a href="https://en.wikipedia.org/wiki/War_(card_game)">War</a> rules, and if it returns <code>Just</code>, we take that <code>Player</code> as the winner of that round.</p>
<p>For part 1, we always defer to the higher-card-wins rule, so we can ignore our decks and return <code>Nothing</code>.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">game1 ::</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> (<span class="dt">Player</span>, <span class="dt">Deck</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>game1 <span class="ot">=</span> playGameWith <span class="op">$</span> \_ _ <span class="ot">-&gt;</span> <span class="dt">Nothing</span></span></code></pre></div>
<p>For part 2, we want to play a game with the tops of the decks given to us, but only if we have enough cards.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">game2 ::</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> (<span class="dt">Player</span>, <span class="dt">Deck</span>)</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>game2 <span class="ot">=</span> playGameWith <span class="op">$</span> \(x <span class="op">:&lt;||</span> xs) (y <span class="op">:&lt;||</span> ys) <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> xs&#39; <span class="ot">&lt;-</span> takeExactly x xs</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> ys&#39; <span class="ot">&lt;-</span> takeExactly y ys</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> <span class="op">$</span> <span class="fu">fst</span> (game2 xs&#39; ys&#39;)</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="ot">takeExactly ::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Seq</span> a <span class="ot">-&gt;</span> <span class="dt">Maybe</span> (<span class="dt">Seq</span> a)</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>takeExactly n xs <span class="ot">=</span> Seq.take n xs <span class="op">&lt;$</span> guard (Seq.length xs <span class="op">&gt;=</span> n)</span></code></pre></div>
<p>If we don’t have enough items to take exactly <code>x</code> items from <code>xs</code>, then we fail and defer to higher-card-wins rules (and same for <code>y</code> and <code>ys</code>). Otherwise, we play a <code>game2</code> with the exactly-sized deck tops to determine the winner. The way the recursion is structured here is pretty night because there is a loop between the two function pointers (<code>game2</code>, and the lambda passed to it), so we can go back and forth between them without allocating new functions.</p>
<p>Now the only thing left is to actually write <code>playGameWith</code> :D This one is not too bad if we use a helper function to make sure things stay tail-recursive so we don’t accidentally leak space. We also would like to make sure we keep the <em>same</em> top-level <code>f</code> in the closure for the whole time, so that the recursive call in <code>go</code> to <code>go</code> will go <em>exactly</em> back to its own function pointer.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Set</span> (<span class="dt">Set</span>)</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Set</span> <span class="kw">as</span> <span class="dt">S</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>playGameWith</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">NEDeck</span> <span class="ot">-&gt;</span> <span class="dt">NEDeck</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Player</span>) <span class="co">-- ^ handler</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="co">-- ^ p1 starting deck</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="co">-- ^ p2 starting deck</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> (<span class="dt">Player</span>, <span class="dt">Deck</span>) <span class="co">-- ^ winner and deck</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>playGameWith f <span class="ot">=</span> go S.empty</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="ot"> go ::</span> <span class="dt">Set</span> (<span class="dt">Deck</span>, <span class="dt">Deck</span>) <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> (<span class="dt">Player</span>, <span class="dt">Deck</span>)</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> go <span class="op">!</span>seen <span class="op">!</span>xs0 <span class="op">!</span>ys0</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> (xs0, ys0) <span class="ot">`S.member`</span> seen <span class="ot">=</span> (<span class="dt">P1</span>, xs0)</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="kw">case</span> (xs0, ys0) <span class="kw">of</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a> (x <span class="op">:&lt;|</span> xs, y <span class="op">:&lt;|</span> ys) <span class="ot">-&gt;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> winner <span class="ot">=</span> <span class="kw">case</span> f (x <span class="op">:&lt;||</span> xs) (y <span class="op">:&lt;||</span> ys) <span class="kw">of</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="kw">if</span> x <span class="op">&gt;</span> y <span class="kw">then</span> <span class="dt">P1</span> <span class="kw">else</span> <span class="dt">P2</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> p <span class="ot">-&gt;</span> p</span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> <span class="kw">case</span> winner <span class="kw">of</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">P1</span> <span class="ot">-&gt;</span> go seen&#39; (xs <span class="op">:|&gt;</span> x <span class="op">:|&gt;</span> y) ys</span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">P2</span> <span class="ot">-&gt;</span> go seen&#39; xs (ys <span class="op">:|&gt;</span> y <span class="op">:|&gt;</span> x)</span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Empty</span>, _ ) <span class="ot">-&gt;</span> (<span class="dt">P2</span>, ys0)</span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a> (_ , <span class="dt">Empty</span>) <span class="ot">-&gt;</span> (<span class="dt">P1</span>, xs0)</span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a> seen&#39; <span class="ot">=</span> S.insert (xs0, ys0) seen</span></code></pre></div>
<p>Most of this implementation follows the logic straightforwardly, remembering to use <code>f</code> to give the callback a chance to “intercept” the “highest card win” rule if it wants to. We get a lot of mileage here out of the <code>:&lt;|</code>, <code>:|&gt;</code> and <code>Empty</code> constructors for <code>Seq</code>, which allows us to match on the head and tail or an empty <code>Seq</code> as a pattern. Note that this isn’t <em>perfectly</em> tail-recursive – we do get another layer of data allocated whenever we recurse into a game. But at least it’s tail-recursive within the same game.</p>
<p>Note that this talk about tail recursion isn’t because we are afraid of overflowing the call stack like in other languages (and trying to take advantage of tail-call optimization) — the value in tail recursion is that we can stay constant-space on the heap (since haskell function calls go on the heap, not a call stack).</p>
<p>This works, but we can make it a little faster in a way that only purely functional languages can benefit from. Checking for seen decks in a <code>Set (Deck, Deck)</code> can be pretty expensive in such a tight loop, and it’s definitely the bottleneck of our loop. One quick optimization we can do is use an <code>IntSet</code> instead of a <code>Set</code>, and store a “hash” (really, partition index) of our data:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>hashHand ;<span class="op">:</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>hashHand xs ys <span class="ot">=</span> hash (<span class="fu">take</span> <span class="dv">2</span> (toList xs), <span class="fu">take</span> <span class="dv">2</span> (toList ys), <span class="fu">length</span> xs)</span></code></pre></div>
<p>So instead of checking if a hand pair has been seen before, we can only check <code>hashHand xs0 ys0 `IS.member` seen</code>, and <code>IS.insert (hashHand xs0 ys0) seen</code> at every step. This becomes very efficient (takes my time from 1.8s down to 8ms), effectively eliminating the main bottleneck.</p>
<p>However, this method is mathematically unsound because it’s possible for two different decks to “hash” to the same <code>Int</code>. It didn’t happen in my own input, but it happened when solving the game for one of my friend’s inputs.</p>
<p>Instead what we can do is implement “hash set”, with easy negative checks, and expensive positive checks — but those should only happen basically once per <em>game</em>, and not once per round. We can store a <code>IntMap (Set (Deck, Deck))</code>:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot">go ::</span> <span class="dt">IntMap</span> (<span class="dt">Set</span> (<span class="dt">Deck</span>, <span class="dt">Deck</span>)) <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> <span class="dt">Deck</span> <span class="ot">-&gt;</span> (<span class="dt">Player</span>, <span class="dt">Deck</span>)</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>go <span class="op">!</span>seen <span class="op">!</span>xs0 <span class="op">!</span>ys0</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> collision <span class="ot">=</span> (<span class="dt">P1</span>, xs0)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="kw">case</span> (xs0, ys0) <span class="kw">of</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> (x <span class="op">:&lt;|</span> xs, y <span class="op">:&lt;|</span> ys) <span class="ot">-&gt;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> winner <span class="ot">=</span> <span class="kw">case</span> f (x <span class="op">:&lt;||</span> xs) (y <span class="op">:&lt;||</span> ys) <span class="kw">of</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="kw">if</span> x <span class="op">&gt;</span> y <span class="kw">then</span> <span class="dt">P1</span> <span class="kw">else</span> <span class="dt">P2</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> p <span class="ot">-&gt;</span> p</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> <span class="kw">case</span> winner <span class="kw">of</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">P1</span> <span class="ot">-&gt;</span> go seen&#39; (xs <span class="op">:|&gt;</span> x <span class="op">:|&gt;</span> y) ys</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">P2</span> <span class="ot">-&gt;</span> go seen&#39; xs (ys <span class="op">:|&gt;</span> y <span class="op">:|&gt;</span> x)</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Empty</span>, _ ) <span class="ot">-&gt;</span> (<span class="dt">P2</span>, ys0)</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> (_ , <span class="dt">Empty</span>) <span class="ot">-&gt;</span> (<span class="dt">P1</span>, xs0)</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> collision <span class="ot">=</span> <span class="kw">case</span> IM.lookup (hashHand xs0 ys0) seen <span class="kw">of</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> <span class="dt">False</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> s <span class="ot">-&gt;</span> (xs0, ys0) <span class="ot">`S.member`</span> s</span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> seen&#39; <span class="ot">=</span> IM.insertWith (<span class="op">&lt;&gt;</span>) (hashHand xs0 ys0) (S.singleton (xs0, ys0)) seen</span></code></pre></div>
<p>Note storing the <code>(Deck, Deck)</code> in our <code>IntMap</code> is very expensive if we are using in-place mutation for our decks: we’d have to do a full copy of our decks <em>every round</em> to store them into our set, because mutating them will change them. In the purely functional case, we don’t have to do anything special because no values are ever mutated — the reference to our old data is already there!</p>
<p>In addition, inserting/popping values off of a <code>Seq</code> does <em>not</em> require a full copy: because <code>Seq</code> is internally a <a href="https://en.wikipedia.org/wiki/Finger_tree">finger tree</a> (a purely functional persistent data structure optimized for these operations), adding a new value does not require a full copy, but instead allocates very little because most of your “new” tree’s internal nodes are pointing at references to the original tree. So no copying is ever made, and storing these <code>Seq</code>s in our <code>IntMap</code> is essentially just storing a pointer.</p>
<p>This is one of the nice ways which immutability can give us performance increases! These are always fun to highlight because there’s some common fantasy that immutability = slower, when in reality it’s often an <em>optimization</em>.</p>
<h3 id="day-22-benchmarks">Day 22 Benchmarks</h3>
<pre><code>&gt;&gt; Day 22a
benchmarking...
time 230.6 μs (228.1 μs .. 236.1 μs)
0.998 R² (0.993 R² .. 1.000 R²)
mean 228.9 μs (227.4 μs .. 233.8 μs)
std dev 7.584 μs (798.7 ns .. 15.31 μs)
variance introduced by outliers: 29% (moderately inflated)
* parsing and formatting times excluded
&gt;&gt; Day 22b
benchmarking...
time 7.770 ms (7.469 ms .. 8.183 ms)
0.988 R² (0.976 R² .. 0.999 R²)
mean 7.805 ms (7.666 ms .. 7.963 ms)
std dev 398.9 μs (262.8 μs .. 568.5 μs)
variance introduced by outliers: 25% (moderately inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-22</link>
<pubDate>Tue, 22 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 21</title>
<description><h2 id="day-21">Day 21</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day21.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/21">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day21.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day21.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day21.md">Standalone Reflection Page</a></em></p>
<p>Another nice self-contained constraint satisfaction problem, along the lines of <a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day16.md">Day 16</a> :) Actually, after solving this one, I went back and rewrote my day 16 solution in terms of a common solver function that works for both!</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Given a map of @k@ to possible @a@s for that @k@, find possible</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- configurations where each @k@ is given its own unique @a@.</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ot">pickUnique ::</span> (<span class="dt">Ord</span> k, <span class="dt">Ord</span> a) <span class="ot">=&gt;</span> [(k, <span class="dt">Set</span> a)] <span class="ot">-&gt;</span> [<span class="dt">Map</span> k a]</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>pickUnique mp <span class="ot">=</span> <span class="fu">flip</span> evalStateT S.empty <span class="op">$</span> <span class="kw">do</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="fu">fmap</span> M.fromList <span class="op">.</span> for opts <span class="op">.</span> <span class="fu">traverse</span> <span class="op">$</span> \poss <span class="ot">-&gt;</span> <span class="kw">do</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> seen <span class="ot">&lt;-</span> get</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> pick <span class="ot">&lt;-</span> lift <span class="op">$</span> S.toList (poss <span class="ot">`S.difference`</span> seen)</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> pick <span class="op">&lt;$</span> modify (S.insert pick)</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a> opts <span class="ot">=</span> sortOn (S.size <span class="op">.</span> <span class="fu">snd</span>) mp</span></code></pre></div>
<p>It uses <code>StateT</code> over list, like I described in <a href="https://blog.jle.im/entry/unique-sample-drawing-searches-with-list-and-statet.html">a constraint solving blog post</a>. Basically it explores all of the possibilities of drawing from a state of “items left-over to assign”. The state is a <code>Set a</code> of items not yet picked, and at every step we non-deterministically <code>pick</code> an <code>a</code> out of the given <code>(k, Set a)</code> of options that hasn’t already been chosen. We use that pick and add that picked item to the picked item set along that branch.</p>
<p>We also sort by the size of the possibility set for each <code>k</code>, because starting with smaller possibilities keeps our tree tight at the top, instead of wide — we can eliminate options much more quickly.</p>
<p>Now all we need to do is to get our information into a <code>[(k, Set a)]</code>. In our case, this is <code>[(String, Set String)]</code> – with each allergen, associate a set of possible foods they might be associated with.</p>
<p>We can do this by just taking an intersection of all the possibilities on each line:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>assembleOptions</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> (<span class="dt">Ord</span> k, <span class="dt">Ord</span> a)</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> [(<span class="dt">Set</span> a, <span class="dt">Set</span> k)] <span class="co">-- set of foods, set of allergens</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Map</span> k (<span class="dt">Set</span> a) <span class="co">-- each allergen with the foods they were seen with in all occurrences</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>assembleOptions info <span class="ot">=</span> M.unionsWith S.intersection <span class="op">$</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> [ M.fromSet (<span class="fu">const</span> igr) alg <span class="co">-- a map of allergens to all foods they were seen with in this item</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> (igr, alg) <span class="ot">&lt;-</span> info</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>We generate a list of allergens to all foods they were seen with on each item, and then <code>intersect</code> all of those foods within an allergen, so that our final <code>Map k (Set a)</code> matches each <code>k</code> allergen with a set ofall foods that were present in <em>all</em> of the occurrences of each allergen.</p>
<p>Now part 2 is basically just reading off the results of <code>pickUnique</code></p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">part2 ::</span> [(<span class="dt">Set</span> <span class="dt">String</span>, <span class="dt">Set</span> <span class="dt">String</span>)] <span class="ot">-&gt;</span> <span class="dt">Maybe</span> [<span class="dt">String</span>]</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>part2 <span class="ot">=</span> <span class="fu">fmap</span> M.elems <span class="op">.</span> listToMaybe <span class="op">.</span> pickUnique <span class="op">.</span> assembleOptions</span></code></pre></div>
<p>We definitely have a nice advantage here in that the <code>Map String String</code> (the result map of allergens to foods) already is sorted in order of allergens (alphabetically), so no need to do anything other than just <code>M.elems</code> :)</p>
<p>Part 1 is definitely slightly more complicated: not only do we need to find the allergenic foods, we have to count the occurrences of non-allergenic foods in all the items:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">part2 ::</span> [(<span class="dt">Set</span> <span class="dt">String</span>, <span class="dt">Set</span> <span class="dt">String</span>)] <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Int</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>part2 info <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> allergenicFoods <span class="ot">&lt;-</span> <span class="fu">fmap</span> (S.fromList <span class="op">.</span> M.elems)</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> listToMaybe</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> pickUnique</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span> assembleOptions</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="op">$</span> info</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">pure</span> <span class="op">.</span> <span class="fu">sum</span> <span class="op">$</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> [ <span class="fu">length</span> <span class="op">$</span> <span class="fu">filter</span> (<span class="ot">`S.notMember`</span> allergenicFoods) foods</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> (foods, _) <span class="ot">&lt;-</span> info</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="ot"> allFoodOccurrences ::</span> [<span class="dt">String</span>]</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> allFoodOccurrences <span class="ot">=</span> <span class="fu">concatMap</span> (S.toList <span class="op">.</span> <span class="fu">fst</span>) info</span></code></pre></div>
<h3 id="day-21-benchmarks">Day 21 Benchmarks</h3>
<pre><code>&gt;&gt; Day 21a
benchmarking...
time 270.6 μs (267.0 μs .. 277.0 μs)
0.997 R² (0.994 R² .. 0.999 R²)
mean 273.1 μs (269.2 μs .. 283.4 μs)
std dev 22.37 μs (8.162 μs .. 40.92 μs)
variance introduced by outliers: 71% (severely inflated)
* parsing and formatting times excluded
&gt;&gt; Day 21b
benchmarking...
time 162.9 μs (160.4 μs .. 165.9 μs)
0.997 R² (0.994 R² .. 1.000 R²)
mean 160.2 μs (158.4 μs .. 165.3 μs)
std dev 9.685 μs (3.385 μs .. 17.84 μs)
variance introduced by outliers: 59% (severely inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-21</link>
<pubDate>Mon, 21 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 20</title>
<description><h2 id="day-20">Day 20</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day20.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/20">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day20.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day20.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day20.md">Standalone Reflection Page</a></em></p>
<p>Ah, the infamous Day 20 :) I actually went through a few different possible solutions for this before settling on the one I have now. It also pushed me to flesh out my “direction manipulation” mini-library (that I used <a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day12.md">Day 12</a>) to be a full “orientation manipulation” mini-library. With it, I get to enumerate, manipulate, and combine the eight possible orientations of a 2d square grid in a nice way.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Dir</span> <span class="ot">=</span> <span class="dt">North</span> <span class="op">|</span> <span class="dt">East</span> <span class="op">|</span> <span class="dt">South</span> <span class="op">|</span> <span class="dt">West</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Rotate a point by a direction</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ot">rotPoint ::</span> <span class="dt">Num</span> a <span class="ot">=&gt;</span> <span class="dt">Dir</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> a <span class="ot">-&gt;</span> <span class="dt">V2</span> a</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="ot">allDir ::</span> [<span class="dt">Dir</span>]</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>allDir <span class="ot">=</span> [<span class="dt">North</span> <span class="op">..</span>]</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- All of these instances are described in my day 12 writeup</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Semigroup</span> <span class="dt">Dir</span> <span class="kw">where</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Monoid</span> <span class="dt">Dir</span> <span class="kw">where</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Group</span> <span class="dt">Dir</span> <span class="kw">where</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Abelian</span> <span class="dt">Dir</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="co">-- | A possible orientation (flip and rotate) of a 2d square grid</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">D8</span> <span class="ot">=</span> <span class="dt">D8</span> {<span class="ot"> d8Rot ::</span> <span class="dt">Dir</span>,<span class="ot"> d8Flip ::</span> <span class="dt">Bool</span> }</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Semigroup</span> <span class="dt">D8</span> <span class="kw">where</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> x1 <span class="dt">False</span> <span class="op">&lt;&gt;</span> <span class="dt">D8</span> x2 y2 <span class="ot">=</span> <span class="dt">D8</span> (x1 <span class="op">&lt;&gt;</span> x2) y2</span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> x1 <span class="dt">True</span> <span class="op">&lt;&gt;</span> <span class="dt">D8</span> x2 y2 <span class="ot">=</span> <span class="dt">D8</span> (x1 <span class="op">&lt;&gt;</span> invert x2) (<span class="fu">not</span> y2)</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Monoid</span> <span class="dt">D8</span> <span class="kw">where</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> <span class="fu">mempty</span> <span class="ot">=</span> <span class="dt">D8</span> <span class="dt">North</span> <span class="dt">False</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Group</span> <span class="dt">D8</span> <span class="kw">where</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> invert (<span class="dt">D8</span> x <span class="dt">False</span>) <span class="ot">=</span> <span class="dt">D8</span> (invert x) <span class="dt">False</span></span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> invert (<span class="dt">D8</span> x <span class="dt">True</span> ) <span class="ot">=</span> <span class="dt">D8</span> x <span class="dt">True</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a><span class="ot">allD8 ::</span> [<span class="dt">D8</span>]</span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>allD8 <span class="ot">=</span> <span class="dt">D8</span> <span class="op">&lt;$&gt;</span> allDir <span class="op">&lt;*&gt;</span> [<span class="dt">False</span>, <span class="dt">True</span>]</span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Rotate and flip a point by a &#39;D8&#39;</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a><span class="ot">orientPoint ::</span> <span class="dt">Num</span> a <span class="ot">=&gt;</span> <span class="dt">D8</span> <span class="ot">-&gt;</span> <span class="dt">V2</span> a <span class="ot">-&gt;</span> <span class="dt">V2</span> a</span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a>orientPoint <span class="ot">=</span> \<span class="kw">case</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">North</span> <span class="dt">False</span> <span class="ot">-&gt;</span> <span class="fu">id</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">East</span> <span class="dt">False</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> y (<span class="op">-</span>x)</span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">West</span> <span class="dt">False</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> (<span class="op">-</span>y) x</span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">South</span> <span class="dt">False</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> (<span class="op">-</span>x) (<span class="op">-</span>y)</span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">North</span> <span class="dt">True</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> (<span class="op">-</span>x) y</span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">East</span> <span class="dt">True</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> y x</span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">West</span> <span class="dt">True</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> (<span class="op">-</span>y) (<span class="op">-</span>x)</span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a> <span class="dt">D8</span> <span class="dt">South</span> <span class="dt">True</span> <span class="ot">-&gt;</span> \(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> <span class="dt">V2</span> x (<span class="op">-</span>y)</span></code></pre></div>
<p>Having orientations as a data type I can manipulate as first-class values helped me “think” my way through everything a little easier.</p>
<p>First things first, we can break apart a 10x10 tile into the parts that actually matter: its eight edges (which we can represent as a set of <code>Finite 10</code>s) and its core (which we can represent as a set of <code>V2 (Finite 8)</code>, 8x8 points). I’m using <code>Finite</code> from <em><a href="https://hackage.haskell.org/package/finite-typelits">finite-typelits</a></em> mostly as a way for me to keep track of what I have at each stage — remember that <code>Finite 8</code>, for instance, is one of 0,1,2,3,4,5,6, or 7. This is also handy because the library gives us <code>strengthen &lt;=&lt; unshift :: Finite 10 -&gt; Maybe (Finite 8)</code>, that lets us “chop off” the outer edges of a <code>Set (Finite 10)</code> to get the <code>Set (Finite 8)</code> core.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Edge</span> <span class="ot">=</span> <span class="dt">Set</span> (<span class="dt">Finite</span> <span class="dv">10</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Core</span> <span class="ot">=</span> <span class="dt">Set</span> (<span class="dt">V2</span> (<span class="dt">Finite</span> <span class="dv">8</span>))</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- | Shift corner to (0,0)</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="ot">shiftToZero ::</span> (<span class="dt">Applicative</span> f, <span class="dt">Num</span> a, <span class="dt">Ord</span> a) <span class="ot">=&gt;</span> <span class="dt">Set</span> (<span class="dt">V2</span> a) <span class="ot">-&gt;</span> <span class="dt">Set</span> (<span class="dt">V2</span> a)</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- | mapMaybe but for sets</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="ot">mapMaybeSet ::</span> <span class="dt">Ord</span> b <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> <span class="dt">Maybe</span> b) <span class="ot">-&gt;</span> <span class="dt">Set</span> a <span class="ot">-&gt;</span> <span class="dt">Set</span> b</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>toTiles</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Set</span> (<span class="dt">V2</span> (<span class="dt">Finite</span> <span class="dv">10</span>))</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> ((<span class="dt">Core</span>, <span class="dt">D8</span> <span class="ot">-&gt;</span> <span class="dt">Edge</span>), <span class="dt">Map</span> <span class="dt">Edge</span> <span class="dt">D8</span>)</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>toTiles ps <span class="ot">=</span> ((core, getEdge), M.toList (<span class="fu">map</span> swap oToEdge))</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> core <span class="ot">=</span> mapMaybeSet (<span class="fu">traverse</span> (strengthen <span class="op">&lt;=&lt;</span> unshift)) ps</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> getEdge o <span class="ot">=</span> oMap <span class="op">M.!</span> o</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> oMap <span class="ot">=</span> M.fromList oToEdge</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> oToEdge <span class="ot">=</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> [ (o, mapMaybeSet (\(<span class="dt">V2</span> x y) <span class="ot">-&gt;</span> x <span class="op">&lt;$</span> guard (y <span class="op">==</span> <span class="dv">0</span>)) ps&#39;)</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> o <span class="ot">&lt;-</span> allD8</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a> , <span class="kw">let</span> ps&#39; <span class="ot">=</span> shiftToZero <span class="op">$</span> orientPoint (invert o) <span class="ot">`S.map`</span> ps</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>Both “orientation to edge at that orientation” (<code>D8 -&gt; Edge</code>) and “edge to the orientation that that edge exists at” (<code>Map Edge D8</code>) are useful things to have, so we can generate them both here.</p>
<p>Once we do this we can get three separate <code>IntMap</code>s after parsing the file:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">IntMap</span> <span class="dt">Core</span> <span class="co">-- a map of tile id&#39;s to their cores (for drawing)</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="dt">IntMap</span> (<span class="dt">D8</span> <span class="ot">-&gt;</span> <span class="dt">Edge</span>) <span class="co">-- a map of tile id&#39;s to their edges at each orientation</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="dt">IntMap</span> (<span class="dt">Map</span> <span class="dt">Edge</span> <span class="dt">D8</span>) <span class="co">-- a map of tile id&#39;s to all of their edges and the orientations they are at</span></span></code></pre></div>
<p>Now for the actual solve — we’re going to build up a <code>Map Point (Int, D8)</code> one at a time, where the point (<code>V2 Int</code>) is going to contain the tile id at that point, as well as the orientation that tile has to be at.</p>
<p>To do that, we’re going to use a queue of “open edges”: the location that the open edge is facing, and the direction (north/south/east/west) of that open edge – a <code>Map Edge (Point, Dir)</code>. We’ll also keep a set of tile id’s that have not been placed yet. And then at each step:</p>
<ol type="1">
<li>Pop an edge off of that queue – <code>(Edge, (Point, Dir))</code></li>
<li>Search to see if any non-used tiles have any matching edge
<ol type="a">
<li>If there is not any, it means that that edge is at the edge of the overall map, so just skip.</li>
<li>If there is a tile, place that tile at the indicated <code>(Point, Dir)</code> and place all of <em>its</em> edges into the queue.</li>
</ol></li>
<li>Repeat until the queue is empty.</li>
</ol>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | A placement is a Tile ID and the orientation of that tile</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Placement</span> <span class="ot">=</span> (<span class="dt">Int</span>, <span class="dt">D8</span>)</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Point</span> <span class="ot">=</span> <span class="dt">V2</span> <span class="dt">Int</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>assembleMap</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">IntMap</span> (<span class="dt">D8</span> <span class="ot">-&gt;</span> <span class="dt">Edge</span>) <span class="co">-- ^ tile id to the edge at each orientation</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">IntMap</span> (<span class="dt">Map</span> <span class="dt">Edge</span> <span class="dt">Placement</span>) <span class="co">-- ^ tile id to the map of edges to what tile id, orientation that edge is at</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Map</span> <span class="dt">Point</span> <span class="dt">Placement</span> <span class="co">-- ^ map of points to the tile id, orientation at each point</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>assembleMap tileMap tiles0 <span class="ot">=</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a> go (toQueue <span class="dv">0</span> <span class="fu">mempty</span> t0id allDir)</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a> (IM.keysSet tiles1)</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a> (M.singleton <span class="dv">0</span> (t0id, <span class="fu">mempty</span>))</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a> <span class="co">-- populate the initial tile and the initial queue</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a> ((_ , t0Map), tiles1) <span class="ot">=</span> IM.deleteFindMin tiles0</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a> ((_, (t0id, _)), _ ) <span class="ot">=</span> M.deleteFindMin t0Map</span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a> <span class="co">-- a cache of edges to tiles ID&#39;s (and orientations) that have that edge.</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a><span class="ot"> tileCache ::</span> <span class="dt">Map</span> <span class="dt">Edge</span> [<span class="dt">Placement</span>]</span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a> tileCache <span class="ot">=</span> M.fromListWith (<span class="op">++</span>)</span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a> [ (edge, [placement])</span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> (_ , tileEdges) <span class="ot">&lt;-</span> IM.toList tiles0</span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a> , (edge, placement) <span class="ot">&lt;-</span> M.toList tileEdges</span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a><span class="ot"> go ::</span> <span class="dt">Map</span> <span class="dt">Edge</span> (<span class="dt">Point</span>, <span class="dt">Dir</span>) <span class="co">-- ^ queue: edge -&gt; place, orientation</span></span>
<span id="cb4-27"><a href="#cb4-27" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">IntSet</span> <span class="co">-- ^ leftover points</span></span>
<span id="cb4-28"><a href="#cb4-28" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Map</span> <span class="dt">Point</span> <span class="dt">Placement</span> <span class="co">-- ^ current map</span></span>
<span id="cb4-29"><a href="#cb4-29" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Map</span> <span class="dt">Point</span> <span class="dt">Placement</span> <span class="co">-- ^ sweet tail rescursion</span></span>
<span id="cb4-30"><a href="#cb4-30" aria-hidden="true" tabindex="-1"></a> go queue tiles mp <span class="ot">=</span> <span class="kw">case</span> M.minViewWithKey queue <span class="kw">of</span></span>
<span id="cb4-31"><a href="#cb4-31" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> mp</span>
<span id="cb4-32"><a href="#cb4-32" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> ((edge, (pos, d)), queue&#39;) <span class="ot">-&gt;</span></span>
<span id="cb4-33"><a href="#cb4-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">case</span> find ((<span class="ot">`IS.member`</span> tiles) <span class="op">.</span> <span class="fu">fst</span>) (tileCache <span class="op">NEM.!</span> edge) <span class="kw">of</span></span>
<span id="cb4-34"><a href="#cb4-34" aria-hidden="true" tabindex="-1"></a> <span class="dt">Nothing</span> <span class="ot">-&gt;</span> go queue&#39; tiles mp</span>
<span id="cb4-35"><a href="#cb4-35" aria-hidden="true" tabindex="-1"></a> <span class="dt">Just</span> (tileId, o) <span class="ot">-&gt;</span></span>
<span id="cb4-36"><a href="#cb4-36" aria-hidden="true" tabindex="-1"></a> <span class="co">-- If we&#39;re adding a North edge, then it&#39;s the new tile&#39;s South</span></span>
<span id="cb4-37"><a href="#cb4-37" aria-hidden="true" tabindex="-1"></a> <span class="co">-- edge; if we are adding a East edge, it&#39;s the new tile&#39;s West</span></span>
<span id="cb4-38"><a href="#cb4-38" aria-hidden="true" tabindex="-1"></a> <span class="co">-- edge, etc; (d &lt;&gt; South) is the right relationship to properly</span></span>
<span id="cb4-39"><a href="#cb4-39" aria-hidden="true" tabindex="-1"></a> <span class="co">-- flip</span></span>
<span id="cb4-40"><a href="#cb4-40" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> o&#39; <span class="ot">=</span> o <span class="op">&lt;&gt;</span> <span class="dt">D8</span> (d <span class="op">&lt;&gt;</span> <span class="dt">South</span>) <span class="dt">True</span></span>
<span id="cb4-41"><a href="#cb4-41" aria-hidden="true" tabindex="-1"></a> newQueue <span class="ot">=</span> toQueue pos o&#39;</span>
<span id="cb4-42"><a href="#cb4-42" aria-hidden="true" tabindex="-1"></a> tileId</span>
<span id="cb4-43"><a href="#cb4-43" aria-hidden="true" tabindex="-1"></a> (<span class="fu">filter</span> (<span class="op">/=</span> d <span class="op">&lt;&gt;</span> <span class="dt">South</span>) allDir)</span>
<span id="cb4-44"><a href="#cb4-44" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> go (newQueue <span class="op">&lt;&gt;</span> queue)</span>
<span id="cb4-45"><a href="#cb4-45" aria-hidden="true" tabindex="-1"></a> (IS.delete tileId tiles)</span>
<span id="cb4-46"><a href="#cb4-46" aria-hidden="true" tabindex="-1"></a> (M.insert pos (tileId, invert o&#39;) mp)</span>
<span id="cb4-47"><a href="#cb4-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-48"><a href="#cb4-48" aria-hidden="true" tabindex="-1"></a> <span class="co">-- | For a given image, add the given edges into the queue</span></span>
<span id="cb4-49"><a href="#cb4-49" aria-hidden="true" tabindex="-1"></a> toQueue</span>
<span id="cb4-50"><a href="#cb4-50" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Foldable</span> f</span>
<span id="cb4-51"><a href="#cb4-51" aria-hidden="true" tabindex="-1"></a> <span class="ot">=&gt;</span> <span class="dt">Point</span> <span class="co">-- ^ location of corner</span></span>
<span id="cb4-52"><a href="#cb4-52" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">D8</span> <span class="co">-- ^ orientation to insert</span></span>
<span id="cb4-53"><a href="#cb4-53" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="co">-- ^ tile id</span></span>
<span id="cb4-54"><a href="#cb4-54" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> f <span class="dt">Dir</span> <span class="co">-- ^ edges to insert</span></span>
<span id="cb4-55"><a href="#cb4-55" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Map</span> <span class="dt">Edge</span> (<span class="dt">Point</span>, <span class="dt">Dir</span>)</span>
<span id="cb4-56"><a href="#cb4-56" aria-hidden="true" tabindex="-1"></a> toQueue p0 o tileId ds <span class="ot">=</span> M.fromList <span class="op">$</span> ds <span class="op">&lt;&amp;&gt;</span> \d <span class="ot">-&gt;</span> <span class="co">-- for each dir</span></span>
<span id="cb4-57"><a href="#cb4-57" aria-hidden="true" tabindex="-1"></a> ( (tileMap <span class="op">IM.!</span> tileId) (o <span class="op">&lt;&gt;</span> <span class="dt">D8</span> d <span class="dt">False</span>) <span class="co">-- the edge</span></span>
<span id="cb4-58"><a href="#cb4-58" aria-hidden="true" tabindex="-1"></a> , ( p0 <span class="op">+</span> rotPoint d (<span class="dt">V2</span> <span class="dv">0</span> (<span class="op">-</span><span class="dv">1</span>)) <span class="co">-- the new point</span></span>
<span id="cb4-59"><a href="#cb4-59" aria-hidden="true" tabindex="-1"></a> , d</span>
<span id="cb4-60"><a href="#cb4-60" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb4-61"><a href="#cb4-61" aria-hidden="true" tabindex="-1"></a> )</span></code></pre></div>
<p>We can wrap this all up in a solver to extract the <code>Map Point Placement</code> (using <code>assembleMap</code>) and the <code>Set Point</code> — the “actual” pixel map that represents all of the points themselves in 2d space.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>solve</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">IntMap</span> (<span class="dt">Set</span> (<span class="dt">V2</span> (<span class="dt">Finite</span> <span class="dv">10</span>)))</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> (<span class="dt">Map</span> <span class="dt">Point</span> <span class="dt">Placement</span>, <span class="dt">Set</span> <span class="dt">Point</span>)</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>solve ts <span class="ot">=</span> (shiftToZero mp, blitted)</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> info <span class="ot">=</span> toTiles <span class="op">&lt;$&gt;</span> ts</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a> edgeMap <span class="ot">=</span> IM.mapWithKey (\i (_, e) <span class="ot">-&gt;</span> (i,) <span class="op">&lt;$&gt;</span> e) info</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a> edges <span class="ot">=</span> <span class="fu">snd</span> <span class="op">.</span> <span class="fu">fst</span> <span class="op">&lt;$&gt;</span> info</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a> mp <span class="ot">=</span> assembleMap edges edgeMap</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a> blitted <span class="ot">=</span> <span class="fu">flip</span> M.foldMapWithKey mp <span class="op">$</span> \p (tileId, o) <span class="ot">-&gt;</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> core <span class="ot">=</span> <span class="fu">fst</span> <span class="op">.</span> <span class="fu">fst</span> <span class="op">$</span> info <span class="op">IM.!</span> tileId</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">in</span> S.map ((<span class="op">+</span> (p <span class="op">*</span> <span class="dv">8</span>)) <span class="op">.</span> shiftToZero <span class="op">.</span> orientPoint o) core</span></code></pre></div>
<p>We can use the <code>Map Point Placement</code> to get the answer to part 1: just look at the tile id’s at the corners of the map. Since we <code>shiftToZero</code>, we can just look up <code>mp M.! V2 0 0</code>, <code>mp M.! V2 0 12</code>, <code>mp M.! V2 12 0</code>, and <code>mp M.! V2 12 12</code>, and multiply them all together.</p>
<p>For part 2, after we assemble the actual <code>Point</code>, we can do a search for all dragons at all orientations.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- | given a pattern and a map of points, poke out all points matching that</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- pattern.</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>pokePattern</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Set</span> <span class="dt">Point</span> <span class="co">-- ^ pattern</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Set</span> <span class="dt">Point</span> <span class="co">-- ^ map</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Set</span> <span class="dt">Point</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>pokePattern pat ps0 <span class="ot">=</span> foldl&#39; go ps0 (<span class="fu">range</span> (<span class="dt">V2</span> <span class="dv">0</span> <span class="dv">0</span>, <span class="dt">V2</span> <span class="dv">96</span> <span class="dv">96</span>))</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> go ps d</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> pat&#39; <span class="ot">`S.isSubsetOf`</span> ps <span class="ot">=</span> ps <span class="dt">S</span><span class="op">.</span>\\ pat&#39;</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> ps</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> pat&#39; <span class="ot">=</span> S.mapMonotonic (<span class="op">+</span> d) pat</span></code></pre></div>
<p>And now we try <code>pokePattern</code> with the dragon at all orientations until we find one that gets any pokes:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ot">dragon ::</span> <span class="dt">Set</span> <span class="dt">Point</span> <span class="co">-- the dragon image</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="ot">allDragons ::</span> [<span class="dt">Set</span> <span class="dt">Point</span>] <span class="co">-- the dragon image at all orientations</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>allDragons <span class="ot">=</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> [ shiftToZero <span class="op">$</span> orientPoint o <span class="ot">`S.map`</span> dragon</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> o <span class="ot">&lt;-</span> allD8</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>dragonCount</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a><span class="ot"> ::</span> <span class="dt">Set</span> <span class="dt">Point</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Int</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>dragonCount fullMap <span class="ot">=</span> listToMaybe</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> [ res</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> drgn <span class="ot">&lt;-</span> allDragons</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> , <span class="kw">let</span> res <span class="ot">=</span> S.size <span class="op">$</span> pokePattern drgn fullMap</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> , res <span class="op">/=</span> S.size fullMap</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>And that concludes my solve of what was probably the most complex challenge of the month! Overall a lot of moving parts, but I was at least very happy to be able to use some knowledge of group theory (in particular, how the orientations of a square compose and interact) to break the puzzle down into pieces that were much easier to think about.</p>
<h3 id="day-20-benchmarks">Day 20 Benchmarks</h3>
<pre><code>&gt;&gt; Day 20a
benchmarking...
time 29.10 ms (28.84 ms .. 29.92 ms)
0.997 R² (0.990 R² .. 1.000 R²)
mean 29.03 ms (28.81 ms .. 29.63 ms)
std dev 762.3 μs (159.7 μs .. 1.370 ms)
* parsing and formatting times excluded
&gt;&gt; Day 20b
benchmarking...
time 73.35 ms (66.76 ms .. 90.08 ms)
0.931 R² (0.829 R² .. 1.000 R²)
mean 69.27 ms (66.81 ms .. 78.84 ms)
std dev 7.768 ms (154.8 μs .. 13.53 ms)
variance introduced by outliers: 35% (moderately inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-20</link>
<pubDate>Sun, 20 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 19</title>
<description><h2 id="day-19">Day 19</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day19.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/19">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day19.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day19.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day19.md">Standalone Reflection Page</a></em></p>
<p>I had originally solved this puzzle using recursive knot tying and a funky custom Monad — the writeup for that is <a href="https://github.com/mstksg/advent-of-code-2020/blob/5065aad720f6996386e9c94fbd7904a6fa9f2d9d/reflections-out/day19.md">available online here</a>. But after some thought and reflection, I saw that things might be a little cleaner as a hylomorphism from <em><a href="https://hackage.haskell.org/package/recursion-schemes">recursion-schemes</a></em>, so I did a rewrite based on it! It also ended up being about 25% faster to run, which was a nice bonus. Note that I do have a <a href="">blog post on hylomorphisms and recurion schemes</a> (https://blog.jle.im/entry/tries-with-recursion-schemes.html), if you’d like to investigate more about the topic :)</p>
<p>The central type (“base functor”) is <code>Rule</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Rule</span> a <span class="ot">=</span> <span class="dt">Simple</span> <span class="dt">Char</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Compound</span> [[a]]</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Show</span>, <span class="dt">Eq</span>, <span class="dt">Ord</span>, <span class="dt">Generic</span>, <span class="dt">Functor</span>)</span></code></pre></div>
<p>A <code>Rule a</code> is either a “base” <code>Char</code> match, or it is a list of options of sequences (a list of “or”’s of “and then”’s) of <code>a</code>. The choice of <code>a</code> gives us our interesting behavior.</p>
<p>For example, our initial ruleset from the input file is a list of <code>Rule Int</code>s: either they are a simple <code>Char</code>, or they contain a list of options of sequences of rule id’s (<code>Int</code>). We can load it all as an <code>IntMap (Rule Int)</code>, where each <code>Rule Int</code> is stored under its rule ID.</p>
<p>Just to help us get an intuition for this type, let’s look at what happens if we want to “expand” out a rule all the way to only leaves at the end of a bunch of nested choices and sequences. This isn’t required for the solve, but could be pretty fun.</p>
<p>For that, we can use the <code>Fix</code> data type:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">Fix</span> f <span class="ot">=</span> <span class="dt">Fix</span> (f (<span class="dt">Fix</span> f))</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">ExpandedRule</span> <span class="ot">=</span> <span class="dt">Fix</span> <span class="dt">Rule</span></span></code></pre></div>
<p>A <code>Fix Rule</code> is infinite nested <code>Rule</code>s: it’s essentially <code>Rule (Rule (Rule (Rule ...)))</code> forever, meaning underneath each <code>Compound</code> are new rules, and at the end of it all we only have <code>Leaf Char</code>s, and no more <code>Int</code>s. For example, we could represent rule 0 of</p>
<pre><code>0: 1 2 | 3
1: 3
2: 3 3
3: &quot;a&quot;</code></pre>
<p>as</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">Fix</span> <span class="op">$</span> <span class="dt">Compound</span> [</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> [<span class="dt">Fix</span> <span class="op">$</span> <span class="dt">Compoud</span> [[<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;a&#39;</span>)]], <span class="dt">Fix</span> <span class="op">$</span> <span class="dt">Compound</span> [[<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;a&#39;</span>), <span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;a&#39;</span>)]]]</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> , [<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;a&#39;</span>)]</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>But, given an <code>IntMap (Rule Int)</code> (the “unexpanded” raw rules as they are in the input file), how do we get our <code>Fix Rule</code>?</p>
<p>We can use the handy <code>ana</code> function, which, given an expansion function <code>a -&gt; Rule a</code>, returns a <code>a -&gt; Fix Rule</code>: It runs the <code>a -&gt; Rule a</code> expansion function on the “seed” <code>a</code>, and then runs it again on all the <code>a</code>s in the result, and again, and again, etc., until there are no more <code>a</code>s to expand.</p>
<p>Well, in our case, our “expansion” function is <code>Int -&gt; Rule Int</code>: “To expand an <code>Int</code>, look it up in the <code>IntMap Int (RuleInt)</code>”. And that gives us a function to fully expand any rule number:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">expandRule ::</span> <span class="dt">IntMap</span> (<span class="dt">Rule</span> <span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Fix</span> <span class="dt">Rule</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>expandRule rs <span class="ot">=</span> ana (rs <span class="op">IM.!</span>)</span></code></pre></div>
<p>Neat, huh? That will fully expand the rule at any index by repeatedly re-expanding it with <code>(rs IM.!)</code> until we are out of things to expand.</p>
<p>Another fun thing we can write that we could actually use for part 1 is to turn an <code>Fix Rule</code> into a list of all possible strings to match. We want to write a <code>Fix Rule -&gt; [String]</code> function by tearing down our recursive data type, and this could be nicely expressed with a catamorphism (<code>cata :: (Rule a -&gt; a) -&gt; Fix Rule -&gt; a</code>), where we specify how to tear down a “single layer” of our <code>Rule</code> type, and <code>cata</code> will generalize that to tear down the entire structure. I talk about this a bit <a href="https://blog.jle.im/entry/tries-with-recursion-schemes.html">in my recursion schemes blog post</a>, and the explanation I give is “The <code>a</code> values in the <code>Rule</code> become the very things we swore to create.” — in this case, the <code>[String]</code></p>
<p>So let’s write our <code>Rule [String] -&gt; [String]</code>:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ot">generateAlg ::</span> <span class="dt">Rule</span> [<span class="dt">String</span>] <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>generateAlg <span class="ot">=</span> \<span class="kw">case</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Simple</span> c <span class="ot">-&gt;</span> [[c]] <span class="co">-- the single single-char string is created</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">Compoud</span> xs <span class="ot">-&gt;</span> <span class="fu">concatMap</span> (<span class="fu">fmap</span> <span class="fu">concat</span> <span class="op">.</span> <span class="fu">sequence</span>) xs <span class="co">-- concat/sequence all options</span></span></code></pre></div>
<p>And now <code>cata generateAlg</code> will generate all possible matches from a ruleset</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>ghci<span class="op">&gt;</span> cata generateAlg</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> (<span class="dt">Fix</span> <span class="op">$</span> <span class="dt">Compound</span> [[<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;h&#39;</span>), <span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;e&#39;</span>)], [<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;h&#39;</span>)], [<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;q&#39;</span>)]])</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>[<span class="st">&quot;he&quot;</span>,<span class="st">&quot;h&quot;</span>,<span class="st">&quot;q&quot;</span>]</span></code></pre></div>
<p>Okay, that’s enough playing around for now…time to find our real solution :)</p>
<p>Note that we can “interpret” a rule to match it on a string by turning it into a <code>String -&gt; [String]</code>: it’ll take a string and return a list of the leftovers of every possible match. For example, running the rules <code>(he)|h|q</code> on <code>"hello"</code> <em>should</em> give us <code>["llo","ello"]</code>. Then we can just see if we have any matches that return empty leftovers.</p>
<p>For aid in thinking, let’s imagine turning a <code>Fix Rule</code> into a <code>String -&gt; [String]</code>. We can do that with the help of <code>cata :: (Rule a -&gt; a) -&gt; Fix Rule -&gt; a</code>. Because we want to write a <code>Fix Rule -&gt; (String -&gt; [String])</code>, our catamorphism function (“algebra”) is <code>Rule (String -&gt; [String]) -&gt; (String -&gt; [String])</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">matchAlg ::</span> <span class="dt">Rule</span> (<span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]) <span class="ot">-&gt;</span> <span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>matchAlg <span class="ot">=</span> \<span class="kw">case</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">Simple</span> c <span class="ot">-&gt;</span> \<span class="kw">case</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> [] <span class="ot">-&gt;</span> []</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> d<span class="op">:</span>ds <span class="ot">-&gt;</span> <span class="kw">if</span> c <span class="op">==</span> d <span class="kw">then</span> [ds] <span class="kw">else</span> []</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">Compound</span> xs <span class="ot">-&gt;</span> \str <span class="ot">-&gt;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">concatMap</span> (sequenceAll str) xs</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="co">-- run the String -&gt; [String]s on an input String one after the other</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a><span class="ot"> sequenceAll ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]] <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> sequenceAll s0 fs <span class="ot">=</span> <span class="fu">foldr</span> (<span class="op">&gt;=&gt;</span>) <span class="fu">pure</span> fs s0</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a><span class="ot">match ::</span> <span class="dt">Fix</span> <span class="dt">Rule</span> <span class="ot">-&gt;</span> <span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>match <span class="ot">=</span> cata matchAlg</span></code></pre></div>
<p>We want to fail on our input string (return no matches) if we see a <code>Simple c</code> with either an empty input string or one that doesn’t match the <code>c</code>. Then for the <code>Compound</code> case with our <code>xs :: [[String -&gt; [String]]]</code>, we take a choice (<code>concatMap</code>) of all of the possible full sequences of the inner <code>[String -&gt; [String]]</code> sequences.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>ghci<span class="op">&gt;</span> match (<span class="dt">Fix</span> <span class="op">$</span> <span class="dt">Compound</span> [[<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;h&#39;</span>), <span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;e&#39;</span>)], [<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;h&#39;</span>)], [<span class="dt">Fix</span> (<span class="dt">Leaf</span> <span class="ch">&#39;q&#39;</span>)]])</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;hello&quot;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>[<span class="st">&quot;llo&quot;</span>, <span class="st">&quot;ello&quot;</span>]</span></code></pre></div>
<p>Alright, so now how do we solve the final puzzle?</p>
<p>It looks like we need to “generate” a <code>Fix Rule</code>, and <em>immediately</em> tear it down into a <code>String -&gt; [String]</code> to use it to match a string. “Generate recursively and immediately tear down recursively”…that’s a hylomorphism!</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ot">hylo ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (f b <span class="ot">-&gt;</span> b) <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- which we use as...</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="ot">hylo ::</span> (<span class="dt">Rule</span> b <span class="ot">-&gt;</span> b) <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> <span class="dt">Rule</span> a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- which we use as...</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a><span class="ot">hylo ::</span> (<span class="dt">Rule</span> (<span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]) <span class="ot">-&gt;</span> (<span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]))</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> (<span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Rule</span> <span class="dt">Int</span>)</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> <span class="ot">-&gt;</span> (<span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>])</span></code></pre></div>
<p>If we give <code>hylo</code> a way to “break down nested <code>Rule</code>s” and a way to “build up nested <code>Rule</code>s”, then it can actually iteratively expand up <code>Rule</code>s while immediately tearing them down. The nice thing about this is that it’s very lazy: it’ll only <em>call</em> the generator function if you ever <em>need</em> the thing during your teardown function. Since our teardown function (the <code>String -&gt; [String]</code>) will terminate whenever we encounter an empty string or no matches, <code>hylo</code> will only run the build-up function until the point that we hit one of those conditions. You can also think of it as running it on a <code>Rule Int</code> where each <code>Int</code> is dynamically looked up as you need it from the rules map.</p>
<p>The neat thing about this is that we don’t ever need <code>Fix</code> at all: it’s all built up and torn down “in-place”, and we never built up any intermediate value. That’s why I mentioned that the <code>Fix</code> part earlier was more of a side-tangent! But it definitely helps us understand the big picture, I feel.</p>
<p>Our final code (the whole of it, minus the parser) ends up being:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Rule</span> a <span class="ot">=</span> <span class="dt">Simple</span> <span class="dt">Char</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> <span class="dt">Compound</span> [[a]]</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">deriving</span> (<span class="dt">Show</span>, <span class="dt">Eq</span>, <span class="dt">Ord</span>, <span class="dt">Generic</span>, <span class="dt">Functor</span>)</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="ot">matchAlg ::</span> <span class="dt">Rule</span> (<span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]) <span class="ot">-&gt;</span> <span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>matchAlg <span class="ot">=</span> \<span class="kw">case</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">Simple</span> c <span class="ot">-&gt;</span> \<span class="kw">case</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> [] <span class="ot">-&gt;</span> []</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> d<span class="op">:</span>ds <span class="ot">-&gt;</span> <span class="kw">if</span> c <span class="op">==</span> d <span class="kw">then</span> [ds] <span class="kw">else</span> []</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">Compound</span> xs <span class="ot">-&gt;</span> \str <span class="ot">-&gt;</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> <span class="fu">concatMap</span> (sequenceAll str) xs</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a> sequenceAll s0 fs <span class="ot">=</span> <span class="fu">foldr</span> (<span class="op">&gt;=&gt;</span>) <span class="fu">pure</span> fs s0</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a><span class="ot">matcher ::</span> <span class="dt">IntMap</span> (<span class="dt">Rule</span> <span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">String</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>matcher rules <span class="ot">=</span> hylo matchAlg (rules <span class="op">IM.!</span>) <span class="dv">0</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a><span class="ot">solver ::</span> <span class="dt">IntMap</span> (<span class="dt">Rule</span> <span class="dt">Int</span>) <span class="ot">-&gt;</span> [<span class="dt">String</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a>solver rules <span class="ot">=</span> <span class="fu">length</span> <span class="op">.</span> <span class="fu">filter</span> (<span class="fu">any</span> <span class="fu">null</span> <span class="op">.</span> matcher rules)</span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a><span class="ot">part1 ::</span> <span class="dt">IntMap</span> <span class="dt">Rule</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a>part1 <span class="ot">=</span> solver</span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a><span class="ot">part2 ::</span> <span class="dt">IntMap</span> <span class="dt">Rule</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true" tabindex="-1"></a>part2 rs <span class="ot">=</span> solver (extraRules <span class="op">&lt;&gt;</span> rs)</span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true" tabindex="-1"></a><span class="ot">extraRules ::</span> <span class="dt">IntMap</span> (<span class="dt">Rule</span> <span class="dt">Int</span>)</span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true" tabindex="-1"></a>extraRules <span class="ot">=</span> IM.fromList [</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true" tabindex="-1"></a> (<span class="dv">8</span> , <span class="dt">Compound</span> [[<span class="dv">42</span>],[<span class="dv">42</span>,<span class="dv">8</span>]])</span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true" tabindex="-1"></a> , (<span class="dv">11</span>, <span class="dt">Compound</span> [[<span class="dv">42</span>,<span class="dv">31</span>],[<span class="dv">42</span>,<span class="dv">11</span>,<span class="dv">31</span>]])</span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>As a nice little bonus, we can also use <code>generateAlg</code> with a hylomorphism to also turn an <code>IntMap (Rule Int)</code> into a list of all possible strings, which works for part 1 but would return an infinite list for part 2.</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">generateAll ::</span> <span class="dt">IntMap</span> (<span class="dt">Rule</span> <span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> [<span class="dt">String</span>]</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>generateAll rules <span class="ot">=</span> hylo generateAlg (rules <span class="op">IM.!</span>) <span class="dv">0</span></span></code></pre></div>
<h3 id="day-19-benchmarks">Day 19 Benchmarks</h3>
<pre><code>&gt;&gt; Day 19a
benchmarking...
time 4.273 ms (4.202 ms .. 4.507 ms)
0.990 R² (0.965 R² .. 1.000 R²)
mean 4.244 ms (4.200 ms .. 4.390 ms)
std dev 220.8 μs (54.67 μs .. 480.5 μs)
variance introduced by outliers: 30% (moderately inflated)
* parsing and formatting times excluded
&gt;&gt; Day 19b
benchmarking...
time 27.13 ms (26.34 ms .. 28.22 ms)
0.994 R² (0.987 R² .. 1.000 R²)
mean 26.20 ms (25.94 ms .. 26.80 ms)
std dev 908.6 μs (525.5 μs .. 1.450 ms)
variance introduced by outliers: 10% (moderately inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-19</link>
<pubDate>Sat, 19 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 18</title>
<description><h2 id="day-18">Day 18</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day18.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/18">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day18.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day18.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day18.md">Standalone Reflection Page</a></em></p>
<p>Let’s parse with parser combinators!</p>
<p>The main way I have learned how to deal with these binary-operation parsers is to separate out the stages into a “bottom” level containing only the leaves (here, the int literals) and parentheses, and then build up layers of precedence one-by-one from highest to lowest. For the first part we only have two layers, then, since we only have one level of precedence.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ot">{-# LANGUAGE OverloadedStrings #-}</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Text.Megaparsec</span> <span class="kw">as</span> <span class="dt">P</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Text.Megaparsec.Char</span> <span class="kw">as</span> <span class="dt">P</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Text.Megaparsec.Char.Lexer</span> <span class="kw">as</span> <span class="dt">PP</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Parser</span> <span class="ot">=</span> <span class="dt">P.Parsec</span> <span class="dt">Void</span> <span class="dt">String</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ot">parseBottom1 ::</span> <span class="dt">Parser</span> <span class="dt">Int</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>parseBottom1 <span class="ot">=</span> P.choice</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> [ PP.decimal</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> , P.between <span class="st">&quot;(&quot;</span> <span class="st">&quot;)&quot;</span> parseTop1 <span class="co">-- use -XOverloadedStrings to get parsers that match strings</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="ot">parseTop1 ::</span> <span class="dt">Parser</span> <span class="dt">Int</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>parseTop1 <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> leftOfOp <span class="ot">&lt;-</span> parseBottom1 <span class="co">-- parse the left hand side of a possible binary operator</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a> doNext acc</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a> doNext acc <span class="ot">=</span> P.choice <span class="co">-- once we parse a left hand side, pick from:</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a> [ <span class="kw">do</span> <span class="st">&quot; * &quot;</span> <span class="co">-- either it&#39;s a *</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a> rightOfOp <span class="ot">&lt;-</span> parseBottom1 <span class="co">-- ... so we parse the right hand side and multiply</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a> doNext (acc <span class="op">*</span> rightOfOp)</span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a> , <span class="kw">do</span> <span class="st">&quot; + &quot;</span> <span class="co">-- or it&#39;s a +</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a> rightOfOp <span class="ot">&lt;-</span> parseBottom1 <span class="co">-- ... so we parse the right hand side and add</span></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a> doNext (acc <span class="op">+</span> rightOfOp)</span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a> , <span class="fu">pure</span> acc <span class="co">-- otherwise that was it, no operator</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>Remember that <code>leftOfOp</code> could either come from a leaf literal number or from a parenthesized equation. In the end, we get an <code>Int</code>, representing whatever number was on the left hand side of our operator. Then we move into <code>doNext</code>, which continually accumulates new operations after that first <code>leftOfOp</code> parse.</p>
<p>If we see a <code>*</code>, we parse the right hand side, fold that into our accumulator and repeat until we hit a dead end and yield our accumulated value; same for <code>+</code>.</p>
<p>So there’s this sort of “cycle” that <code>parseTop</code> defers to <code>parseBottom</code> for its underlying things “in between” the operators, but <code>parseBottom</code> loops back up to <code>parseTop</code> to handle what is in the parentheses.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ot">part1 ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Int</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>part1 <span class="ot">=</span> P.parseMaybe <span class="op">$</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">sum</span> <span class="op">&lt;$&gt;</span> P.many parseTop1</span></code></pre></div>
<p>The twist for part 2 is that now we have to have another layer of precedence, so we split things out:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ot">parseBottom2 ::</span> <span class="dt">Parser</span> <span class="dt">Int</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>parseBottom2 <span class="ot">=</span> P.choice</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> [ PP.decimal</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> , P.between <span class="st">&quot;(&quot;</span> <span class="st">&quot;)&quot;</span> parseTop2</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="ot">parseMiddle2 ::</span> <span class="dt">Parser</span> <span class="dt">Int</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>parseMiddle2 <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> leftOfOp <span class="ot">&lt;-</span> parseBottom2</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a> doNext leftOfOp</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a> doNext acc <span class="ot">=</span> P.choice</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a> [ <span class="kw">do</span> <span class="st">&quot; + &quot;</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a> rightOfOp <span class="ot">&lt;-</span> parseBottom2</span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a> doNext (acc <span class="op">+</span> rightOfOp)</span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> , <span class="fu">pure</span> acc</span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> ]</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="ot">parseTop2 ::</span> <span class="dt">Parser</span> <span class="dt">Int</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>parseTop2 <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> leftOfOp <span class="ot">&lt;-</span> parseMiddle2</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a> doNext leftOfOp</span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> <span class="kw">where</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a> doNext acc <span class="ot">=</span> P.choice</span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a> [ <span class="kw">do</span> <span class="st">&quot; * &quot;</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> rightOfOp <span class="ot">&lt;-</span> parseMiddle2</span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a> doNext (acc <span class="op">*</span> rightOfOp)</span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a> , <span class="fu">pure</span> acc</span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>So the parser dependency again is kind of interesting: <code>parseTop2</code> is built up of chained <code>parseMiddle2</code>s, which is built up of chained <code>parseBottom2</code>, which could loop back up with <code>parseTop2</code> if detect parentheses.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">part2 ::</span> <span class="dt">String</span> <span class="ot">-&gt;</span> <span class="dt">Maybe</span> <span class="dt">Int</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>part2 <span class="ot">=</span> P.parseMaybe <span class="op">$</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">sum</span> <span class="op">&lt;$&gt;</span> (parseTop2 <span class="ot">`P.sepBy`</span> P.newline)</span></code></pre></div>
<p>Note that this chaining and looping behavior can be abstracted out — that’s essentially what I wrote in my <a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day18.hs">cleaned up solution</a>. But also the <em><a href="https://hackage.haskell.org/package/parser-combinators-1.2.1/docs/Control-Monad-Combinators-Expr.html">Control.Monad.Combinators.Expr</a></em> module also abstracts over this pattern, letting you specify the “layers” you want, and it’ll generate the right parser for you with the correct weaving of dependencies like I described here. But still, I think it’s fun to see how these things end up looking like under the hood :)</p>
<h3 id="day-18-benchmarks">Day 18 Benchmarks</h3>
<pre><code>&gt;&gt; Day 18a
benchmarking...
time 2.824 ms (2.691 ms .. 3.014 ms)
0.975 R² (0.952 R² .. 0.998 R²)
mean 2.748 ms (2.703 ms .. 2.844 ms)
std dev 208.7 μs (100.8 μs .. 383.4 μs)
variance introduced by outliers: 53% (severely inflated)
* parsing and formatting times excluded
&gt;&gt; Day 18b
benchmarking...
time 2.270 ms (2.143 ms .. 2.447 ms)
0.974 R² (0.958 R² .. 0.996 R²)
mean 2.231 ms (2.180 ms .. 2.378 ms)
std dev 236.7 μs (129.2 μs .. 403.0 μs)
variance introduced by outliers: 70% (severely inflated)
* parsing and formatting times excluded</code></pre>
</description>
<link>https://github.com/mstksg/advent-of-code-2020/blob/master/reflections.md#day-18</link>
<pubDate>Fri, 18 Dec 2020 01:00:00 EST</pubDate>
</item>
<item>
<title>Day 17</title>
<description><h2 id="day-17">Day 17</h2>
<!--
This section is generated and compiled by the build script at ./Build.hs from
the file `./reflections/day17.md`. If you want to edit this, edit
that file instead!
-->
<p><em><a href="https://adventofcode.com/2020/day/17">Prompt</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/src/AOC/Challenge/Day17.hs">Code</a></em> / <em><a href="https://mstksg.github.io/advent-of-code-2020/src/AOC.Challenge.Day17.html">Rendered</a></em> / <em><a href="https://github.com/mstksg/advent-of-code-2020/blob/master/reflections-out/day17.md">Standalone Reflection Page</a></em></p>
<p>Neat, Game of Life! :D Actually, the 3D/4D twist does make a big impact for the best method we’d pick: we run into the <a href="https://en.wikipedia.org/wiki/Curse_of_dimensionality">curse of dimensionality</a>. It means that when we get to 3D and 4D, our world will become vanishingly sparse. In my own input, only about 4% of the 3D space ended up being active, and 2% of my 4D space ends up being active. This means that holding a dense vector of all possible active points (which will be <code>(6+8+6)^n</code>) is up to 98% wasteful. And because of the way this process works, we have to completely copy our entire space at every iteration.</p>
<p>In these times, I’m happy that Haskell has a nice immutable sparse data structure like <code>Set</code>. Sparse being beneficial in that we can easily look up and process only the 2% of active squares, and immutable being beneficial in that each step already requires a full copy in any case, so immutability doesn’t give us any drawback.</p>
<p>First a function to get all neighbors of a point, using the <code>V3</code> type from the <em><a href="https://hackage.haskell.org/package/linear">linear</a></em> library, which I’ve used many times already for its convenient <code>Num</code> and <code>Applicative</code> instances:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Set</span> (<span class="dt">Set</span>)</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Set</span> <span class="kw">as</span> <span class="dt">S</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- from linear</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">V3</span> a <span class="ot">=</span> <span class="dt">V3</span> a a a</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- its Applicative instance</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="fu">pure</span> x <span class="ot">=</span> <span class="dt">V3</span> x x x</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="ot">neighbsSet ::</span> <span class="dt">V3</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">Set</span> (<span class="dt">V3</span> <span class="dt">Int</span>)</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>neighbsSet p <span class="ot">=</span> S.fromList</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> [ p <span class="op">+</span> d</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> d <span class="ot">&lt;-</span> <span class="fu">sequence</span> (<span class="fu">pure</span> [<span class="op">-</span><span class="dv">1</span>,<span class="dv">0</span>,<span class="dv">1</span>])</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> , d <span class="op">/=</span> <span class="fu">pure</span> <span class="dv">0</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a> ]</span></code></pre></div>
<p>Just as a reminder, <code>pure [0,1]</code> for <code>V3 Int</code> gives us <code>V3 [0,1] [0,1] [0,1]</code>, and if we <code>sequence</code> that we get a cartesian N-product of all combinations <code>[V3 0 0, V3 0 0 1, V3 0 1 0, V3 0 1 1, V3 1 0 0, .. etc.]</code>. We add each of those to <code>p</code>, except for the one that is <code>V3 0 0 0</code>.</p>
<p>Now we can write our stepper, which takes a <code>Set (V3 Int)</code> and returns the next <code>Set (V3 Int)</code> after applying the rules. We can do that first by making a <code>Map (V3 Int) Int</code>, where <code>Int</code> is the number of neighbors at a given point. This can be done by “exploding” every <code>V3 Int</code> in our set to a <code>Map (V3 Int) Int</code>, a map of all its neighbors keyed to values 1, and then using <code>M.unionsWith (+)</code> to union together all of those exploded neighbors, adding any overlapping keys.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="dt">Data.Map</span> (<span class="dt">Map</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Map</span> <span class="kw">as</span> <span class="dt">M</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="ot">neighborMap ::</span> <span class="dt">Set</span> (<span class="dt">V3</span> <span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">Map</span> (<span class="dt">V3</span> <span class="dt">Int</span>) <span class="dt">Int</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>neighborMap ps <span class="ot">=</span> M.unionsWith (<span class="op">+</span>)</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> [ M.fromSet (<span class="fu">const</span> <span class="dv">1</span>) (neighbsSet p)</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> <span class="op">|</span> p <span class="ot">&lt;-</span> S.toList ps</span>