├── .gitignore
├── COPYING
├── LICENSE-MIT
├── README.md
├── UNLICENSE
├── aoc01
├── Cargo.lock
├── Cargo.toml
├── input
│ └── input.txt
└── src
│ └── main.rs
├── aoc02
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc03
├── Cargo.lock
├── Cargo.toml
├── input
│ └── input.txt
└── src
│ └── main.rs
├── aoc04
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc05
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc06
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc07
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc08
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc09
├── Cargo.lock
├── Cargo.toml
├── input
│ └── input.txt
└── src
│ └── main.rs
├── aoc10
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc11
├── Cargo.lock
├── Cargo.toml
└── src
│ └── main.rs
├── aoc12
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc13
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ ├── test.txt
│ └── test2.txt
└── src
│ └── main.rs
├── aoc14
├── Cargo.lock
├── Cargo.toml
└── src
│ └── main.rs
├── aoc15
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ ├── test-movement.txt
│ ├── test1.txt
│ ├── test2.txt
│ ├── test3.txt
│ ├── test4.txt
│ ├── test5.txt
│ └── test6.txt
└── src
│ └── main.rs
├── aoc16
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input-samples.txt
│ ├── input-test.txt
│ ├── input.txt
│ └── test-samples.txt
└── src
│ └── main.rs
├── aoc17
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ ├── input2.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc18
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc19
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test.txt
└── src
│ └── main.rs
├── aoc20
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ ├── me1.txt
│ ├── me2.txt
│ ├── test1.txt
│ ├── test2.txt
│ ├── test3.txt
│ ├── test4.txt
│ └── test5.txt
└── src
│ └── main.rs
├── aoc21
├── Cargo.lock
├── Cargo.toml
├── input
│ └── input.txt
└── src
│ └── main.rs
├── aoc22
├── Cargo.lock
├── Cargo.toml
├── input
│ └── input.txt
└── src
│ └── main.rs
├── aoc23
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ ├── test1.txt
│ └── test2.txt
└── src
│ └── main.rs
├── aoc24
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ └── test1.txt
└── src
│ └── main.rs
├── aoc25
├── Cargo.lock
├── Cargo.toml
├── input
│ ├── input.txt
│ ├── test1.txt
│ ├── test2.txt
│ ├── test3.txt
│ └── test4.txt
└── src
│ └── main.rs
└── setup-day
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | tags
3 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | This project is dual-licensed under the Unlicense and MIT licenses.
2 |
3 | You may use this code under the terms of either license.
4 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Andrew Gallant
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | BurntSushi's 2018 Advent of Code solutions
2 | ==========================================
3 |
4 | I chose to write this year's solutions in Rust. I don't have any particularly
5 | ambitious goals, but I am trying to write the solutions using idiomatic code.
6 | In particular, it should not be possible for any input to cause one of the
7 | solutions to panic.
8 |
9 | I have not spent any time benchmarking the code.
10 |
11 | To run a solution, `cd` into its directory and invoke the program with Cargo:
12 |
13 | ```
14 | $ cd aoc01
15 | $ cargo run --release < input/input.txt
16 | ```
17 |
18 | If you have questions about the code, please open an issue and ask away!
19 | Beginner questions are very much welcome.
20 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/aoc01/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc01-1"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc01/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc01-1"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc01/input/input.txt:
--------------------------------------------------------------------------------
1 | +9
2 | +15
3 | -14
4 | +10
5 | +13
6 | +15
7 | +10
8 | -16
9 | -5
10 | +2
11 | +8
12 | +7
13 | -4
14 | +11
15 | +7
16 | +10
17 | -9
18 | +15
19 | -10
20 | +12
21 | -5
22 | +6
23 | +7
24 | -12
25 | -12
26 | -19
27 | -6
28 | +4
29 | +11
30 | -3
31 | +8
32 | +10
33 | +15
34 | +7
35 | +12
36 | -14
37 | +7
38 | +12
39 | +4
40 | -6
41 | -6
42 | +5
43 | +15
44 | +13
45 | +15
46 | +15
47 | -11
48 | -12
49 | +18
50 | -12
51 | -17
52 | +14
53 | +1
54 | +6
55 | +5
56 | -10
57 | -11
58 | -3
59 | +10
60 | +18
61 | -6
62 | -6
63 | +14
64 | -7
65 | +13
66 | +20
67 | +2
68 | -1
69 | +17
70 | +6
71 | -3
72 | -11
73 | +12
74 | +15
75 | -18
76 | -13
77 | -15
78 | +13
79 | +19
80 | +7
81 | -15
82 | -16
83 | +17
84 | +16
85 | +14
86 | -4
87 | +16
88 | -2
89 | +9
90 | +17
91 | -9
92 | -2
93 | -5
94 | +12
95 | -3
96 | +4
97 | +10
98 | -6
99 | -16
100 | -5
101 | -18
102 | +6
103 | -12
104 | +14
105 | +18
106 | -16
107 | -18
108 | +19
109 | -20
110 | +13
111 | +13
112 | -20
113 | +18
114 | -20
115 | +4
116 | +10
117 | +16
118 | -12
119 | +19
120 | +15
121 | +6
122 | +4
123 | +9
124 | -18
125 | +15
126 | -5
127 | +12
128 | -19
129 | +9
130 | -16
131 | +17
132 | -6
133 | +18
134 | +15
135 | +17
136 | -12
137 | +6
138 | -3
139 | +11
140 | -7
141 | -2
142 | +8
143 | -17
144 | -12
145 | -3
146 | +5
147 | -8
148 | -3
149 | -1
150 | -3
151 | -8
152 | -10
153 | +19
154 | +13
155 | -16
156 | +13
157 | +12
158 | -7
159 | +3
160 | +2
161 | +6
162 | +3
163 | +1
164 | +9
165 | -14
166 | -4
167 | +17
168 | +8
169 | -14
170 | -13
171 | +17
172 | -19
173 | +20
174 | +16
175 | -9
176 | +19
177 | +7
178 | +14
179 | +9
180 | -4
181 | +2
182 | -16
183 | +11
184 | +6
185 | +10
186 | -19
187 | +15
188 | +11
189 | +5
190 | +2
191 | -17
192 | +7
193 | +16
194 | +16
195 | -14
196 | +7
197 | -3
198 | -9
199 | -2
200 | -5
201 | -12
202 | -6
203 | +10
204 | +6
205 | +15
206 | -4
207 | -18
208 | +4
209 | +6
210 | -8
211 | +10
212 | -18
213 | -16
214 | -7
215 | -5
216 | -8
217 | +16
218 | +1
219 | +4
220 | +13
221 | -5
222 | +6
223 | +12
224 | -21
225 | -23
226 | +3
227 | -15
228 | +10
229 | -3
230 | +17
231 | -8
232 | -2
233 | -11
234 | +16
235 | -17
236 | -5
237 | +1
238 | +22
239 | -16
240 | -8
241 | +3
242 | -8
243 | +15
244 | +2
245 | +19
246 | -14
247 | -18
248 | -2
249 | +12
250 | +23
251 | +8
252 | +32
253 | +7
254 | +6
255 | -3
256 | +13
257 | -14
258 | +12
259 | -18
260 | +13
261 | +3
262 | -2
263 | +15
264 | -10
265 | -8
266 | +13
267 | +20
268 | -17
269 | -1
270 | +9
271 | +17
272 | -4
273 | -2
274 | +7
275 | -3
276 | -19
277 | -6
278 | +8
279 | -5
280 | +6
281 | -17
282 | +21
283 | +2
284 | +16
285 | -26
286 | +9
287 | +19
288 | -3
289 | +9
290 | +19
291 | +1
292 | -6
293 | -7
294 | +8
295 | -3
296 | -2
297 | +11
298 | +7
299 | +20
300 | -4
301 | -4
302 | +9
303 | +9
304 | -5
305 | -6
306 | -6
307 | +15
308 | -4
309 | -16
310 | -3
311 | -6
312 | -2
313 | -14
314 | +13
315 | -21
316 | -25
317 | -33
318 | +14
319 | +20
320 | +25
321 | -3
322 | +30
323 | +32
324 | +2
325 | +5
326 | -6
327 | +5
328 | +6
329 | +7
330 | -14
331 | +16
332 | +8
333 | +4
334 | +9
335 | +21
336 | -16
337 | +17
338 | -8
339 | +16
340 | +19
341 | -1
342 | -1
343 | +14
344 | +6
345 | -14
346 | +9
347 | +23
348 | +15
349 | -4
350 | -20
351 | +7
352 | +21
353 | +17
354 | +11
355 | -16
356 | +23
357 | +17
358 | -22
359 | +27
360 | -9
361 | -29
362 | +7
363 | -22
364 | -27
365 | -12
366 | +1
367 | +17
368 | -22
369 | -1
370 | -1
371 | -18
372 | -4
373 | +15
374 | -17
375 | -15
376 | -7
377 | +14
378 | +19
379 | -24
380 | -3
381 | -3
382 | -5
383 | -9
384 | +27
385 | -1
386 | -19
387 | +16
388 | -11
389 | +1
390 | +6
391 | -16
392 | +19
393 | +16
394 | -10
395 | -36
396 | +13
397 | -23
398 | -3
399 | -10
400 | +6
401 | -9
402 | -4
403 | +3
404 | +3
405 | +2
406 | -10
407 | -4
408 | -21
409 | +9
410 | +6
411 | +11
412 | +6
413 | +13
414 | -2
415 | -50
416 | -66
417 | +10
418 | -52
419 | -7
420 | -10
421 | +25
422 | -41
423 | -12
424 | -46
425 | -43
426 | -16
427 | -3
428 | +7
429 | +3
430 | +29
431 | -27
432 | -23
433 | -56
434 | -111
435 | +14
436 | -511
437 | -59243
438 | +6
439 | -3
440 | -11
441 | -6
442 | -15
443 | -10
444 | -13
445 | +8
446 | +17
447 | -4
448 | +19
449 | -8
450 | +4
451 | +17
452 | +8
453 | -5
454 | +4
455 | +14
456 | +4
457 | -6
458 | +15
459 | +4
460 | +21
461 | -17
462 | +12
463 | -31
464 | -70
465 | -10
466 | +6
467 | -11
468 | +13
469 | +11
470 | -5
471 | +2
472 | -15
473 | -19
474 | -20
475 | +14
476 | -11
477 | +5
478 | -12
479 | -3
480 | -17
481 | +4
482 | -8
483 | +5
484 | -10
485 | -14
486 | -13
487 | -2
488 | -9
489 | +10
490 | -11
491 | -21
492 | +12
493 | +1
494 | +11
495 | -21
496 | -15
497 | +4
498 | -6
499 | -14
500 | -13
501 | -5
502 | -7
503 | +6
504 | +10
505 | +14
506 | +6
507 | +14
508 | +2
509 | +8
510 | +18
511 | -15
512 | +18
513 | -13
514 | -19
515 | -20
516 | -19
517 | -16
518 | -18
519 | -7
520 | -1
521 | -12
522 | +14
523 | +15
524 | +14
525 | -4
526 | -16
527 | +19
528 | -9
529 | +16
530 | -12
531 | +3
532 | +6
533 | -15
534 | -10
535 | -10
536 | -15
537 | +11
538 | +20
539 | -23
540 | -19
541 | +1
542 | -12
543 | +3
544 | +17
545 | +4
546 | +3
547 | +17
548 | -9
549 | +1
550 | -2
551 | -19
552 | -11
553 | -5
554 | -14
555 | -1
556 | +11
557 | +3
558 | -6
559 | -1
560 | +14
561 | -11
562 | -5
563 | +18
564 | +9
565 | +2
566 | -9
567 | -16
568 | -12
569 | -13
570 | -1
571 | -7
572 | +12
573 | +4
574 | +11
575 | -22
576 | +14
577 | +23
578 | +6
579 | +11
580 | -9
581 | -9
582 | -4
583 | +17
584 | -8
585 | -10
586 | -16
587 | -10
588 | -4
589 | +7
590 | -9
591 | -14
592 | -14
593 | +12
594 | +10
595 | +13
596 | -16
597 | -13
598 | -18
599 | +19
600 | +10
601 | +10
602 | +20
603 | -5
604 | +8
605 | -27
606 | +16
607 | +7
608 | -5
609 | -8
610 | -20
611 | +8
612 | -19
613 | -7
614 | +16
615 | -22
616 | +9
617 | -19
618 | -19
619 | +15
620 | +16
621 | +18
622 | +7
623 | +10
624 | -16
625 | -6
626 | -18
627 | -2
628 | -7
629 | +8
630 | -7
631 | -20
632 | -2
633 | -2
634 | -19
635 | +18
636 | -4
637 | -18
638 | +8
639 | -19
640 | -16
641 | +3
642 | +17
643 | -12
644 | -10
645 | +3
646 | -17
647 | -15
648 | +19
649 | +4
650 | +3
651 | +16
652 | -1
653 | +13
654 | +6
655 | -15
656 | +14
657 | +19
658 | -3
659 | +16
660 | +1
661 | +5
662 | +3
663 | +1
664 | +8
665 | +12
666 | +7
667 | -18
668 | -16
669 | +23
670 | +6
671 | +3
672 | +6
673 | +13
674 | -15
675 | -22
676 | -13
677 | -16
678 | +19
679 | -9
680 | -7
681 | -13
682 | +2
683 | +25
684 | -19
685 | -16
686 | -4
687 | -27
688 | +19
689 | -21
690 | -7
691 | -23
692 | -19
693 | +17
694 | -8
695 | -3
696 | -10
697 | +1
698 | +16
699 | -6
700 | +15
701 | -8
702 | +17
703 | -16
704 | -14
705 | +11
706 | -10
707 | -10
708 | -16
709 | -6
710 | +2
711 | +1
712 | -17
713 | -19
714 | +17
715 | +12
716 | -11
717 | +7
718 | -13
719 | +15
720 | +15
721 | +10
722 | -9
723 | +3
724 | -1
725 | -1
726 | -20
727 | -1
728 | -9
729 | -13
730 | -13
731 | -10
732 | -15
733 | +17
734 | +4
735 | -9
736 | +16
737 | -4
738 | +17
739 | -14
740 | -11
741 | +17
742 | +20
743 | -11
744 | -2
745 | +19
746 | -20
747 | -21
748 | -4
749 | -8
750 | -7
751 | -6
752 | -16
753 | +15
754 | -12
755 | -13
756 | -4
757 | +16
758 | +4
759 | -14
760 | +9
761 | -18
762 | -12
763 | -19
764 | -12
765 | -19
766 | +9
767 | -15
768 | +8
769 | -12
770 | +8
771 | +8
772 | +16
773 | +6
774 | +10
775 | -5
776 | +2
777 | -17
778 | -13
779 | +24
780 | +26
781 | -11
782 | -17
783 | +37
784 | -3
785 | +14
786 | -5
787 | +4
788 | +16
789 | +3
790 | +12
791 | +4
792 | -25
793 | -18
794 | +7
795 | +16
796 | -11
797 | -22
798 | +11
799 | -19
800 | -11
801 | +23
802 | -21
803 | -14
804 | +26
805 | +17
806 | +19
807 | -29
808 | +33
809 | +45
810 | +14
811 | +9
812 | -22
813 | +10
814 | +7
815 | +13
816 | +9
817 | +12
818 | +9
819 | -20
820 | +3
821 | -9
822 | -19
823 | -18
824 | -23
825 | -7
826 | +34
827 | +11
828 | +46
829 | -16
830 | +21
831 | -8
832 | +18
833 | +10
834 | +7
835 | -15
836 | -7
837 | +1
838 | -16
839 | +28
840 | +16
841 | +2
842 | -8
843 | -3
844 | -3
845 | +39
846 | +15
847 | -10
848 | -3
849 | +6
850 | +17
851 | -27
852 | -19
853 | -10
854 | -43
855 | -42
856 | +133
857 | +19
858 | +19
859 | +27
860 | +59
861 | -14
862 | -18
863 | +5
864 | -10
865 | -7
866 | -21
867 | +108
868 | -41
869 | +53
870 | -309
871 | -42
872 | +123
873 | -1005
874 | -59069
875 | -4
876 | +3
877 | -7
878 | +5
879 | -14
880 | +7
881 | -4
882 | -2
883 | -5
884 | +9
885 | -13
886 | -7
887 | +1
888 | -13
889 | +3
890 | -21
891 | +9
892 | -12
893 | +18
894 | +10
895 | +8
896 | -19
897 | +13
898 | +12
899 | -19
900 | -1
901 | +16
902 | -11
903 | -7
904 | -5
905 | +11
906 | -15
907 | -4
908 | -3
909 | +6
910 | -18
911 | +5
912 | -1
913 | -10
914 | -16
915 | +13
916 | +12
917 | -18
918 | -16
919 | -4
920 | -13
921 | -9
922 | +7
923 | -1
924 | -18
925 | -18
926 | +6
927 | -11
928 | -16
929 | -17
930 | -7
931 | -3
932 | -3
933 | +15
934 | -11
935 | +6
936 | -14
937 | -12
938 | +13
939 | -9
940 | +7
941 | +13
942 | +6
943 | +18
944 | -10
945 | +19
946 | -12
947 | -3
948 | +5
949 | +6
950 | +2
951 | +9
952 | +1
953 | +1
954 | +12
955 | +6
956 | -3
957 | +6
958 | +7
959 | -17
960 | +6
961 | +15
962 | +17
963 | +3
964 | -16
965 | -16
966 | +19
967 | -10
968 | -7
969 | -18
970 | +6
971 | -17
972 | +10
973 | -17
974 | -1
975 | +5
976 | -15
977 | +2
978 | -6
979 | -19
980 | -8
981 | +15
982 | -4
983 | +7
984 | -13
985 | -18
986 | -7
987 | +8
988 | +8
989 | +3
990 | +24
991 | -16
992 | -15
993 | +6
994 | -20
995 | -8
996 | -18
997 | +3
998 | -7
999 | +14
1000 | +5
1001 | +18
1002 | -11
1003 | -6
1004 | -9
1005 | -14
1006 | -9
1007 | +15
1008 | +18
1009 | +6
1010 | +8
1011 | +10
1012 | -2
1013 | -18
1014 | +9
1015 | +13
1016 | -16
1017 | +7
1018 | -9
1019 | +4
1020 | -18
1021 | -7
1022 | +8
1023 | -20
1024 | -17
1025 | -16
1026 | -3
1027 | -15
1028 | +9
1029 | +16
1030 | +14
1031 | +8
1032 | +17
1033 | +120917
1034 |
--------------------------------------------------------------------------------
/aoc01/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashSet;
2 | use std::io::{self, Read, Write};
3 |
4 | type Result = ::std::result::Result>;
5 |
6 | fn main() -> Result<()> {
7 | let mut input = String::new();
8 | io::stdin().read_to_string(&mut input)?;
9 |
10 | part1(&input)?;
11 | part2(&input)?;
12 | Ok(())
13 | }
14 |
15 | fn part1(input: &str) -> Result<()> {
16 | let mut freq = 0;
17 | for line in input.lines() {
18 | let change: i32 = line.parse()?;
19 | freq += change;
20 | }
21 | writeln!(io::stdout(), "{}", freq)?;
22 | Ok(())
23 | }
24 |
25 | fn part2(input: &str) -> Result<()> {
26 | let mut freq = 0;
27 | let mut seen = HashSet::new();
28 | seen.insert(0);
29 |
30 | loop {
31 | for line in input.lines() {
32 | let change: i32 = line.parse()?;
33 | freq += change;
34 | if seen.contains(&freq) {
35 | writeln!(io::stdout(), "{}", freq)?;
36 | return Ok(());
37 | }
38 | seen.insert(freq);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/aoc02/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc02"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc02/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc02"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc02/input/input.txt:
--------------------------------------------------------------------------------
1 | rmyxgdlihczskunpfwbgqoeybv
2 | rmyxgdlksczskunpfwbjqkeatv
3 | rmybgdxibczskunpfwbjqoeatv
4 | rmyxgdlirczskuopfwbjqzeatv
5 | rmyxedlrhczskunpfwbyqoeatv
6 | rmyxfdlicczskunpfwbxqoeatv
7 | rmyxgvlihkzskunpfwbsqoeatv
8 | rmyxgdaihczvkunpfwblqoeatv
9 | nmyxgolihczskunpfwbjqieatv
10 | rhyxgdcihczskunifwbjqoeatv
11 | rmfxgdlihczskunpfwbvqgeatv
12 | smyxgdlihczskunsiwbjqoeatv
13 | rmyxgdcihcxskunpfwbrqoeatv
14 | rmyxgdlihczckuiqfwbjqoeatv
15 | rmyxxdwihczskunifwbjqoeatv
16 | rkzxgdlihczskunpfwhjqoeatv
17 | rmypgdlihczskunpfwbrqoeafv
18 | rmyxgplihczvkunpkwbjqoeatv
19 | rqyxgdlihdzskjnpfwbjqoeatv
20 | rmyxgdlihczskqnpswbjqoeaov
21 | mcyxgdlihczmkunpfwbjqoeatv
22 | rmyxgdlohczspunpowbjqoeatv
23 | tmyxgdlihczskunpfwbeqoeltv
24 | rmyxgdlibccskunpfwbjqoegtv
25 | rmyxgdlehczsaunpfwboqoeatv
26 | rmaxgdlihczseunpfwbjqojatv
27 | rmyxgdlijczskynpfwbjboeatv
28 | kmlxgdlilczskunpfwbjqoeatv
29 | rmsxgdlshczskenpfwbjqoeatv
30 | rmbxgdlihcmskgnpfwbjqoeatv
31 | rayxgdlihczskunpfwbjqoeaef
32 | umyxgdlisczskunpfdbjqoeatv
33 | rmyxgdlihczskunsfwbjqieatg
34 | rmbxgdlihczhkunpfwbjqoeamv
35 | rmyxgdlihczskeypfwbjqxeatv
36 | rmyxgkrihczskunptwbjqoeatv
37 | rmyxgdlihczskunpawbjqoexiv
38 | rmyxgdlihcrskqnpfwbjqceatv
39 | rmyxgblihczskjnpfwbjqieatv
40 | rmyggdlidczskunofwbjqoeatv
41 | rmyxgdlghczskunphwbjqomatv
42 | rmqxgdbihczskunpfnbjqoeatv
43 | rvyxgdlihczsgunpfwbjqoeanv
44 | royxgdlnhczskqnpfwbjqoeatv
45 | rmyxgdlihczskugpfwbkqreatv
46 | rmyxfdlihczskunppwejqoeatv
47 | rqyxgdlipczskunpfwbjqoeqtv
48 | rmyxgdlicczskunpnwbjqotatv
49 | rmyxodlihczskxnpfwijqoeatv
50 | rmyxrdyihczskunpftbjqoeatv
51 | rmtxgdyihwzskunpfwbjqoeatv
52 | tmyxcdliiczskunpfwbjqoeatv
53 | rmyxgdlihczskmnpfwbjjoeadv
54 | rmyxgdnihczskunpqwbjqojatv
55 | bmyxgdlihczskcnpfwboqoeatv
56 | rmysgdlihcyskudpfwbjqoeatv
57 | rmyxgdtihczsmuupfwbjqoeatv
58 | rmyxgdlihczssunpffbjqolatv
59 | rmyogdlihczsklnpfwbjqoxatv
60 | rmyxgjlihczskunpfwsjqoyatv
61 | rmyxgalshczskunpfwbuqoeatv
62 | rmyfgdlihczskunqfwbiqoeatv
63 | tmyxgdlihczskunotwbjqoeatv
64 | rmyxpdzihczskuopfwbjqoeatv
65 | rmyfgdlihczskunpfrbgqoeatv
66 | rmyxgdlwhczskhnofwbjqoeatv
67 | rmyxgdlihczsmudpfrbjqoeatv
68 | rmyxgdlihczokanpfwbjqooatv
69 | rmyxrdlihczskunppwjjqoeatv
70 | rmyxgdjihczskwnpowbjqoeatv
71 | mmyxgdlihczikunpfwbjqoeamv
72 | rmyxgflihczshunpwwbjqoeatv
73 | rmytghlihczskunpfwbjqoeatk
74 | rmyxgdlipczmbunpfwbjqoeatv
75 | rmyxgdlihczkkonpfwbjqomatv
76 | rmfxgslihczskunpfwujqoeatv
77 | dmyxgdlihczykunqfwbjqoeatv
78 | rmyxgalihcbskunpgwbjqoeatv
79 | rmyxgdlinczqkunpfwbjqopatv
80 | rmyxgdlihwzslunplwbjqoeatv
81 | rmypgdlihczskdtpfwbjqoeatv
82 | rmsxgdxieczskunpfwbjqoeatv
83 | rmyxgdlihczskwnpfxrjqoeatv
84 | rmyxgdlihzzskunpflbjpoeatv
85 | rslxgdlihczsnunpfwbjqoeatv
86 | rmyxgdlmcczskunpfwbjqoealv
87 | fmkxgdbihczskunpfwbjqoeatv
88 | rmyxgdiigczxkunpfwbjqoeatv
89 | rjyxgnlqhczskunpfwbjqoeatv
90 | ymyxgolihczskunpfmbjqoeatv
91 | hmyxgdlihczskuncfwbjqoejtv
92 | rmyxgqlihczzkunpfwbjqojatv
93 | rmgfgdlihczskunpfwbjgoeatv
94 | rmyxgdlfhczskunpfwbjqweaxv
95 | rmoxtdlihczskunpfwdjqoeatv
96 | ruyxgdlihczskunpfmbjnoeatv
97 | rmnxgflehczskunpfwbjqoeatv
98 | rmyugdlihczskunpfwfjroeatv
99 | rmyxddbihczskunpfwbjqoeutv
100 | rmyxgdlipczskunofbbjqoeatv
101 | gmyxgdlihczskunpfkbjroeatv
102 | rmyxgdllhcpskunpfwbjqqeatv
103 | rmyxgdlihchskunpfwbjqoelcv
104 | mmyxldlihczskuncfwbjqoeatv
105 | ryyxgdlxhczskcnpfwbjqoeatv
106 | rmyxpdlihczskyntfwbjqoeatv
107 | rmhxgdlibczskwnpfwbjqoeatv
108 | rmyxgdlihczskunpfwojbkeatv
109 | qmyxgdlihczskunpfwbjqoyatm
110 | rmyxgdlzhczskunpfwbjqoealr
111 | rmyegdliqczskunpfgbjqoeatv
112 | umyxgdlihczsvunpfwbfqoeatv
113 | rmyxgdoihfzskunpfmbjqoeatv
114 | rmyxgdlihcdskanpmwbjqoeatv
115 | rmyxgdyihczskunpfrbjqoeaov
116 | rcyxgdlihczskuegfwbjqoeatv
117 | rmyxgdlihgwskunpfwbjkoeatv
118 | rpyxgdlihmzskunpfwbjqoeatp
119 | rmyxgdlihhzskunpfwbjaoeapv
120 | rmyxgdsrhczskunpflbjqoeatv
121 | rmrxgdlihczskunpvwbjqoeabv
122 | rmcxgylihczskunpfwbjyoeatv
123 | rmkxgdlyhczsounpfwbjqoeatv
124 | rmyxgdqihczskunmfwbjqoratv
125 | rmyxgdlihczskunpfibjqofath
126 | rmyxgdliqczskunpqwbjqoeaev
127 | rmhxgdlizcjskunpfwbjqoeatv
128 | rmyxgdlfhcwskunpfwbjqoeaqv
129 | rmyxgdlchclskunpfwbdqoeatv
130 | rmyxgdluhczswunpfwbjqoeatt
131 | rmyxgdlzqczskunpfwbjqoeatq
132 | rmdxgdlihszskunpfwbwqoeatv
133 | rmyxgdlihszsvunpfwbjqueatv
134 | rmyxgdlhhczskunpffbjaoeatv
135 | rmrxgdlphczskunpfwbjqreatv
136 | hmyngdxihczskunpfwbjqoeatv
137 | rmyxgdlizczpkunpfwbyqoeatv
138 | rmyxbdlihyzskunlfwbjqoeatv
139 | rmyxgdlipczsqunnfwbjqoeatv
140 | rmyxgdlihcsskunpfxbjqoaatv
141 | rmyxgdljhcznkunpfwbjqfeatv
142 | rmaxgdlihczspunpfwbjqoqatv
143 | rsyxgdlihczskunpfwbjqoehcv
144 | rmyxgjlicczskunpfwbjqoeitv
145 | rwymgvlihczskunpfwbjqoeatv
146 | rmyxgdlipfzskunpfwbjqweatv
147 | rmyxgglihczskunpgwbjqoealv
148 | royxgdlihczskhnpfwbyqoeatv
149 | rmyxgdlihczskvnpfabkqoeatv
150 | rmyxgdlihczskunpfwhjwzeatv
151 | jlyxgdlihczskunpfwbjqzeatv
152 | rmyxgdlihccskunpfwwjqopatv
153 | rmyxgxlihczskuupfwbjqoeahv
154 | rmyxgdcihcbskungfwbjqoeatv
155 | tmyxgdlihczskunpfwbjmoeftv
156 | rkyxgdlioczskmnpfwbjqoeatv
157 | rmyxgdlrhczskulpfwbjaoeatv
158 | rmysgdlihczikunphwbjqoeatv
159 | rmyxgdlihczskuvpfwbjqoeyty
160 | fmyxgdlihczscunpfqbjqoeatv
161 | rfyxgdlihzzrkunpfwbjqoeatv
162 | rmyxgdlikczskunpfwbjqolath
163 | rmyxqdlihjzskunpfwbjqoeamv
164 | rmuxodiihczskunpfwbjqoeatv
165 | rmyygdliucuskunpfwbjqoeatv
166 | rmyxgdliwczskuppawbjqoeatv
167 | rmyxgdlihczskunprwbjqgehtv
168 | imyvgdlihczskunpfwbjqouatv
169 | rgyxgdluhczskunpflbjqoeatv
170 | rmgxgdlihczsdunpfwwjqoeatv
171 | gdyxgdlihczskunpfwbjqoeavv
172 | rmyxgdlihczskunpfwljjoektv
173 | rmexgdlihczskunpfwxjqoeytv
174 | rmyxqdlihcyskuwpfwbjqoeatv
175 | rmyxgdlihczskunpfiyjqcebtv
176 | amyngdlihczskunpfwbjqseatv
177 | rmzxgdlihczykubpfwbjqoeatv
178 | rmyxgdlihczhkuopfwbjsoeatv
179 | rmyxgdlihczskunpfwbaqowztv
180 | rmgxgdlihczslunpfwbjeoeatv
181 | rmytgdlzhczskunrfwbjqoeatv
182 | rmyxgdtihczskunafobjqoeatv
183 | rmyxgdlihczskuflfbbjqoeatv
184 | rmdxgdlihczskunpfwbjqoealj
185 | rbyxgdlihczskuppdwbjqoeatv
186 | rmyxhdiihcwskunpfwbjqoeatv
187 | rmmggdlfhczskunpfwbjqoeatv
188 | rmbxgblihczskuypfwbjqoeatv
189 | rmyxgslihczsjunpjwbjqoeatv
190 | rmyxgdlohczsaunpfwbjboeatv
191 | rmaxgdhihczskunpfwbjooeatv
192 | rmyxidlihczskunpfgbuqoeatv
193 | rmyxgdlihfzckznpfwbjqoeatv
194 | rmaqgdpihczskunpfwbjqoeatv
195 | rmyvgdlirczskunpfobjqoeatv
196 | rmdxgdlihczlkunpxwbjqoeatv
197 | rmyxgdlihczseunpfwbjvdeatv
198 | rmyxgdlihczskuhpfwbjqneath
199 | rmyxrdlihciskunpfwbjqoratv
200 | rmyxgdmihczsqunpftbjqoeatv
201 | rmyxgdlbhczskulpfbbjqoeatv
202 | rmoxgdlihczskunpfwbjqoeesv
203 | rmyxgdlihczskuijfwejqoeatv
204 | rmyxgdlihczskunpfwnkqoxatv
205 | rmyxgdvihmzskuupfwbjqoeatv
206 | rkyxedlihczskunpfcbjqoeatv
207 | rmyxgdjihczskunprwbjqieatv
208 | omyxgqgihczskunpfwbjqoeatv
209 | rmyxydlihczskunpfwkjqoentv
210 | rmbxgdlicczskunpfwbjqteatv
211 | emyxgdlihczskugpfwbjqneatv
212 | dmyxgflihczskunpfwbjqjeatv
213 | umyxgdlihczskunpfwbjloextv
214 | rmyxgdlihczsbunpfwbyqpeatv
215 | rmyxgdrihczsvunpcwbjqoeatv
216 | qmyxgdlihcwsknnpfwbjqoeatv
217 | ymyxgdlihczskunpfsbjqowatv
218 | rmyxgdlbhczskunpnvbjqoeatv
219 | rmyxfdlixczskunpfwbjqoertv
220 | rmyygdlihszrkunpfwbjqoeatv
221 | rmyxgxlihcpskunpfwbjqoeanv
222 | rmyxgdlihczskjnpfwbjqoprtv
223 | rmyxgdlisczfkunpfwbjqoeath
224 | rmyxgdlihczskunpfkbjqoeaji
225 | rmyxgylihczskunpfwbfqoeatl
226 | rmsxgdbihczskunpfwtjqoeatv
227 | smyxgdlihczskunpfwbjqcwatv
228 | rmyxgdlihczskunppjljqoeatv
229 | rmyxgdlihczskulpfdbjooeatv
230 | rmyxgdlihczskunpfibjqcebtv
231 | rmyxadlihczskunpgwbjyoeatv
232 | rmyxgdlihczdkunpvwbjqoeytv
233 | rmyxgdlihcvskunpfwbjxohatv
234 | rmyxgplihczskunpfgbjqoeauv
235 | rmyxgdlihcysrunmfwbjqoeatv
236 | rmyygdlihczskunpfwbjqvewtv
237 | rmyxgdlihczsmunpfwdjnoeatv
238 | rmyxgdbibczskunpfwbjuoeatv
239 | rmyfgdlihczskubpfwbjqoeatp
240 | rmyxgdlihczskuopfzijqoeatv
241 | rmyqgdlihczskunpwwbjqoeanv
242 | imyxgdlihczskunpfwbjqoqytv
243 | rmyxgdlixcoskbnpfwbjqoeatv
244 | rmyxgrlihccskunpfwbjqteatv
245 | rdyxgdlihcpskunpfwbjqoratv
246 | rmyxgdlihkzskunpfwbjmoeatj
247 | rmyxgslihczskcnpfjbjqoeatv
248 | rmyxgdlihczsqunqfwdjqoeatv
249 | rjyxgdlyhczbkunpfwbjqoeatv
250 | rmyxudlihczjkunpfwbjqzeatv
251 |
--------------------------------------------------------------------------------
/aoc02/input/test.txt:
--------------------------------------------------------------------------------
1 | abcdef
2 | bababc
3 | abbcde
4 | abcccd
5 | aabcdd
6 | abcdee
7 | ababab
8 |
--------------------------------------------------------------------------------
/aoc02/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::io::{self, Read, Write};
2 |
3 | type Result = ::std::result::Result>;
4 |
5 | fn main() -> Result<()> {
6 | let mut input = String::new();
7 | io::stdin().read_to_string(&mut input)?;
8 |
9 | part1(&input)?;
10 | part2(&input)?;
11 | Ok(())
12 | }
13 |
14 | fn part1(input: &str) -> Result<()> {
15 | let mut frequencies = [0u8; 256];
16 | let (mut twos, mut threes) = (0, 0);
17 | for line in input.lines() {
18 | if !line.is_ascii() {
19 | return Err(From::from("part1 only supports ASCII"));
20 | }
21 |
22 | for f in frequencies.iter_mut() {
23 | *f = 0;
24 | }
25 | for b in line.as_bytes().iter().map(|&b| b as usize) {
26 | frequencies[b] = frequencies[b].saturating_add(1);
27 | }
28 | if frequencies.iter().any(|&f| f == 2) {
29 | twos += 1;
30 | }
31 | if frequencies.iter().any(|&f| f == 3) {
32 | threes += 1;
33 | }
34 | }
35 | writeln!(io::stdout(), "{}", twos * threes)?;
36 | Ok(())
37 | }
38 |
39 | fn part2(input: &str) -> Result<()> {
40 | let ids: Vec<&str> = input.lines().collect();
41 | for i in 0..ids.len() {
42 | for j in i+1..ids.len() {
43 | if let Some(common) = common_correct_letters(&ids[i], &ids[j]) {
44 | writeln!(io::stdout(), "{}", common)?;
45 | return Ok(());
46 | }
47 | }
48 | }
49 | Err(From::from("could not find two correct box ids"))
50 | }
51 |
52 | fn common_correct_letters(id1: &str, id2: &str) -> Option {
53 | if id1.len() != id2.len() {
54 | return None;
55 | }
56 |
57 | let mut found_one_wrong = false;
58 | for (c1, c2) in id1.chars().zip(id2.chars()) {
59 | if c1 != c2 {
60 | if found_one_wrong {
61 | return None;
62 | }
63 | found_one_wrong = true;
64 | }
65 | }
66 | Some(
67 | id1.chars().zip(id2.chars())
68 | .filter(|&(c1, c2)| c1 == c2)
69 | .map(|(c, _)| c)
70 | .collect()
71 | )
72 | }
73 |
--------------------------------------------------------------------------------
/aoc03/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc03"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.44"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.1"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
90 | "checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc03/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc03"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | regex = "1"
9 | lazy_static = "1"
10 |
--------------------------------------------------------------------------------
/aoc03/src/main.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | extern crate lazy_static;
3 | extern crate regex;
4 |
5 | use std::collections::HashMap;
6 | use std::error::Error;
7 | use std::io::{self, Read, Write};
8 | use std::result;
9 | use std::str::FromStr;
10 |
11 | use regex::Regex;
12 |
13 | macro_rules! err {
14 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
15 | }
16 |
17 | type Result = result::Result>;
18 |
19 | // Maps a point to the count of overlapping claims corresponding to that point.
20 | type Grid = HashMap<(u32, u32), u32>;
21 |
22 | fn main() -> Result<()> {
23 | let mut input = String::new();
24 | io::stdin().read_to_string(&mut input)?;
25 |
26 | let mut claims: Vec = vec![];
27 | for line in input.lines() {
28 | let claim = line.parse().or_else(|err| {
29 | err!("failed to parse '{:?}': {}", line, err)
30 | })?;
31 | claims.push(claim);
32 | }
33 |
34 | let mut grid = Grid::new();
35 | for claim in &claims {
36 | for (x, y) in claim.iter_points() {
37 | *grid.entry((x, y)).or_default() += 1;
38 | }
39 | }
40 |
41 | part1(&grid)?;
42 | part2(&claims, &grid)?;
43 | Ok(())
44 | }
45 |
46 | fn part1(grid: &Grid) -> Result<()> {
47 | let count = grid.values().filter(|&&count| count > 1).count();
48 | writeln!(io::stdout(), "contested points: {}", count)?;
49 | Ok(())
50 | }
51 |
52 | fn part2(claims: &[Claim], grid: &Grid) -> Result<()> {
53 | for claim in claims {
54 | if claim.iter_points().all(|p| grid[&p] == 1) {
55 | writeln!(io::stdout(), "uncontested claim: {}", claim.id)?;
56 | return Ok(());
57 | }
58 | }
59 | err!("no uncontested claims")
60 | }
61 |
62 | #[derive(Debug)]
63 | struct Claim {
64 | id: u32,
65 | x: u32,
66 | y: u32,
67 | width: u32,
68 | height: u32,
69 | }
70 |
71 | impl Claim {
72 | fn iter_points(&self) -> IterPoints {
73 | IterPoints {
74 | claim: self,
75 | px: self.x,
76 | py: self.y,
77 | }
78 | }
79 | }
80 |
81 | struct IterPoints<'c> {
82 | claim: &'c Claim,
83 | px: u32,
84 | py: u32,
85 | }
86 |
87 | impl<'c> Iterator for IterPoints<'c> {
88 | type Item = (u32, u32);
89 |
90 | fn next(&mut self) -> Option<(u32, u32)> {
91 | if self.py >= self.claim.y + self.claim.height {
92 | self.py = self.claim.y;
93 | self.px += 1;
94 | }
95 | if self.px >= self.claim.x + self.claim.width {
96 | return None;
97 | }
98 | let (px, py) = (self.px, self.py);
99 | self.py += 1;
100 | Some((px, py))
101 | }
102 | }
103 |
104 | impl FromStr for Claim {
105 | type Err = Box;
106 |
107 | fn from_str(s: &str) -> Result {
108 | lazy_static! {
109 | static ref RE: Regex = Regex::new(r"(?x)
110 | \#
111 | (?P[0-9]+)
112 | \s+@\s+
113 | (?P[0-9]+),(?P[0-9]+):
114 | \s+
115 | (?P[0-9]+)x(?P[0-9]+)
116 | ").unwrap();
117 | }
118 |
119 | let caps = RE.captures(s).ok_or("unrecognized claim")?;
120 | Ok(Claim {
121 | id: caps["id"].parse()?,
122 | x: caps["x"].parse()?,
123 | y: caps["y"].parse()?,
124 | width: caps["width"].parse()?,
125 | height: caps["height"].parse()?,
126 | })
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/aoc04/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc04"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.44"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.1"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
90 | "checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc04/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc04"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1"
9 | regex = "1"
10 |
--------------------------------------------------------------------------------
/aoc04/input/test.txt:
--------------------------------------------------------------------------------
1 | [1518-11-01 00:00] Guard #10 begins shift
2 | [1518-11-01 00:05] falls asleep
3 | [1518-11-01 00:25] wakes up
4 | [1518-11-01 00:30] falls asleep
5 | [1518-11-01 00:55] wakes up
6 | [1518-11-01 23:58] Guard #99 begins shift
7 | [1518-11-02 00:40] falls asleep
8 | [1518-11-02 00:50] wakes up
9 | [1518-11-03 00:05] Guard #10 begins shift
10 | [1518-11-03 00:24] falls asleep
11 | [1518-11-03 00:29] wakes up
12 | [1518-11-04 00:02] Guard #99 begins shift
13 | [1518-11-04 00:36] falls asleep
14 | [1518-11-04 00:46] wakes up
15 | [1518-11-05 00:03] Guard #99 begins shift
16 | [1518-11-05 00:45] falls asleep
17 | [1518-11-05 00:55] wakes up
18 |
--------------------------------------------------------------------------------
/aoc04/src/main.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | extern crate lazy_static;
3 | extern crate regex;
4 |
5 | use std::collections::HashMap;
6 | use std::error::Error;
7 | use std::io::{self, Read, Write};
8 | use std::ops::Range;
9 | use std::result;
10 | use std::slice;
11 | use std::str::FromStr;
12 |
13 | use regex::Regex;
14 |
15 | macro_rules! err {
16 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
17 | }
18 |
19 | type Result = result::Result>;
20 |
21 | fn main() -> Result<()> {
22 | let mut input = String::new();
23 | io::stdin().read_to_string(&mut input)?;
24 |
25 | // collect events
26 | let mut events: Vec = vec![];
27 | for line in input.lines() {
28 | let event = line.parse().or_else(|err| {
29 | err!("failed to parse '{:?}': {}", line, err)
30 | })?;
31 | events.push(event);
32 | }
33 | if events.is_empty() {
34 | return err!("found no events");
35 | }
36 |
37 | // sort them by time and group them by guard
38 | events.sort_by(|ev1, ev2| ev1.datetime.cmp(&ev2.datetime));
39 | let mut events_by_guard = EventsByGuard::new();
40 | let mut cur_guard_id = None;
41 | for ev in events {
42 | if let EventKind::StartShift { guard_id } = ev.kind {
43 | cur_guard_id = Some(guard_id);
44 | }
45 | match cur_guard_id {
46 | None => return err!("no guard id set for event"),
47 | Some(id) => {
48 | events_by_guard.entry(id).or_default().push(ev);
49 | }
50 | }
51 | }
52 |
53 | // create a by-minute frequency map for each guard
54 | let mut minutes_asleep: GuardSleepFrequency = HashMap::new();
55 | for (&guard_id, events) in events_by_guard.iter() {
56 | let mut freq: [u32; 60] = [0; 60];
57 | for result in MinutesAsleepIter::new(events) {
58 | for minute in result? {
59 | freq[minute as usize] += 1;
60 | }
61 | }
62 | minutes_asleep.insert(guard_id, freq);
63 | }
64 |
65 | part1(&minutes_asleep)?;
66 | part2(&minutes_asleep)?;
67 | Ok(())
68 | }
69 |
70 | fn part1(minutes_asleep: &GuardSleepFrequency) -> Result<()> {
71 | let (&sleepiest, _) = minutes_asleep
72 | .iter()
73 | .max_by_key(|&(_, ref freqs)| -> u32 {
74 | freqs.iter().sum()
75 | })
76 | // unwrap is OK since we're guaranteed to have at least one event
77 | .unwrap();
78 | let minute = match sleepiest_minute(minutes_asleep, sleepiest) {
79 | None => return err!("guard {} was never asleep", sleepiest),
80 | Some(minute) => minute,
81 | };
82 |
83 | writeln!(io::stdout(), "part 1, product: {}", sleepiest * minute)?;
84 | Ok(())
85 | }
86 |
87 | fn part2(minutes_asleep: &GuardSleepFrequency) -> Result<()> {
88 | let mut sleepiest_minutes: HashMap = HashMap::new();
89 | for (&guard_id, freqs) in minutes_asleep.iter() {
90 | let minute = match sleepiest_minute(minutes_asleep, guard_id) {
91 | None => continue,
92 | Some(minute) => minute,
93 | };
94 | let count = freqs[minute as usize];
95 | sleepiest_minutes.insert(guard_id, (minute, count));
96 | }
97 | if sleepiest_minutes.is_empty() {
98 | return err!("no guards slept");
99 | }
100 |
101 | let (&longest_asleep, &(minute, _)) = sleepiest_minutes
102 | .iter()
103 | .max_by_key(|&(_, (_, count))| count)
104 | // unwrap is OK because sleepiest_minutes is non-empty
105 | .unwrap();
106 |
107 | writeln!(io::stdout(), "part 2, product: {}", longest_asleep * minute)?;
108 | Ok(())
109 | }
110 |
111 | /// Return the minute that the given guard slept the most.
112 | fn sleepiest_minute(
113 | minutes_asleep: &GuardSleepFrequency,
114 | guard_id: GuardID,
115 | ) -> Option {
116 | let (sleepiest_minute, ..) = minutes_asleep[&guard_id]
117 | .iter()
118 | .enumerate()
119 | .max_by_key(|(_, freq)| -> u32 { **freq })
120 | .expect("Iterator of sleepy minutes should not be empty");
121 | Some(sleepiest_minute as u32)
122 | }
123 |
124 | type GuardID = u32;
125 |
126 | type EventsByGuard = HashMap>;
127 |
128 | // maps guard to minutes asleep frequency
129 | type GuardSleepFrequency = HashMap;
130 |
131 | /// An iterator that coalesces "asleep" and "wakeup" events into ranges of
132 | /// minutes slept.
133 | #[derive(Debug)]
134 | struct MinutesAsleepIter<'a> {
135 | events: slice::Iter<'a, Event>,
136 | fell_asleep: Option,
137 | }
138 |
139 | impl<'a> MinutesAsleepIter<'a> {
140 | fn new(events: &'a [Event]) -> MinutesAsleepIter<'a> {
141 | MinutesAsleepIter { events: events.iter(), fell_asleep: None }
142 | }
143 | }
144 |
145 | impl<'a> Iterator for MinutesAsleepIter<'a> {
146 | type Item = Result>;
147 |
148 | fn next(&mut self) -> Option>> {
149 | loop {
150 | let ev = match self.events.next() {
151 | Some(ev) => ev,
152 | None => {
153 | if self.fell_asleep.is_some() {
154 | return Some(err!("found sleep event without wake up"));
155 | }
156 | return None;
157 | }
158 | };
159 | match ev.kind {
160 | EventKind::StartShift { .. } => {}
161 | EventKind::Asleep => {
162 | self.fell_asleep = Some(ev.datetime.minute);
163 | }
164 | EventKind::WakeUp => {
165 | let fell_asleep = match self.fell_asleep.take() {
166 | Some(minute) => minute,
167 | None => {
168 | return Some(err!("found wakeup without sleep"));
169 | }
170 | };
171 | if ev.datetime.minute < fell_asleep {
172 | return Some(err!("found wakeup before sleep"));
173 | }
174 | return Some(Ok(fell_asleep..ev.datetime.minute));
175 | }
176 | }
177 | }
178 | }
179 | }
180 |
181 | #[derive(Debug)]
182 | struct Event {
183 | datetime: DateTime,
184 | kind: EventKind,
185 | }
186 |
187 | #[derive(Debug, Eq, PartialEq, PartialOrd, Ord)]
188 | struct DateTime {
189 | year: u32,
190 | month: u32,
191 | day: u32,
192 | hour: u32,
193 | minute: u32,
194 | }
195 |
196 | #[derive(Debug)]
197 | enum EventKind {
198 | StartShift { guard_id: GuardID },
199 | Asleep,
200 | WakeUp,
201 | }
202 |
203 | impl FromStr for Event {
204 | type Err = Box;
205 |
206 | fn from_str(s: &str) -> Result {
207 | lazy_static! {
208 | static ref RE: Regex = Regex::new(r"(?x)
209 | \[
210 | (?P[0-9]{4})-(?P[0-9]{2})-(?P[0-9]{2})
211 | \s+
212 | (?P[0-9]{2}):(?P[0-9]{2})
213 | \]
214 | \s+
215 | (?:Guard\ \#(?P[0-9]+)\ begins\ shift|(?P.+))
216 | ").unwrap();
217 | }
218 |
219 | let caps = match RE.captures(s) {
220 | None => return err!("unrecognized event"),
221 | Some(caps) => caps,
222 | };
223 | let datetime = DateTime {
224 | year: caps["year"].parse()?,
225 | month: caps["month"].parse()?,
226 | day: caps["day"].parse()?,
227 | hour: caps["hour"].parse()?,
228 | minute: caps["minute"].parse()?,
229 | };
230 | let kind =
231 | if let Some(m) = caps.name("id") {
232 | EventKind::StartShift { guard_id: m.as_str().parse()? }
233 | } else if &caps["sleep"] == "falls asleep" {
234 | EventKind::Asleep
235 | } else if &caps["sleep"] == "wakes up" {
236 | EventKind::WakeUp
237 | } else {
238 | return err!("could not determine event kind")
239 | };
240 | Ok(Event { datetime, kind })
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/aoc05/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc05"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc05/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc05"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc05/input/test.txt:
--------------------------------------------------------------------------------
1 | dabAcCaCBAcCcaDA
2 |
--------------------------------------------------------------------------------
/aoc05/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::io::{self, Read, Write};
2 | use std::mem;
3 |
4 | type Result = ::std::result::Result>;
5 |
6 | fn main() -> Result<()> {
7 | let mut input = String::new();
8 | io::stdin().read_to_string(&mut input)?;
9 | let input = input.trim();
10 |
11 | part1(input)?;
12 | part2(input)?;
13 | Ok(())
14 | }
15 |
16 | fn part1(polymer: &str) -> Result<()> {
17 | writeln!(io::stdout(), "inert length: {}", react(polymer).len())?;
18 | Ok(())
19 | }
20 |
21 | fn part2(polymer: &str) -> Result<()> {
22 | let mut best = polymer.len();
23 | for b in b'A'..=b'Z' {
24 | let unit1 = b as char;
25 | let unit2 = (b + 32) as char;
26 | let cleansed = polymer.replace(unit1, "").replace(unit2, "");
27 | let reacted = react(&cleansed);
28 | if reacted.len() < best {
29 | best = reacted.len();
30 | }
31 | }
32 | writeln!(io::stdout(), "best inert length: {}", best)?;
33 | Ok(())
34 | }
35 |
36 | /// Reacts the given polymer and returns the final inert form.
37 | fn react(polymer_string: &str) -> String {
38 | let mut polymer = polymer_string.as_bytes().to_vec();
39 | let mut reacted_polymer = vec![];
40 | loop {
41 | let mut reacted = false;
42 | let mut i = 1;
43 | while i < polymer.len() {
44 | if reacts(polymer[i-1], polymer[i]) {
45 | reacted = true;
46 | i += 2;
47 | continue;
48 | }
49 | reacted_polymer.push(polymer[i-1]);
50 | i += 1;
51 | }
52 | if i == polymer.len() {
53 | reacted_polymer.push(polymer[i-1]);
54 | }
55 |
56 | mem::swap(&mut polymer, &mut reacted_polymer);
57 | reacted_polymer.clear();
58 | if !reacted {
59 | break;
60 | }
61 | }
62 | // We only ever remove ASCII bytes, which is guaranteed to preserve the
63 | // UTF-8 validity of `polymer`.
64 | String::from_utf8(polymer).unwrap()
65 | }
66 |
67 | /// Returns true if and only if the given bytes correspond to types that
68 | /// react with one another.
69 | fn reacts(b1: u8, b2: u8) -> bool {
70 | if b1 < b2 {
71 | b2 - b1 == 32
72 | } else {
73 | b1 - b2 == 32
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/aoc06/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc06"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc06/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc06"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc06/input/input.txt:
--------------------------------------------------------------------------------
1 | 342, 203
2 | 79, 64
3 | 268, 323
4 | 239, 131
5 | 246, 87
6 | 161, 93
7 | 306, 146
8 | 43, 146
9 | 57, 112
10 | 241, 277
11 | 304, 303
12 | 143, 235
13 | 253, 318
14 | 97, 103
15 | 200, 250
16 | 67, 207
17 | 345, 149
18 | 133, 222
19 | 232, 123
20 | 156, 359
21 | 80, 224
22 | 51, 145
23 | 138, 312
24 | 339, 294
25 | 297, 256
26 | 163, 311
27 | 241, 321
28 | 126, 66
29 | 145, 171
30 | 359, 184
31 | 241, 58
32 | 108, 312
33 | 117, 118
34 | 101, 180
35 | 58, 290
36 | 324, 42
37 | 141, 190
38 | 270, 149
39 | 209, 294
40 | 296, 345
41 | 68, 266
42 | 233, 281
43 | 305, 183
44 | 245, 230
45 | 161, 295
46 | 335, 352
47 | 93, 66
48 | 227, 59
49 | 264, 249
50 | 116, 173
51 |
--------------------------------------------------------------------------------
/aoc06/input/test.txt:
--------------------------------------------------------------------------------
1 | 1, 1
2 | 1, 6
3 | 8, 3
4 | 3, 4
5 | 5, 5
6 | 8, 9
7 |
--------------------------------------------------------------------------------
/aoc06/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::collections::{HashMap, HashSet};
2 | use std::error::Error;
3 | use std::io::{self, Read, Write};
4 | use std::result;
5 | use std::str::FromStr;
6 |
7 | type Result = result::Result>;
8 |
9 | fn main() -> Result<()> {
10 | let mut input = String::new();
11 | io::stdin().read_to_string(&mut input)?;
12 |
13 | let coordinates = input
14 | .lines()
15 | .map(|line| line.parse())
16 | .collect::>>()?;
17 | if coordinates.is_empty() {
18 | return Err(From::from("no coordinates given"));
19 | }
20 |
21 | let mut grid = Grid::new(coordinates);
22 | grid.find_finite();
23 |
24 | part1(&grid)?;
25 | part2(&grid)?;
26 | Ok(())
27 | }
28 |
29 | fn part1(grid: &Grid) -> Result<()> {
30 | let mut biggest_area = 0;
31 | for &loc in &grid.finite {
32 | let mut candidate_area = 0;
33 | for &loc2 in grid.table.values() {
34 | if loc == loc2 {
35 | candidate_area += 1;
36 | }
37 | }
38 | if candidate_area > biggest_area {
39 | biggest_area = candidate_area;
40 | }
41 | }
42 | writeln!(io::stdout(), "biggest area: {}", biggest_area)?;
43 | Ok(())
44 | }
45 |
46 | fn part2(grid: &Grid) -> Result<()> {
47 | // Similar to part 1, we simply choose a bounding box. Experimentation
48 | // indicates convergence.
49 | let bound = 500;
50 | let mut size = 0;
51 | for x in -bound..=bound {
52 | for y in -bound..=bound {
53 | if grid.distance_sum(Coordinate { x, y }) < 10000 {
54 | size += 1;
55 | }
56 | }
57 | }
58 | writeln!(io::stdout(), "size: {}", size)?;
59 | Ok(())
60 | }
61 |
62 | #[derive(Debug)]
63 | struct Grid {
64 | // all coordinates given in the input
65 | locations: Vec,
66 | // all known finite coordinates
67 | finite: HashSet,
68 | // a map from an arbitrary coordinate to its closest location
69 | table: HashMap,
70 | }
71 |
72 | impl Grid {
73 | fn new(locations: Vec) -> Grid {
74 | assert!(!locations.is_empty());
75 | Grid { locations, finite: HashSet::new(), table: HashMap::new() }
76 | }
77 |
78 | fn find_finite(&mut self) {
79 | // This isn't actually guaranteed to be correct. We simply assert that
80 | // after some fixed number of iterations, our set of finite locations
81 | // converges.
82 | //
83 | // I started this trying for a solution that didn't assume a bounding
84 | // box size, which would have made this much simpler. At the end of
85 | // the day, we're still not fully general because there is no logic
86 | // for detecting convergence.
87 | for step in 0..100 {
88 | for loc in &self.locations {
89 | if self.finite.contains(&loc) {
90 | continue;
91 | }
92 | for c in loc.border(step) {
93 | let closest = match self.closest_location(c) {
94 | None => continue,
95 | Some(closest) => closest,
96 | };
97 | self.table.insert(c, closest);
98 | }
99 | }
100 | for &loc in &self.locations {
101 | if !loc.border(step).any(|c| self.table.get(&c) == Some(&loc)) {
102 | self.finite.insert(loc);
103 | }
104 | }
105 | }
106 | }
107 |
108 | /// Returns the sum of distances between the given coordinate and all
109 | /// locations.
110 | fn distance_sum(&self, c: Coordinate) -> i32 {
111 | self.locations.iter().map(|&loc| loc.distance(c)).sum()
112 | }
113 |
114 | /// Returns a unique location with minimum distance to the given
115 | /// coordinate. If no such unique minimum exists, then None is returned.
116 | fn closest_location(&self, c: Coordinate) -> Option {
117 | let (mut min, mut unique) = (self.locations[0], true);
118 | for &loc in &self.locations[1..] {
119 | if loc.distance(c) == min.distance(c) {
120 | unique = false;
121 | } else if loc.distance(c) < min.distance(c) {
122 | min = loc;
123 | unique = true;
124 | }
125 | }
126 | if !unique {
127 | None
128 | } else {
129 | Some(min)
130 | }
131 | }
132 | }
133 |
134 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
135 | struct Coordinate {
136 | x: i32,
137 | y: i32,
138 | }
139 |
140 | impl Coordinate {
141 | fn distance(self, other: Coordinate) -> i32 {
142 | (self.x - other.x).abs() + (self.y - other.y).abs()
143 | }
144 |
145 | fn border(self, step: i32) -> impl Iterator- {
146 | (self.x - step..=self.x + step)
147 | .flat_map(move |x| {
148 | (self.y - step..=self.y + step)
149 | .map(move |y| Coordinate { x, y })
150 | })
151 | .filter(move |&c2| self.distance(c2) == step)
152 | }
153 | }
154 |
155 | impl FromStr for Coordinate {
156 | type Err = Box;
157 |
158 | fn from_str(s: &str) -> Result {
159 | let comma = match s.find(",") {
160 | None => return Err(From::from("could not find comma")),
161 | Some(i) => i,
162 | };
163 | let (pos1, pos2) = (&s[..comma].trim(), s[comma + 1..].trim());
164 | Ok(Coordinate { x: pos1.parse()?, y: pos2.parse()? })
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/aoc07/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc07"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.44"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.1"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
90 | "checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc07/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc07"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1"
9 | regex = "1"
10 |
--------------------------------------------------------------------------------
/aoc07/input/input.txt:
--------------------------------------------------------------------------------
1 | Step G must be finished before step N can begin.
2 | Step N must be finished before step B can begin.
3 | Step P must be finished before step Q can begin.
4 | Step F must be finished before step U can begin.
5 | Step H must be finished before step A can begin.
6 | Step C must be finished before step S can begin.
7 | Step A must be finished before step K can begin.
8 | Step M must be finished before step O can begin.
9 | Step V must be finished before step L can begin.
10 | Step E must be finished before step L can begin.
11 | Step B must be finished before step Q can begin.
12 | Step W must be finished before step J can begin.
13 | Step R must be finished before step D can begin.
14 | Step D must be finished before step S can begin.
15 | Step S must be finished before step X can begin.
16 | Step Q must be finished before step J can begin.
17 | Step I must be finished before step L can begin.
18 | Step U must be finished before step J can begin.
19 | Step Z must be finished before step X can begin.
20 | Step Y must be finished before step T can begin.
21 | Step J must be finished before step K can begin.
22 | Step T must be finished before step L can begin.
23 | Step K must be finished before step O can begin.
24 | Step O must be finished before step X can begin.
25 | Step L must be finished before step X can begin.
26 | Step Y must be finished before step O can begin.
27 | Step F must be finished before step S can begin.
28 | Step K must be finished before step L can begin.
29 | Step Z must be finished before step O can begin.
30 | Step J must be finished before step X can begin.
31 | Step K must be finished before step X can begin.
32 | Step Q must be finished before step X can begin.
33 | Step Y must be finished before step L can begin.
34 | Step E must be finished before step S can begin.
35 | Step H must be finished before step Y can begin.
36 | Step G must be finished before step P can begin.
37 | Step E must be finished before step K can begin.
38 | Step B must be finished before step L can begin.
39 | Step T must be finished before step K can begin.
40 | Step N must be finished before step R can begin.
41 | Step F must be finished before step E can begin.
42 | Step W must be finished before step Y can begin.
43 | Step U must be finished before step X can begin.
44 | Step A must be finished before step I can begin.
45 | Step Q must be finished before step Y can begin.
46 | Step P must be finished before step T can begin.
47 | Step D must be finished before step X can begin.
48 | Step E must be finished before step Y can begin.
49 | Step F must be finished before step B can begin.
50 | Step P must be finished before step I can begin.
51 | Step N must be finished before step S can begin.
52 | Step F must be finished before step V can begin.
53 | Step W must be finished before step U can begin.
54 | Step F must be finished before step A can begin.
55 | Step I must be finished before step Z can begin.
56 | Step E must be finished before step D can begin.
57 | Step R must be finished before step I can begin.
58 | Step M must be finished before step V can begin.
59 | Step R must be finished before step U can begin.
60 | Step R must be finished before step X can begin.
61 | Step G must be finished before step O can begin.
62 | Step G must be finished before step H can begin.
63 | Step M must be finished before step R can begin.
64 | Step E must be finished before step U can begin.
65 | Step F must be finished before step Z can begin.
66 | Step N must be finished before step Q can begin.
67 | Step U must be finished before step O can begin.
68 | Step J must be finished before step T can begin.
69 | Step W must be finished before step Z can begin.
70 | Step I must be finished before step J can begin.
71 | Step U must be finished before step L can begin.
72 | Step I must be finished before step X can begin.
73 | Step Z must be finished before step J can begin.
74 | Step F must be finished before step D can begin.
75 | Step N must be finished before step O can begin.
76 | Step Q must be finished before step U can begin.
77 | Step G must be finished before step L can begin.
78 | Step H must be finished before step Q can begin.
79 | Step M must be finished before step Q can begin.
80 | Step N must be finished before step D can begin.
81 | Step Z must be finished before step L can begin.
82 | Step I must be finished before step Y can begin.
83 | Step E must be finished before step X can begin.
84 | Step J must be finished before step L can begin.
85 | Step H must be finished before step W can begin.
86 | Step P must be finished before step Y can begin.
87 | Step Q must be finished before step T can begin.
88 | Step Z must be finished before step Y can begin.
89 | Step R must be finished before step T can begin.
90 | Step E must be finished before step J can begin.
91 | Step I must be finished before step T can begin.
92 | Step A must be finished before step L can begin.
93 | Step E must be finished before step R can begin.
94 | Step T must be finished before step O can begin.
95 | Step Y must be finished before step X can begin.
96 | Step A must be finished before step Q can begin.
97 | Step W must be finished before step Q can begin.
98 | Step A must be finished before step T can begin.
99 | Step B must be finished before step Y can begin.
100 | Step H must be finished before step E can begin.
101 | Step H must be finished before step K can begin.
102 |
--------------------------------------------------------------------------------
/aoc07/input/test.txt:
--------------------------------------------------------------------------------
1 | Step C must be finished before step A can begin.
2 | Step C must be finished before step F can begin.
3 | Step A must be finished before step B can begin.
4 | Step A must be finished before step D can begin.
5 | Step B must be finished before step E can begin.
6 | Step D must be finished before step E can begin.
7 | Step F must be finished before step E can begin.
8 |
--------------------------------------------------------------------------------
/aoc07/src/main.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | extern crate lazy_static;
3 | extern crate regex;
4 |
5 | use std::collections::{HashMap, HashSet};
6 | use std::error::Error;
7 | use std::io::{self, Read, Write};
8 | use std::result;
9 | use std::str::FromStr;
10 |
11 | use regex::Regex;
12 |
13 | macro_rules! err {
14 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
15 | }
16 |
17 | type Result = result::Result>;
18 |
19 | /// A map from step to all of its required dependency steps. The set of
20 | /// required dependency sets may be empty.
21 | type RequiredFor = HashMap>;
22 |
23 | type Step = char;
24 |
25 | fn main() -> Result<()> {
26 | let mut input = String::new();
27 | io::stdin().read_to_string(&mut input)?;
28 |
29 | let mut deps: Vec = vec![];
30 | for line in input.lines() {
31 | let dep = line.parse().or_else(|err| {
32 | err!("failed to parse '{:?}': {}", line, err)
33 | })?;
34 | deps.push(dep);
35 | }
36 |
37 | let mut required_for: RequiredFor = HashMap::new();
38 | for dep in deps {
39 | required_for.entry(dep.step).or_default().insert(dep.required);
40 | required_for.entry(dep.required).or_default();
41 | }
42 |
43 | part1(&required_for)?;
44 | part2(&required_for)?;
45 | Ok(())
46 | }
47 |
48 | fn part1(required_for: &RequiredFor) -> Result<()> {
49 | let mut taken: HashSet = HashSet::new();
50 | let mut order: Vec = vec![];
51 | let mut next: Vec = vec![];
52 | loop {
53 | find_next_steps(&required_for, &taken, &taken, &mut next);
54 | let next_step = match next.pop() {
55 | None => break,
56 | Some(next_step) => next_step,
57 | };
58 | taken.insert(next_step);
59 | order.push(next_step);
60 | }
61 |
62 | let answer: String = order.iter().cloned().collect();
63 | writeln!(io::stdout(), "step order: {}", answer)?;
64 | Ok(())
65 | }
66 |
67 | fn part2(required_for: &RequiredFor) -> Result<()> {
68 | let mut workers = Workers::new(5);
69 | let mut assigned: HashSet = HashSet::new();
70 | let mut done: HashSet = HashSet::new();
71 | let mut order: Vec = vec![];
72 | let mut next: Vec = vec![];
73 |
74 | let mut seconds = 0;
75 | loop {
76 | workers.run_one_step(&mut order, &mut done);
77 |
78 | find_next_steps(&required_for, &assigned, &done, &mut next);
79 | if next.is_empty() && workers.all_idle() {
80 | break;
81 | }
82 | for worker in workers.available() {
83 | let next_step = match next.pop() {
84 | None => break,
85 | Some(next_step) => next_step,
86 | };
87 | assigned.insert(next_step);
88 | workers.work_on(worker, next_step);
89 | }
90 | seconds += 1;
91 | }
92 |
93 | let answer: String = order.iter().cloned().collect();
94 | writeln!(io::stdout(), "step order (part 2): {}", answer)?;
95 | writeln!(io::stdout(), "total seconds: {}", seconds)?;
96 | Ok(())
97 | }
98 |
99 | /// Populate `next_stack` with next steps such that the steps are sorted in
100 | /// reverse lexicographically with no duplicates.
101 | ///
102 | /// Steps in `taken` are never added to the stack.
103 | ///
104 | /// Steps in `done` signify which steps have already been completed. Only steps
105 | /// with all dependencies completed will be put on to the stack.
106 | fn find_next_steps(
107 | required_for: &RequiredFor,
108 | taken: &HashSet,
109 | done: &HashSet,
110 | next_stack: &mut Vec,
111 | ) {
112 | for (&step, dependencies) in required_for {
113 | if taken.contains(&step) {
114 | continue;
115 | }
116 | if dependencies.iter().all(|s| done.contains(s)) {
117 | next_stack.push(step);
118 | }
119 | }
120 | next_stack.sort();
121 | next_stack.dedup();
122 | next_stack.reverse();
123 | }
124 |
125 | /// Workers manages the simulation of a fixed size worker pool. This tracks
126 | /// the status of each worker, whether idle or active. When active, we record
127 | /// how much and what work remains until that worker is idle again.
128 | #[derive(Debug)]
129 | struct Workers {
130 | status: Vec,
131 | }
132 |
133 | type WorkerID = usize;
134 |
135 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
136 | enum Status {
137 | Idle,
138 | Working { step: Step, remaining: u32 }
139 | }
140 |
141 | impl Workers {
142 | fn new(count: usize) -> Workers {
143 | Workers { status: vec![Status::Idle; count] }
144 | }
145 |
146 | fn available(&self) -> Vec {
147 | let mut available = vec![];
148 | for (worker, &status) in self.status.iter().enumerate() {
149 | if status == Status::Idle {
150 | available.push(worker);
151 | }
152 | }
153 | available
154 | }
155 |
156 | fn all_idle(&self) -> bool {
157 | self.status.iter().all(|s| *s == Status::Idle)
158 | }
159 |
160 | fn work_on(&mut self, worker: WorkerID, step: Step) {
161 | let status = &mut self.status[worker];
162 | assert!(*status == Status::Idle, "worker {} is not available", worker);
163 |
164 | let remaining = (step as u32) - b'A' as u32 + 1 + 60;
165 | *status = Status::Working { step, remaining }
166 | }
167 |
168 | /// Run one step in the simulation. Workers that have finished their work
169 | /// are transitioned to idle status.
170 | fn run_one_step(&mut self, order: &mut Vec, done: &mut HashSet) {
171 | for worker in 0..self.status.len() {
172 | let mut is_done = false;
173 | match self.status[worker] {
174 | Status::Idle => {}
175 | Status::Working { step, ref mut remaining } => {
176 | *remaining -= 1;
177 | if *remaining == 0 {
178 | is_done = true;
179 | order.push(step);
180 | done.insert(step);
181 | }
182 | }
183 | }
184 | if is_done {
185 | self.status[worker] = Status::Idle;
186 | }
187 | }
188 | }
189 | }
190 |
191 | #[derive(Clone, Copy, Debug)]
192 | struct Dependency {
193 | step: Step,
194 | required: Step,
195 | }
196 |
197 | impl FromStr for Dependency {
198 | type Err = Box;
199 |
200 | fn from_str(s: &str) -> Result {
201 | lazy_static! {
202 | static ref RE: Regex = Regex::new(
203 | r"Step ([A-Z]) must be finished before step ([A-Z]) can begin."
204 | ).unwrap();
205 | }
206 |
207 | let caps = match RE.captures(s) {
208 | None => return err!("unrecognized dependency"),
209 | Some(caps) => caps,
210 | };
211 | Ok(Dependency {
212 | step: caps[2].as_bytes()[0] as Step,
213 | required: caps[1].as_bytes()[0] as Step,
214 | })
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/aoc08/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc08"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc08/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc08"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc08/input/test.txt:
--------------------------------------------------------------------------------
1 | 2 3 0 3 10 11 12 1 1 0 1 99 2 1 1 2
2 |
--------------------------------------------------------------------------------
/aoc08/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 | use std::io::{self, Read, Write};
3 | use std::result;
4 |
5 | macro_rules! err {
6 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
7 | }
8 |
9 | type Result = result::Result>;
10 |
11 | fn main() -> Result<()> {
12 | let mut input = String::new();
13 | io::stdin().read_to_string(&mut input)?;
14 |
15 | let mut flat = vec![];
16 | for number in input.split_whitespace() {
17 | flat.push(number.parse()?);
18 | }
19 | let root = Node::from_flat(&flat)?;
20 |
21 | part1(&root)?;
22 | part2(&root)?;
23 | Ok(())
24 | }
25 |
26 | fn part1(root: &Node) -> Result<()> {
27 | writeln!(io::stdout(), "{}", root.sum_all_metadata())?;
28 | Ok(())
29 | }
30 |
31 | fn part2(root: &Node) -> Result<()> {
32 | writeln!(io::stdout(), "{}", root.value())?;
33 | Ok(())
34 | }
35 |
36 | #[derive(Debug, Default)]
37 | struct Node {
38 | metadata: Vec,
39 | children: Vec,
40 | // Total count of numbers in this node. For the root node, this corresponds
41 | // to the total count of all numbers in the tree.
42 | len: usize,
43 | }
44 |
45 | impl Node {
46 | fn from_flat(flat: &[i32]) -> Result {
47 | if flat.len() < 2 {
48 | return err!("invalid header for node");
49 | }
50 |
51 | let (child_count, meta_count) = (flat[0], flat[1]);
52 | let mut node = Node { len: 2, ..Node::default() };
53 | for _ in 0..child_count {
54 | let child = Node::from_flat(&flat[node.len..])?;
55 | node.len += child.len;
56 | node.children.push(child);
57 | }
58 | for _ in 0..meta_count {
59 | let meta = match flat.get(node.len) {
60 | None => return err!("no meta data matching header"),
61 | Some(&i) if i < 1 => return err!("invalid meta data"),
62 | Some(&i) => i,
63 | };
64 | node.metadata.push(meta);
65 | node.len += 1;
66 | }
67 | Ok(node)
68 | }
69 |
70 | fn sum_all_metadata(&self) -> i32 {
71 | let mut sum = self.metadata.iter().cloned().sum();
72 | for child in &self.children {
73 | sum += child.sum_all_metadata();
74 | }
75 | sum
76 | }
77 |
78 | fn value(&self) -> i32 {
79 | if self.children.is_empty() {
80 | return self.metadata.iter().cloned().sum::();
81 | }
82 |
83 | let mut sum = 0;
84 | for &i in &self.metadata {
85 | let child = match self.children.get(i as usize - 1) {
86 | None => continue,
87 | Some(child) => child,
88 | };
89 | sum += child.value();
90 | }
91 | sum
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/aoc09/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc09"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc09/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc09"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc09/input/input.txt:
--------------------------------------------------------------------------------
1 | 418 players; last marble is worth 70769 points
2 |
--------------------------------------------------------------------------------
/aoc09/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::fmt;
2 | use std::io::{self, Write};
3 |
4 | type Result = std::result::Result>;
5 |
6 | fn main() -> Result<()> {
7 | part1()?;
8 | part2()?;
9 | Ok(())
10 | }
11 |
12 | fn part1() -> Result<()> {
13 | const PLAYERS: usize = 418;
14 | const LAST_MARBLE: u32 = 70769;
15 |
16 | let mut players = vec![Player::default(); PLAYERS];
17 | play_game(&mut players, &mut Circle::new(), LAST_MARBLE);
18 | writeln!(
19 | io::stdout(),
20 | "winning score (part 1): {}",
21 | players.iter().map(|p| p.points).max().unwrap(),
22 | )?;
23 | Ok(())
24 | }
25 |
26 | fn part2() -> Result<()> {
27 | const PLAYERS: usize = 418;
28 | const LAST_MARBLE: u32 = 7076900;
29 |
30 | let mut players = vec![Player::default(); PLAYERS];
31 | play_game(&mut players, &mut Circle::new(), LAST_MARBLE);
32 | writeln!(
33 | io::stdout(),
34 | "winning score (part 2): {}",
35 | players.iter().map(|p| p.points).max().unwrap(),
36 | )?;
37 | Ok(())
38 | }
39 |
40 | fn play_game(players: &mut [Player], circle: &mut Circle, marbles: u32) {
41 | let start = circle.max_marble_value() + 1; // 1 for fresh game
42 | let end = start + marbles;
43 | for (player_id, value) in (0..players.len()).cycle().zip(start..end) {
44 | circle.turn(&mut players[player_id], value);
45 | }
46 | }
47 |
48 | #[derive(Clone, Debug, Default)]
49 | struct Player {
50 | points: u32,
51 | }
52 |
53 | /// Circle is a cyclic linked list of marbles. Instead of using real pointers
54 | /// like a traditional linked list, we simply assign an incrementing ID to
55 | /// each marble.
56 | ///
57 | /// In this implementation, removing a marble doesn't reclaim its space, but
58 | /// that could be implemented fairly easily with a free list of identifiers.
59 | ///
60 | /// A traditional linked list would have been easy enough to implement with
61 | /// Rc> for `next` and `Weak>` for prev, but
62 | /// the circle actually causes a cycle among the `next` pointers, unlike a
63 | /// normal linked list. We could handle it as a special case, but in the end,
64 | /// this code is simpler.
65 | struct Circle {
66 | marbles: Vec,
67 | current: MarbleID,
68 | }
69 |
70 | type MarbleID = usize;
71 | type MarbleValue = u32;
72 |
73 | #[derive(Debug)]
74 | struct Marble {
75 | value: MarbleValue,
76 | prev: MarbleID,
77 | next: MarbleID,
78 | }
79 |
80 | impl Circle {
81 | fn new() -> Circle {
82 | let first = Marble { value: 0, prev: 0, next: 0 };
83 | Circle { marbles: vec![first], current: 0 }
84 | }
85 |
86 | fn turn(&mut self, player: &mut Player, value: MarbleValue) {
87 | let marble_id = self.add_marble(value);
88 | if value % 23 != 0 {
89 | let insert_at = self.clockwise(1);
90 | self.insert_after(marble_id, insert_at);
91 | self.current = marble_id;
92 | return;
93 | }
94 |
95 | player.points += value;
96 | let remove_id = self.counter_clockwise(7);
97 | player.points += self.marbles[remove_id].value;
98 | self.remove(remove_id);
99 | self.current = self.counter_clockwise(6);
100 | }
101 |
102 | fn max_marble_value(&self) -> MarbleValue {
103 | (self.marbles.len() - 1) as MarbleValue
104 | }
105 |
106 | fn add_marble(&mut self, value: MarbleValue) -> MarbleID {
107 | let id = self.marbles.len();
108 | self.marbles.push(Marble::unlinked(value));
109 | id
110 | }
111 |
112 | fn insert_after(&mut self, to_insert: MarbleID, after: MarbleID) {
113 | let old_next = self.marbles[after].next;
114 | self.marbles[after].next = to_insert;
115 | self.marbles[old_next].prev = to_insert;
116 | self.marbles[to_insert].prev = after;
117 | self.marbles[to_insert].next = old_next;
118 | }
119 |
120 | fn remove(&mut self, id: MarbleID) {
121 | let (prev, next) = (self.marbles[id].prev, self.marbles[id].next);
122 | self.marbles[prev].next = next;
123 | self.marbles[next].prev = prev;
124 | }
125 |
126 | fn clockwise(&mut self, mut i: usize) -> MarbleID {
127 | let mut id = self.current;
128 | while i > 0 {
129 | id = self.marbles[id].next;
130 | i -= 1;
131 | }
132 | id
133 | }
134 |
135 | fn counter_clockwise(&mut self, mut i: usize) -> MarbleID {
136 | let mut id = self.current;
137 | while i > 0 {
138 | id = self.marbles[id].prev;
139 | i -= 1;
140 | }
141 | id
142 | }
143 | }
144 |
145 | impl Marble {
146 | fn unlinked(value: MarbleValue) -> Marble {
147 | Marble { value, prev: 0, next: 0 }
148 | }
149 | }
150 |
151 | impl fmt::Debug for Circle {
152 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
153 | let mut id = self.current;
154 | loop {
155 | let m = &self.marbles[id];
156 | write!(f, "{} ", m.value)?;
157 | id = m.next;
158 | if id == self.current {
159 | break;
160 | }
161 | }
162 | Ok(())
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/aoc10/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc10"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.44"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.1"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
90 | "checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc10/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc10"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1"
9 | regex = "1"
10 |
--------------------------------------------------------------------------------
/aoc10/input/test.txt:
--------------------------------------------------------------------------------
1 | position=< 9, 1> velocity=< 0, 2>
2 | position=< 7, 0> velocity=<-1, 0>
3 | position=< 3, -2> velocity=<-1, 1>
4 | position=< 6, 10> velocity=<-2, -1>
5 | position=< 2, -4> velocity=< 2, 2>
6 | position=<-6, 10> velocity=< 2, -2>
7 | position=< 1, 8> velocity=< 1, -1>
8 | position=< 1, 7> velocity=< 1, 0>
9 | position=<-3, 11> velocity=< 1, -2>
10 | position=< 7, 6> velocity=<-1, -1>
11 | position=<-2, 3> velocity=< 1, 0>
12 | position=<-4, 3> velocity=< 2, 0>
13 | position=<10, -3> velocity=<-1, 1>
14 | position=< 5, 11> velocity=< 1, -2>
15 | position=< 4, 7> velocity=< 0, -1>
16 | position=< 8, -2> velocity=< 0, 1>
17 | position=<15, 0> velocity=<-2, 0>
18 | position=< 1, 6> velocity=< 1, 0>
19 | position=< 8, 9> velocity=< 0, -1>
20 | position=< 3, 3> velocity=<-1, 1>
21 | position=< 0, 5> velocity=< 0, -1>
22 | position=<-2, 2> velocity=< 2, 0>
23 | position=< 5, -2> velocity=< 1, 2>
24 | position=< 1, 4> velocity=< 2, 1>
25 | position=<-2, 7> velocity=< 2, -2>
26 | position=< 3, 6> velocity=<-1, -1>
27 | position=< 5, 0> velocity=< 1, 0>
28 | position=<-6, 0> velocity=< 2, 0>
29 | position=< 5, 9> velocity=< 1, -2>
30 | position=<14, 7> velocity=<-2, 0>
31 | position=<-3, 6> velocity=< 2, -1>
32 |
--------------------------------------------------------------------------------
/aoc10/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::cmp;
2 | use std::error::Error;
3 | use std::io::{self, Read, Write};
4 | use std::result;
5 | use std::str::{self, FromStr};
6 |
7 | use lazy_static::lazy_static;
8 | use regex::Regex;
9 |
10 | macro_rules! err {
11 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
12 | }
13 |
14 | type Result = result::Result>;
15 |
16 | fn main() -> Result<()> {
17 | let mut input = String::new();
18 | io::stdin().read_to_string(&mut input)?;
19 |
20 | let mut points: Vec = vec![];
21 | for line in input.lines() {
22 | let point = line.parse().or_else(|err| {
23 | err!("failed to parse '{:?}': {}", line, err)
24 | })?;
25 | points.push(point);
26 | }
27 | let mut points = Points::new(points)?;
28 |
29 | for _ in 0..1_000_000 {
30 | points.step();
31 | let (w, h) = points.dimensions();
32 | if w <= 80 && h <= 80 {
33 | writeln!(io::stdout(), "seconds: {}", points.seconds)?;
34 | writeln!(io::stdout(), "{}", points.grid_string().trim())?;
35 | writeln!(io::stdout(), "{}", "~".repeat(79))?;
36 | }
37 | }
38 | writeln!(io::stdout(), "message should be in one of the above grids")?;
39 | Ok(())
40 | }
41 |
42 | #[derive(Clone, Debug)]
43 | struct Points {
44 | points: Vec,
45 | seconds: u32,
46 | }
47 |
48 | impl Points {
49 | fn new(points: Vec) -> Result {
50 | if points.is_empty() {
51 | err!("no points given")
52 | } else {
53 | Ok(Points { points, seconds: 0 })
54 | }
55 | }
56 |
57 | fn step(&mut self) {
58 | for p in &mut self.points {
59 | p.x += p.vx;
60 | p.y += p.vy;
61 | }
62 | self.seconds += 1;
63 | }
64 |
65 | fn bounds(&self) -> Bounds {
66 | let mut b = Bounds {
67 | minx: self.points[0].x,
68 | maxx: self.points[0].x,
69 | miny: self.points[0].y,
70 | maxy: self.points[0].y,
71 | };
72 | for p in &self.points {
73 | b.minx = cmp::min(b.minx, p.x);
74 | b.maxx = cmp::max(b.maxx, p.x);
75 | b.miny = cmp::min(b.miny, p.y);
76 | b.maxy = cmp::max(b.maxy, p.y);
77 | }
78 | b
79 | }
80 |
81 | fn dimensions(&self) -> (usize, usize) {
82 | let b = self.bounds();
83 | (b.width(), b.height())
84 | }
85 |
86 | fn grid_string(&self) -> String {
87 | let bounds = self.bounds();
88 | let mut grid = vec![vec![b'.'; bounds.width()]; bounds.height()];
89 | for p in &self.points {
90 | let x = bounds.normal_x(p.x);
91 | let y = bounds.normal_y(p.y);
92 | grid[y as usize][x as usize] = b'#';
93 | }
94 |
95 | let mut buf = String::new();
96 | for row in grid {
97 | buf.push_str(str::from_utf8(&row).unwrap());
98 | buf.push('\n');
99 | }
100 | buf
101 | }
102 | }
103 |
104 | #[derive(Clone, Copy, Debug, Default)]
105 | struct Bounds {
106 | minx: i32,
107 | maxx: i32,
108 | miny: i32,
109 | maxy: i32,
110 | }
111 |
112 | impl Bounds {
113 | fn normal_x(&self, x: i32) -> u32 {
114 | if self.minx >= 0 {
115 | (x - self.minx) as u32
116 | } else {
117 | (x + self.minx.abs()) as u32
118 | }
119 | }
120 |
121 | fn normal_y(&self, y: i32) -> u32 {
122 | if self.miny >= 0 {
123 | (y - self.miny) as u32
124 | } else {
125 | (y + self.miny.abs()) as u32
126 | }
127 | }
128 |
129 | fn width(&self) -> usize {
130 | (self.maxx - self.minx + 1) as usize
131 | }
132 |
133 | fn height(&self) -> usize {
134 | (self.maxy - self.miny + 1) as usize
135 | }
136 | }
137 |
138 | #[derive(Clone, Debug)]
139 | struct Point {
140 | x: i32,
141 | y: i32,
142 | vx: i32,
143 | vy: i32,
144 | }
145 |
146 | impl FromStr for Point {
147 | type Err = Box;
148 |
149 | fn from_str(s: &str) -> Result {
150 | lazy_static! {
151 | static ref RE: Regex = Regex::new(r"(?x)
152 | position=<\s*(?P[-0-9]+),\s*(?P[-0-9]+)>
153 | \s+
154 | velocity=<\s*(?P[-0-9]+),\s*(?P[-0-9]+)>
155 | ").unwrap();
156 | }
157 |
158 | let caps = match RE.captures(s) {
159 | None => return err!("unrecognized position/velocity"),
160 | Some(caps) => caps,
161 | };
162 | Ok(Point {
163 | x: caps["x"].parse()?,
164 | y: caps["y"].parse()?,
165 | vx: caps["vx"].parse()?,
166 | vy: caps["vy"].parse()?,
167 | })
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/aoc11/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc11"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc11/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc11"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc11/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 | use std::io::{self, Write};
3 | use std::result;
4 |
5 | type Result = result::Result>;
6 |
7 | const SERIAL_NUMBER: i32 = 2866;
8 | const GRID_SIZE: i32 = 300;
9 |
10 | fn main() -> Result<()> {
11 | assert!(GRID_SIZE > 0);
12 |
13 | let mut grid = Grid::new(GRID_SIZE);
14 | for x in 1..=GRID_SIZE {
15 | for y in 1..=GRID_SIZE {
16 | grid.set(x, y, fuel_cell_power(x, y));
17 | }
18 | }
19 |
20 | part1(&grid)?;
21 | part2(&grid)?;
22 | Ok(())
23 | }
24 |
25 | fn part1(grid: &Grid) -> Result<()> {
26 | let (mut max_x, mut max_y, mut max_power) =
27 | (1, 1, grid.square_power(3, 1, 1));
28 | for x in 1..=GRID_SIZE {
29 | for y in 1..=GRID_SIZE {
30 | let power = grid.square_power(3, x, y);
31 | if power > max_power {
32 | max_x = x;
33 | max_y = y;
34 | max_power = power;
35 | }
36 | }
37 | }
38 | writeln!(io::stdout(), "most powerful 3x3 square: {},{}", max_x, max_y)?;
39 | Ok(())
40 | }
41 |
42 | fn part2(grid: &Grid) -> Result<()> {
43 | let (mut max_size, mut max_x, mut max_y, mut max_power) =
44 | (1, 1, 1, grid.square_power(1, 1, 1));
45 |
46 | // This smells like a problem that can reuse results to make it faster,
47 | // but didn't have time to think through that. This brute force approach
48 | // is slow, but simple. In particular, we are bailed out by the fact that
49 | // we do not check squares that contain a cell outside of the grid. In
50 | // practice, this makes checking squares that are large with respect to
51 | // the full grid very fast.
52 | for size in 1..=GRID_SIZE {
53 | for x in 1..=GRID_SIZE {
54 | for y in 1..=GRID_SIZE {
55 | let power = grid.square_power(size, x, y);
56 | if power > max_power {
57 | max_size = size;
58 | max_x = x;
59 | max_y = y;
60 | max_power = power;
61 | }
62 | if y + size > GRID_SIZE {
63 | break;
64 | }
65 | }
66 | if x + size > GRID_SIZE {
67 | break;
68 | }
69 | }
70 | }
71 | writeln!(
72 | io::stdout(),
73 | "most powerful square: {},{},{}",
74 | max_x, max_y, max_size,
75 | )?;
76 | Ok(())
77 | }
78 |
79 | struct Grid {
80 | power: Vec>,
81 | }
82 |
83 | impl Grid {
84 | fn new(size: i32) -> Grid {
85 | Grid { power: vec![vec![0; size as usize]; size as usize] }
86 | }
87 |
88 | fn set(&mut self, x: i32, y: i32, power: i32) {
89 | self.power[x as usize - 1][y as usize - 1] = power;
90 | }
91 |
92 | fn get(&self, x: i32, y: i32) -> Option {
93 | let (x, y) = (x - 1, y - 1);
94 | if 0 <= x && x < GRID_SIZE && 0 <= y && y < GRID_SIZE {
95 | Some(self.power[x as usize][y as usize])
96 | } else {
97 | None
98 | }
99 | }
100 |
101 | fn square_power(
102 | &self,
103 | size: i32,
104 | top_left_x: i32,
105 | top_left_y: i32,
106 | ) -> i32 {
107 | let mut power = 0;
108 | for x in top_left_x..top_left_x + size {
109 | for y in top_left_y..top_left_y + size {
110 | power += self.get(x, y).unwrap_or(0);
111 | }
112 | }
113 | power
114 | }
115 | }
116 |
117 | fn fuel_cell_power(x: i32, y: i32) -> i32 {
118 | let rack_id = x + 10;
119 | let mut power = rack_id * y;
120 | power += SERIAL_NUMBER;
121 | power *= rack_id;
122 | power = (power / 100) % 10;
123 | power -= 5;
124 | power
125 | }
126 |
--------------------------------------------------------------------------------
/aoc12/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc12"
11 | version = "0.1.0"
12 | dependencies = [
13 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
16 | ]
17 |
18 | [[package]]
19 | name = "cfg-if"
20 | version = "0.1.6"
21 | source = "registry+https://github.com/rust-lang/crates.io-index"
22 |
23 | [[package]]
24 | name = "fnv"
25 | version = "1.0.6"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 |
28 | [[package]]
29 | name = "lazy_static"
30 | version = "1.2.0"
31 | source = "registry+https://github.com/rust-lang/crates.io-index"
32 |
33 | [[package]]
34 | name = "libc"
35 | version = "0.2.45"
36 | source = "registry+https://github.com/rust-lang/crates.io-index"
37 |
38 | [[package]]
39 | name = "memchr"
40 | version = "2.1.2"
41 | source = "registry+https://github.com/rust-lang/crates.io-index"
42 | dependencies = [
43 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
44 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
45 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
46 | ]
47 |
48 | [[package]]
49 | name = "regex"
50 | version = "1.1.0"
51 | source = "registry+https://github.com/rust-lang/crates.io-index"
52 | dependencies = [
53 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
54 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
55 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
56 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
57 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
58 | ]
59 |
60 | [[package]]
61 | name = "regex-syntax"
62 | version = "0.6.4"
63 | source = "registry+https://github.com/rust-lang/crates.io-index"
64 | dependencies = [
65 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
66 | ]
67 |
68 | [[package]]
69 | name = "thread_local"
70 | version = "0.3.6"
71 | source = "registry+https://github.com/rust-lang/crates.io-index"
72 | dependencies = [
73 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
74 | ]
75 |
76 | [[package]]
77 | name = "ucd-util"
78 | version = "0.1.3"
79 | source = "registry+https://github.com/rust-lang/crates.io-index"
80 |
81 | [[package]]
82 | name = "utf8-ranges"
83 | version = "1.0.2"
84 | source = "registry+https://github.com/rust-lang/crates.io-index"
85 |
86 | [[package]]
87 | name = "version_check"
88 | version = "0.1.5"
89 | source = "registry+https://github.com/rust-lang/crates.io-index"
90 |
91 | [metadata]
92 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
93 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
94 | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
95 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
96 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
97 | "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
98 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
99 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
100 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
101 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
102 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
103 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
104 |
--------------------------------------------------------------------------------
/aoc12/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc12"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | fnv = "1"
9 | lazy_static = "1"
10 | regex = "1"
11 |
--------------------------------------------------------------------------------
/aoc12/input/input.txt:
--------------------------------------------------------------------------------
1 | initial state: #........#.#.#...###..###..###.#..#....###.###.#.#...####..##..##.#####..##...#.#.....#...###.#.####
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 |
--------------------------------------------------------------------------------
/aoc12/input/test.txt:
--------------------------------------------------------------------------------
1 | initial state: #..#.#..##......###...###
2 |
3 | ...## => #
4 | ..#.. => #
5 | .#... => #
6 | .#.#. => #
7 | .#.## => #
8 | .##.. => #
9 | .#### => #
10 | #.#.# => #
11 | #.### => #
12 | ##.#. => #
13 | ##.## => #
14 | ###.. => #
15 | ###.# => #
16 | ####. => #
17 |
--------------------------------------------------------------------------------
/aoc12/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::cmp;
2 | use std::error::Error;
3 | use std::fmt;
4 | use std::io::{self, Read, Write};
5 | use std::result;
6 | use std::str::FromStr;
7 |
8 | use fnv::FnvHashMap as HashMap;
9 | use lazy_static::lazy_static;
10 | use regex::Regex;
11 |
12 | macro_rules! err {
13 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
14 | }
15 |
16 | type Result = result::Result>;
17 |
18 | fn main() -> Result<()> {
19 | let mut input = String::new();
20 | io::stdin().read_to_string(&mut input)?;
21 |
22 | let pots: Pots = input.parse()?;
23 | run(pots.clone(), 20)?;
24 | run(pots.clone(), 500)?;
25 | run(pots.clone(), 5_000)?;
26 | run(pots.clone(), 50_000)?;
27 | // After running the above, there is an obvious pattern. The result is
28 | // always 4x..x866 where x=0 and is repeated N-1 times where N is the
29 | // number of zeros in the generation count. 50_000_000_000 has 10 zeros,
30 | // which means our answer is 4000000000866.
31 | //
32 | // Given the implementation here, it would take about a month for it to run
33 | // over 50 billion generations. There's likely a more clever solution
34 | // that detects convergence of the pot states?
35 | Ok(())
36 | }
37 |
38 | fn run(mut pots: Pots, generations: usize) -> Result<()> {
39 | for i in 0..generations {
40 | pots = pots.step();
41 | if i % 100_000 == 0 {
42 | println!("gen: {}, min: {}, max: {}, size: {}",
43 | i, pots.min, pots.max, pots.pots.len());
44 | }
45 | }
46 | writeln!(
47 | io::stdout(),
48 | "sum of pots with plants after {} generations: {}",
49 | generations, pots.sum_plant(),
50 | )?;
51 | Ok(())
52 | }
53 |
54 | #[derive(Clone)]
55 | pub struct Pots {
56 | pots: HashMap,
57 | transitions: Vec,
58 | min: i32,
59 | max: i32,
60 | }
61 |
62 | impl Pots {
63 | fn sum_plant(&self) -> i32 {
64 | self.pots
65 | .iter()
66 | .filter(|&(_, pot)| pot.has_plants())
67 | .map(|(&i, _)| i)
68 | .sum()
69 | }
70 |
71 | fn step(&self) -> Pots {
72 | let mut new = self.fresh();
73 | for &i in self.pots.keys() {
74 | for j in i-2..=i+2 {
75 | new.set_pot(j, self.next_state(&self.current_state(j)));
76 | }
77 | }
78 | new
79 | }
80 |
81 | fn fresh(&self) -> Pots {
82 | Pots {
83 | pots: HashMap::default(),
84 | transitions: self.transitions.clone(),
85 | min: self.min,
86 | max: self.max,
87 | }
88 | }
89 |
90 | fn next_state(&self, current: &[Pot]) -> Pot {
91 | for t in &self.transitions {
92 | if t.is_match(current) {
93 | return t.to;
94 | }
95 | }
96 | Pot::Empty
97 | }
98 |
99 | fn current_state(&self, at: i32) -> Vec {
100 | let mut state = vec![];
101 | for i in at-2..=at+2 {
102 | state.push(self.pot(i));
103 | }
104 | state
105 | }
106 |
107 | fn pot(&self, i: i32) -> Pot {
108 | self.pots.get(&i).map(|&pot| pot).unwrap_or(Pot::Empty)
109 | }
110 |
111 | fn set_pot(&mut self, i: i32, pot: Pot) {
112 | if pot.has_plants() {
113 | self.min = cmp::min(self.min, i - 2);
114 | self.max = cmp::max(self.max, i + 2);
115 | self.pots.insert(i, pot);
116 | }
117 | }
118 | }
119 |
120 | impl FromStr for Pots {
121 | type Err = Box;
122 |
123 | fn from_str(s: &str) -> Result {
124 | let mut lines = s.lines();
125 | let first = match lines.next() {
126 | None => return err!("empty input for pots"),
127 | Some(first) => first,
128 | };
129 |
130 | let prefix = "initial state: ";
131 | if !first.starts_with(prefix) {
132 | return err!("unexpected prefix for first line: {:?}", first);
133 | }
134 | let pots: HashMap = first[prefix.len()..]
135 | .char_indices()
136 | .map(|(i, _)| s[prefix.len() + i..].parse())
137 | .collect::>>()?
138 | .into_iter()
139 | .enumerate()
140 | .map(|(i, pot)| (i as i32, pot))
141 | .collect();
142 |
143 | match lines.next() {
144 | None => return err!("missing empty line separating transitions"),
145 | Some(second) => {
146 | if !second.is_empty() {
147 | return err!("second line is not empty: {:?}", second);
148 | }
149 | }
150 | }
151 |
152 | let transitions = lines
153 | .map(|line| line.parse())
154 | .collect::>>()?
155 | // Drop transitions to empty pots.
156 | .into_iter()
157 | .filter(|t| t.to.has_plants())
158 | .collect::>();
159 |
160 | let (min, max) = (-2, pots.len() as i32 + 2);
161 | Ok(Pots { pots, transitions, min, max })
162 | }
163 | }
164 |
165 | impl fmt::Debug for Pots {
166 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167 | for i in self.min..=self.max {
168 | if self.pot(i).has_plants() {
169 | write!(f, "#")?;
170 | } else {
171 | write!(f, ".")?;
172 | }
173 | }
174 | Ok(())
175 | }
176 | }
177 |
178 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
179 | enum Pot {
180 | Plants,
181 | Empty,
182 | }
183 |
184 | impl Pot {
185 | fn has_plants(&self) -> bool {
186 | *self == Pot::Plants
187 | }
188 | }
189 |
190 | impl FromStr for Pot {
191 | type Err = Box;
192 |
193 | fn from_str(s: &str) -> Result {
194 | if s.is_empty() {
195 | err!("no pot in empty string")
196 | } else if &s[0..1] == "#" {
197 | Ok(Pot::Plants)
198 | } else if &s[0..1] == "." {
199 | Ok(Pot::Empty)
200 | } else {
201 | err!("unrecognized pot state: {:?}", s)
202 | }
203 | }
204 | }
205 |
206 | #[derive(Clone, Debug)]
207 | struct Transition {
208 | from: Vec,
209 | to: Pot,
210 | }
211 |
212 | impl Transition {
213 | fn is_match(&self, state: &[Pot]) -> bool {
214 | self.from == state
215 | }
216 | }
217 |
218 | impl FromStr for Transition {
219 | type Err = Box;
220 |
221 | fn from_str(s: &str) -> Result {
222 | lazy_static! {
223 | static ref RE: Regex = Regex::new(
224 | r"^(?P[#.]{5}) => (?P[#.])$",
225 | ).unwrap();
226 | }
227 |
228 | let caps = match RE.captures(s) {
229 | None => return err!("unrecognized transition"),
230 | Some(caps) => caps,
231 | };
232 | let from = caps["from"]
233 | .char_indices()
234 | .map(|(i, _)| s[i..].parse())
235 | .collect::>>()?;
236 | Ok(Transition {
237 | from: from,
238 | to: caps["to"].parse()?,
239 | })
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/aoc13/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc13"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc13/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc13"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
--------------------------------------------------------------------------------
/aoc13/input/test.txt:
--------------------------------------------------------------------------------
1 | /->-\
2 | | | /----\
3 | | /-+--+-\ |
4 | | | | | v |
5 | \-+-/ \-+--/
6 | \------/
7 |
--------------------------------------------------------------------------------
/aoc13/input/test2.txt:
--------------------------------------------------------------------------------
1 | />-<\
2 | | |
3 | | /<+-\
4 | | | | v
5 | \>+ |
6 | | ^
7 | \<->/
8 |
--------------------------------------------------------------------------------
/aoc14/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc14"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc14/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc14"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc14/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 | use std::io::{self, Write};
3 | use std::result;
4 |
5 | type Result = result::Result>;
6 |
7 | fn main() -> Result<()> {
8 | part1(110201)?;
9 | part2(&[1, 1, 0, 2, 0, 1])?;
10 | Ok(())
11 | }
12 |
13 | fn part1(recipe_count: usize) -> Result<()> {
14 | let mut recipes = Recipes::new();
15 | while recipes.scores.len() < recipe_count + 10 {
16 | recipes.step();
17 | }
18 |
19 | let scores = recipes.scores[recipe_count..recipe_count+10]
20 | .iter()
21 | .map(|s| s.to_string())
22 | .collect::>()
23 | .concat();
24 | writeln!(io::stdout(), "scores of next ten recipes: {}", scores)?;
25 | Ok(())
26 | }
27 |
28 | fn part2(digits: &[u32]) -> Result<()> {
29 | let mut recipes = Recipes::new();
30 | let ends_at;
31 | loop {
32 | if recipes.scores.ends_with(&digits) {
33 | ends_at = recipes.scores.len() - digits.len();
34 | break;
35 | } else if recipes.scores[..recipes.scores.len()-1].ends_with(&digits) {
36 | ends_at = recipes.scores.len() - digits.len() - 1;
37 | break;
38 | }
39 | recipes.step();
40 | }
41 |
42 | writeln!(io::stdout(), "recipes to the left: {}", ends_at)?;
43 | Ok(())
44 | }
45 |
46 | #[derive(Clone, Debug)]
47 | struct Recipes {
48 | elves: Vec,
49 | scores: Vec,
50 | }
51 |
52 | impl Recipes {
53 | fn new() -> Recipes {
54 | Recipes { scores: vec![3, 7], elves: vec![0, 1] }
55 | }
56 |
57 | fn step(&mut self) {
58 | let new_recipe: u32 = self.elves
59 | .iter()
60 | .map(|&e| self.scores[e])
61 | .sum();
62 | for &digit in new_recipe.to_string().as_bytes() {
63 | let digit_value = digit - b'0';
64 | self.scores.push(digit_value as u32);
65 | }
66 | for e in &mut self.elves {
67 | *e = (*e + self.scores[*e] as usize + 1) % self.scores.len();
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/aoc15/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc15"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc15/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc15"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc15/input/input.txt:
--------------------------------------------------------------------------------
1 | ################################
2 | #######################.########
3 | ######################....######
4 | #######################.....####
5 | ##################..##......####
6 | ###################.##.....#####
7 | ###################.....G..#####
8 | ##################.....G...#####
9 | ############.....GG.G...#..#####
10 | ##############...##....##.######
11 | ############...#..G............#
12 | ###########......E.............#
13 | ###########...#####..E........##
14 | #...#######..#######.......#####
15 | #..#..G....G#########.........##
16 | #..#....G...#########..#....####
17 | ##.....G....#########.E......###
18 | #####G.....G#########..E.....###
19 | #####.......#########....#.....#
20 | #####G#G....G#######.......#..E#
21 | ###.....G.....#####....#.#######
22 | ###......G.....G.G.......#######
23 | ###..................#..########
24 | #####...................########
25 | #####..............#...#########
26 | ####......G........#.E.#E..#####
27 | ####.###.........E...#E...######
28 | ####..##........#...##.....#####
29 | ########.#......######.....#####
30 | ########...E....#######....#####
31 | #########...##..########...#####
32 | ################################
33 |
--------------------------------------------------------------------------------
/aoc15/input/test-movement.txt:
--------------------------------------------------------------------------------
1 | #########
2 | #G..G..G#
3 | #.......#
4 | #.......#
5 | #G..E..G#
6 | #.......#
7 | #.......#
8 | #G..G..G#
9 | #########
10 |
--------------------------------------------------------------------------------
/aoc15/input/test1.txt:
--------------------------------------------------------------------------------
1 | #######
2 | #.G...#
3 | #...EG#
4 | #.#.#G#
5 | #..G#E#
6 | #.....#
7 | #######
8 |
--------------------------------------------------------------------------------
/aoc15/input/test2.txt:
--------------------------------------------------------------------------------
1 | #######
2 | #G..#E#
3 | #E#E.E#
4 | #G.##.#
5 | #...#E#
6 | #...E.#
7 | #######
8 |
--------------------------------------------------------------------------------
/aoc15/input/test3.txt:
--------------------------------------------------------------------------------
1 | #######
2 | #E..EG#
3 | #.#G.E#
4 | #E.##E#
5 | #G..#.#
6 | #..E#.#
7 | #######
8 |
--------------------------------------------------------------------------------
/aoc15/input/test4.txt:
--------------------------------------------------------------------------------
1 | #######
2 | #E.G#.#
3 | #.#G..#
4 | #G.#.G#
5 | #G..#.#
6 | #...E.#
7 | #######
8 |
--------------------------------------------------------------------------------
/aoc15/input/test5.txt:
--------------------------------------------------------------------------------
1 | #######
2 | #.E...#
3 | #.#..G#
4 | #.###.#
5 | #E#G#G#
6 | #...#G#
7 | #######
8 |
--------------------------------------------------------------------------------
/aoc15/input/test6.txt:
--------------------------------------------------------------------------------
1 | #########
2 | #G......#
3 | #.E.#...#
4 | #..##..G#
5 | #...##..#
6 | #...#...#
7 | #.G...G.#
8 | #.....G.#
9 | #########
10 |
--------------------------------------------------------------------------------
/aoc16/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc16"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc16/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc16"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
--------------------------------------------------------------------------------
/aoc16/input/test-samples.txt:
--------------------------------------------------------------------------------
1 | Before: [3, 2, 1, 1]
2 | 9 2 1 2
3 | After: [3, 2, 2, 1]
4 |
--------------------------------------------------------------------------------
/aoc17/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc17"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.45"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.2"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
90 | "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc17/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc17"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1"
9 | regex = "1"
10 |
--------------------------------------------------------------------------------
/aoc17/input/test.txt:
--------------------------------------------------------------------------------
1 | x=495, y=2..7
2 | y=7, x=495..501
3 | x=501, y=3..7
4 | x=498, y=2..4
5 | x=506, y=1..2
6 | x=498, y=10..13
7 | x=504, y=10..13
8 | y=13, x=498..504
9 |
--------------------------------------------------------------------------------
/aoc17/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::cmp;
2 | use std::collections::{HashMap, HashSet};
3 | use std::error::Error;
4 | use std::fmt;
5 | use std::io::{self, Read, Write};
6 | use std::ops::RangeInclusive;
7 | use std::result;
8 | use std::str::{self, FromStr};
9 |
10 | use lazy_static::lazy_static;
11 | use regex::Regex;
12 |
13 | macro_rules! err {
14 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
15 | }
16 |
17 | type Result = result::Result>;
18 |
19 | fn main() -> Result<()> {
20 | let mut input = String::new();
21 | io::stdin().read_to_string(&mut input)?;
22 |
23 | let mut scans: Vec = vec![];
24 | for line in input.lines() {
25 | let scan = line.parse().or_else(|err| {
26 | err!("failed to parse '{:?}': {}", line, err)
27 | })?;
28 | scans.push(scan);
29 | }
30 |
31 | let mut ground = Ground::new();
32 | ground.add_clay_scans(&scans);
33 | while ground.add_water() {}
34 |
35 | writeln!(io::stdout(), "reachable tiles: {}", ground.water_in_bounds())?;
36 | writeln!(io::stdout(), "remaining water: {}", ground.water_at_rest())?;
37 | Ok(())
38 | }
39 |
40 | #[derive(Clone, Debug)]
41 | struct Ground {
42 | spring: Coordinate,
43 | clay: HashSet,
44 | water: HashMap,
45 | // When determing the next downward coordinate, we use this set to avoid
46 | // searching for a spot among settled water. If we know that the entire
47 | // next row is settled, then just move on---there is no place for the water
48 | // to go. This substantially speeds up this particular implementation
49 | // which is otherwise quite slow!
50 | settled: HashSet,
51 | min: Coordinate,
52 | max: Coordinate,
53 | }
54 |
55 | impl Ground {
56 | fn new() -> Ground {
57 | Ground {
58 | spring: Coordinate { x: 500, y: 0 },
59 | clay: HashSet::new(),
60 | water: HashMap::new(),
61 | settled: HashSet::new(),
62 | min: Coordinate { x: 0, y: 0 },
63 | max: Coordinate { x: 0, y: 0 },
64 | }
65 | }
66 |
67 | fn water_in_bounds(&self) -> usize {
68 | self.water
69 | .keys()
70 | .filter(|&&c| self.min.y <= c.y && c.y <= self.max.y)
71 | .count()
72 | }
73 |
74 | fn water_at_rest(&self) -> usize {
75 | self.water.values().filter(|&&w| w == Water::Rest).count()
76 | }
77 |
78 | fn add_water(&mut self) -> bool {
79 | let mut rested = false;
80 | let mut stack = vec![self.spring];
81 | let mut seen = HashSet::new();
82 | while let Some(c) = stack.pop() {
83 | if seen.contains(&c) {
84 | continue;
85 | }
86 | seen.insert(c);
87 |
88 | if let Some(down) = self.down(c) {
89 | if down.y <= self.max.y {
90 | stack.push(down);
91 | self.water.insert(down, Water::Flow);
92 | }
93 | continue;
94 | }
95 |
96 | let mut blocked = true;
97 | let mut c2 = c;
98 | while let Some(left) = self.left(c2) {
99 | c2 = left;
100 | self.water.insert(c2, Water::Flow);
101 | if self.down(c2).is_some() {
102 | stack.push(c2);
103 | blocked = false;
104 | break;
105 | }
106 | }
107 | c2 = c;
108 | while let Some(right) = self.right(c2) {
109 | c2 = right;
110 | self.water.insert(c2, Water::Flow);
111 | if self.down(c2).is_some() {
112 | stack.push(c2);
113 | blocked = false;
114 | break;
115 | }
116 | }
117 | if blocked {
118 | self.water.insert(c, Water::Rest);
119 | rested = true;
120 | }
121 | }
122 | rested
123 | }
124 |
125 | fn down(&mut self, c: Coordinate) -> Option {
126 | let down = Coordinate { x: c.x, y: c.y + 1 };
127 | if self.is_clay(down) {
128 | return None;
129 | }
130 | if !self.is_settled(down) {
131 | return Some(down);
132 | }
133 |
134 | let mut left = Coordinate { x: down.x - 1, y: down.y };
135 | if !self.settled.contains(&left) {
136 | let start = left;
137 | while !self.is_clay(left) {
138 | if !self.is_settled(left) {
139 | return Some(left);
140 | }
141 | left.x -= 1;
142 | }
143 | self.settled.insert(start);
144 | }
145 |
146 | let mut right = Coordinate { x: down.x + 1, y: down.y };
147 | if !self.settled.contains(&right) {
148 | let start = right;
149 | while !self.is_clay(right) {
150 | if !self.is_settled(right) {
151 | return Some(right);
152 | }
153 | right.x += 1;
154 | }
155 | self.settled.insert(start);
156 | }
157 |
158 | None
159 | }
160 |
161 | fn left(&self, c: Coordinate) -> Option {
162 | let left = Coordinate { x: c.x - 1, y: c.y };
163 | if self.is_clay(left) || self.is_settled(left) {
164 | None
165 | } else {
166 | Some(left)
167 | }
168 | }
169 |
170 | fn right(&self, c: Coordinate) -> Option {
171 | let right = Coordinate { x: c.x + 1, y: c.y };
172 | if self.is_clay(right) || self.is_settled(right) {
173 | None
174 | } else {
175 | Some(right)
176 | }
177 | }
178 |
179 | fn is_clay(&self, c: Coordinate) -> bool {
180 | self.clay.contains(&c)
181 | }
182 |
183 | fn is_settled(&self, c: Coordinate) -> bool {
184 | self.water.get(&c).map_or(false, |&w| w == Water::Rest)
185 | }
186 |
187 | fn add_clay_scans(&mut self, scans: &[ClayScan]) {
188 | if scans.is_empty() {
189 | return;
190 | }
191 | self.min = Coordinate {
192 | x: *scans[0].x.start(),
193 | y: *scans[0].y.start(),
194 | };
195 | self.max = self.min;
196 | for scan in scans {
197 | for x in scan.x.clone() {
198 | for y in scan.y.clone() {
199 | let c = Coordinate { x, y };
200 | self.clay.insert(c);
201 | self.min.x = cmp::min(self.min.x, c.x);
202 | self.min.y = cmp::min(self.min.y, c.y);
203 | self.max.x = cmp::max(self.max.x, c.x);
204 | self.max.y = cmp::max(self.max.y, c.y);
205 | }
206 | }
207 | }
208 | }
209 | }
210 |
211 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
212 | enum Water {
213 | Flow,
214 | Rest,
215 | }
216 |
217 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
218 | struct Coordinate {
219 | x: i64,
220 | y: i64,
221 | }
222 |
223 | #[derive(Clone, Debug)]
224 | struct ClayScan {
225 | x: RangeInclusive,
226 | y: RangeInclusive,
227 | }
228 |
229 | impl FromStr for ClayScan {
230 | type Err = Box;
231 |
232 | fn from_str(s: &str) -> Result {
233 | lazy_static! {
234 | static ref RE1: Regex = Regex::new(r"(?x)
235 | x=(?P[0-9]+),\sy=(?P[0-9]+)\.\.(?P[0-9]+)
236 | ").unwrap();
237 |
238 | static ref RE2: Regex = Regex::new(r"(?x)
239 | y=(?P[0-9]+),\sx=(?P[0-9]+)\.\.(?P[0-9]+)
240 | ").unwrap();
241 | }
242 |
243 | if let Some(caps) = RE1.captures(s) {
244 | let x = caps["x"].parse()?;
245 | let (y1, y2) = (caps["y1"].parse()?, caps["y2"].parse()?);
246 | return Ok(ClayScan {
247 | x: x..=x,
248 | y: y1..=y2,
249 | });
250 | }
251 | if let Some(caps) = RE2.captures(s) {
252 | let (x1, x2) = (caps["x1"].parse()?, caps["x2"].parse()?);
253 | let y = caps["y"].parse()?;
254 | return Ok(ClayScan {
255 | x: x1..=x2,
256 | y: y..=y,
257 | });
258 | }
259 | err!("unrecognized clay scan: {:?}", s)
260 | }
261 | }
262 |
263 | impl fmt::Display for Ground {
264 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265 | for y in self.spring.y..=self.max.y {
266 | for x in (self.min.x - 1)..=(self.max.x + 1) {
267 | let c = Coordinate { x, y };
268 | if c == self.spring {
269 | write!(f, "+")?;
270 | } else if self.clay.contains(&c) {
271 | write!(f, "#")?;
272 | } else if let Some(&w) = self.water.get(&c) {
273 | write!(f, "{}", w)?;
274 | } else {
275 | write!(f, ".")?;
276 | }
277 | }
278 | write!(f, "\n")?;
279 | }
280 | Ok(())
281 | }
282 | }
283 |
284 | impl fmt::Display for Water {
285 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 | match *self {
287 | Water::Flow => write!(f, "|"),
288 | Water::Rest => write!(f, "~"),
289 | }
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/aoc18/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc18"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc18/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc18"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [profile.release]
8 | debug = true
9 |
--------------------------------------------------------------------------------
/aoc18/input/input.txt:
--------------------------------------------------------------------------------
1 | |#..|#...|..|.#..|###|.....#.|.......||#..|....||.
2 | #||..##.#........||#...##.|..###.|.||...|.#.|.|.#.
3 | ##.#.###....##....|..||#.||##.|.###|........||.##.
4 | #.|.||#...|..####......|.#|#..#.#|##...||..#..|...
5 | .....#..#.|.####..#..#...|||...||.|...#......#..|.
6 | .|..#..#.......|...#.|...|.##....|#|..#|###..#..#.
7 | .##..#..##..|.#|||.##..|..#.##..|....#..#|.##|.|#.
8 | |#..|#...|...|.|.......#.#......|...|.#.|||.|||#.#
9 | |....#...|..#..#.....#.|..#.#..|#|.#|...|..|#..|#|
10 | .#...##..|#.##......##...#|||..|.....#.|..|...|..#
11 | #.....|..|...||.|.|.....|....#|..|#...#|...#.....#
12 | ...|...###.||......|..#|..|...|.##|........#|#|..|
13 | |.|.#.#......||#|||..|#....|#.|...#|..|.|.#|#.|.|.
14 | ###.#.|....|.......##.#|###.|#.#..#.|.#...#...###.
15 | |###...|.....#.|.##..#...|#.#.|.##.#........#..|..
16 | |.||.|...##...##|......#..|.##.##|..#..|#..#.##...
17 | #....|#.....|...|...|............#..#|.....|.#.|.#
18 | ...#..|..|||#.|.......#|...#...##|.......####.|...
19 | .#..|..#..|....||#.##.....|||...#..|.#..|.#..|..##
20 | ....#...##.........#....|..#.......#...|.....##.#.
21 | |...|...|....#|####||###..|.|..|.||.#......#.|#...
22 | .#.#|.|.|....#.....||...||..|...##.#..|.|.#......|
23 | ..|.......|||.|..#.#......|.|..##.||....|###....#.
24 | ##....#.......#.|#.##.........|.|....#...|.#|.|.#|
25 | |#.##...|||||#.##.#...#.|#...|.||.|...|..#...#..|.
26 | ...#||..#.......||..|.###.#.|#......||..|.#.....#.
27 | #..|.||#.#...|..........#.....#...#...###||.#.....
28 | #..#.|###|#|..|##...##.#......#|.#.#|..#.......|#.
29 | .|.....|.|..#.###|.#|.##.....|.|..|..|..#..|...##.
30 | .|........#...#..|.|..||#....|....#..|.|........|#
31 | ....#.|...#|||...#......#...##......|#....#.||.#..
32 | .|.....|....#......#.|#.|.|.|..#.#.|..##.#||.....#
33 | .....#...|.#|..#..#|#.#|.|..|.#........#|..#|....#
34 | |.||..##...|#.#||..|..#.|..|..#..|..#.|.#|.#...|#.
35 | ...|#.###...#..|#..##..||....#.||..#.|.|#.#..|..||
36 | ......|#|.#.#|.|....#..##|##|#...|.#.|.#....##|#..
37 | #..||.....#....#....#.#.....|.....#....|....|...#.
38 | .#....#.##..........|.||.#.....#|#|||.#..#|......|
39 | ..||..|....#..........#.|...#|.|#.|#..|#||.#...|#|
40 | ..#..#.#|......#|.....||.#..##.|.#..#.||...|.|||..
41 | .#....|....#.|#...#..||..||.##..#.||....|.#|....|.
42 | ..#|.|.....#....#..|..||..#..##.|.||..||||#.#..|.|
43 | .|#.|.||........#|.#|#....||..#||#...|..........##
44 | ..#|.|..|||..###..|||.#..#.#||||.#.|##...|#......|
45 | ..|...#|...|.#.#|.#...#.|..||##.#..#.|...#.#.#|#..
46 | #..#..|##.#|......#...|#|##..#.|...#.#.....#..##..
47 | ..#.|..###|.|#.|........|.....|.....#..|.|.#...|.#
48 | ..#|.|#.#.|#..|....|#...|.....|........|.|##.|#||#
49 | #.....##.#..#..#...|#||.#.#.#..|....|||.|.|......#
50 | ...#|#....|.#.#..##.|.....#....|.|||..##.|.#.|.##.
51 |
--------------------------------------------------------------------------------
/aoc18/input/test.txt:
--------------------------------------------------------------------------------
1 | .#.#...|#.
2 | .....#|##|
3 | .|..|...#.
4 | ..|#.....#
5 | #.#|||#|#|
6 | ...#.||...
7 | .|....|...
8 | ||...#|.#|
9 | |.||||..|.
10 | ...#.|..|.
11 |
--------------------------------------------------------------------------------
/aoc18/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 | use std::fmt;
3 | use std::io::{self, Read, Write};
4 | use std::mem;
5 | use std::result;
6 | use std::str::{self, FromStr};
7 |
8 | macro_rules! err {
9 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
10 | }
11 |
12 | type Result = result::Result>;
13 |
14 | fn main() -> Result<()> {
15 | let mut input = String::new();
16 | io::stdin().read_to_string(&mut input)?;
17 |
18 | let minutes = 10;
19 | let mut area: Area = input.parse()?;
20 | for _ in 0..minutes {
21 | area.step();
22 | }
23 | writeln!(
24 | io::stdout(),
25 | "resource value after {} minutes: {}",
26 | minutes,
27 | area.resource_value(),
28 | )?;
29 |
30 | // Doing 1000000000 will take way too long. Instead, print out resource
31 | // values at a lower number. It is easy to notice that it is periodic.
32 | // Specifically, it is periodic over 28 values. Namely,
33 | // 1_000_000_000 % 28 == 20. The period is active, at minimum, after 1000
34 | // minutes. Therefore, 1028 % 28 == 20 implies that the resource value
35 | // after 1028 minutes is the same as the resource value after 1_000_000_000
36 | // minutes.
37 | let minutes = 1028;
38 | let mut area: Area = input.parse()?;
39 | for _ in 0..minutes {
40 | area.step();
41 | }
42 | writeln!(
43 | io::stdout(),
44 | "resource value after {} minutes: {}",
45 | minutes,
46 | area.resource_value(),
47 | )?;
48 |
49 | Ok(())
50 | }
51 |
52 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
53 | struct Coordinate {
54 | x: i64,
55 | y: i64,
56 | }
57 |
58 | #[derive(Clone, Debug)]
59 | struct Area {
60 | acres: Vec>,
61 | acres2: Vec>,
62 | }
63 |
64 | impl Area {
65 | fn resource_value(&self) -> usize {
66 | let (mut wooded, mut lumber) = (0, 0);
67 | for row in &self.acres {
68 | for acre in row {
69 | match acre {
70 | Acre::Open => {}
71 | Acre::Trees => wooded += 1,
72 | Acre::Lumberyard => lumber += 1,
73 | }
74 | }
75 | }
76 | wooded * lumber
77 | }
78 |
79 | // I foolishly tried to optimize the code below before realizing it was
80 | // futile and started looking for a pattern in the output. ---AG
81 |
82 | fn step(&mut self) {
83 | let mut new = mem::replace(&mut self.acres2, vec![]);
84 | for y in 0..self.height() {
85 | for x in 0..self.width() {
86 | self.step_cell(x, y, &mut new);
87 | }
88 | }
89 | self.acres2 = mem::replace(&mut self.acres, vec![]);
90 | self.acres = new;
91 | }
92 |
93 | fn step_cell(
94 | &self,
95 | x: usize,
96 | y: usize,
97 | new: &mut Vec>,
98 | ) {
99 | use self::Acre::*;
100 |
101 | new[y][x] = self.acres[y][x];
102 | match self.acres[y][x] {
103 | Open => {
104 | let count = self.neighbors(
105 | x, y, 0, |count, n| {
106 | if n == Trees { count + 1 } else { count }
107 | },
108 | );
109 | if count >= 3 {
110 | new[y][x] = Trees;
111 | }
112 | }
113 | Trees => {
114 | let count = self.neighbors(
115 | x, y, 0, |count, n| {
116 | if n == Lumberyard { count + 1 } else { count }
117 | },
118 | );
119 | if count >= 3 {
120 | new[y][x] = Lumberyard;
121 | }
122 | }
123 | Lumberyard => {
124 | let (has_lumber, has_trees) = self.neighbors(
125 | x, y, (false, false),
126 | |(lumber, trees), n| {
127 | (lumber || n == Lumberyard, trees || n == Trees)
128 | },
129 | );
130 | if !has_lumber || !has_trees {
131 | new[y][x] = Open;
132 | }
133 | }
134 | }
135 | }
136 |
137 | fn neighbors(
138 | &self,
139 | ox: usize,
140 | oy: usize,
141 | init: T,
142 | mut f: impl FnMut(T, Acre) -> T,
143 | ) -> T {
144 | let mut ret = init;
145 | for y in oy.saturating_sub(1)..=oy.saturating_add(1) {
146 | for x in ox.saturating_sub(1)..=ox.saturating_add(1) {
147 | if x == ox && y == oy {
148 | continue;
149 | }
150 | if x >= self.width() || y >= self.height() {
151 | continue;
152 | }
153 | ret = f(ret, self.acres[y][x]);
154 | }
155 | }
156 | ret
157 | }
158 |
159 | fn width(&self) -> usize {
160 | self.acres[0].len()
161 | }
162 |
163 | fn height(&self) -> usize {
164 | self.acres.len()
165 | }
166 | }
167 |
168 | impl FromStr for Area {
169 | type Err = Box;
170 |
171 | fn from_str(s: &str) -> Result {
172 | if !s.is_ascii() {
173 | return err!("area must be in ASCII");
174 | }
175 |
176 | let ylen = s.lines().count();
177 | if ylen == 0 {
178 | return err!("area cannot be empty");
179 | }
180 |
181 | let xlen = s.lines().next().unwrap().len();
182 | let mut area = Area {
183 | acres: vec![vec![Acre::Open; xlen]; ylen],
184 | acres2: vec![vec![Acre::Open; xlen]; ylen],
185 | };
186 | for (y, line) in s.lines().enumerate() {
187 | if line.len() != xlen {
188 | return err!(
189 | "all rows expected to have length {}, but found {}",
190 | xlen, line.len()
191 | );
192 | }
193 | for x in 0..line.len() {
194 | area.acres[y][x] = line[x..x+1].parse()?;
195 | }
196 | }
197 | Ok(area)
198 | }
199 | }
200 |
201 | #[derive(Clone, Copy, Debug, Eq, PartialEq)]
202 | enum Acre {
203 | Open,
204 | Trees,
205 | Lumberyard,
206 | }
207 |
208 | impl FromStr for Acre {
209 | type Err = Box;
210 |
211 | fn from_str(s: &str) -> Result {
212 | match s.chars().next() {
213 | None => err!("cannot parse acre from empty string"),
214 | Some('.') => Ok(Acre::Open),
215 | Some('|') => Ok(Acre::Trees),
216 | Some('#') => Ok(Acre::Lumberyard),
217 | Some(c) => err!("invalid acre: '{}'", c),
218 | }
219 | }
220 | }
221 |
222 | impl fmt::Display for Area {
223 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224 | for row in &self.acres {
225 | for col in row {
226 | write!(f, "{}", col)?;
227 | }
228 | write!(f, "\n")?;
229 | }
230 | Ok(())
231 | }
232 | }
233 |
234 | impl fmt::Display for Acre {
235 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 | match *self {
237 | Acre::Open => write!(f, "."),
238 | Acre::Trees => write!(f, "|"),
239 | Acre::Lumberyard => write!(f, "#"),
240 | }
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/aoc19/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc19"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.45"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.2"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
90 | "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc19/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc19"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1"
9 | regex = "1"
10 |
--------------------------------------------------------------------------------
/aoc19/input/input.txt:
--------------------------------------------------------------------------------
1 | #ip 5
2 | addi 5 16 5
3 | seti 1 0 4
4 | seti 1 8 1
5 | mulr 4 1 3
6 | eqrr 3 2 3
7 | addr 3 5 5
8 | addi 5 1 5
9 | addr 4 0 0
10 | addi 1 1 1
11 | gtrr 1 2 3
12 | addr 5 3 5
13 | seti 2 4 5
14 | addi 4 1 4
15 | gtrr 4 2 3
16 | addr 3 5 5
17 | seti 1 7 5
18 | mulr 5 5 5
19 | addi 2 2 2
20 | mulr 2 2 2
21 | mulr 5 2 2
22 | muli 2 11 2
23 | addi 3 6 3
24 | mulr 3 5 3
25 | addi 3 9 3
26 | addr 2 3 2
27 | addr 5 0 5
28 | seti 0 5 5
29 | setr 5 9 3
30 | mulr 3 5 3
31 | addr 5 3 3
32 | mulr 5 3 3
33 | muli 3 14 3
34 | mulr 3 5 3
35 | addr 2 3 2
36 | seti 0 1 0
37 | seti 0 0 5
38 |
--------------------------------------------------------------------------------
/aoc19/input/test.txt:
--------------------------------------------------------------------------------
1 | #ip 0
2 | seti 5 0 1
3 | seti 6 0 2
4 | addi 0 1 0
5 | addr 1 2 3
6 | setr 1 0 0
7 | seti 8 0 4
8 | seti 9 0 5
9 |
--------------------------------------------------------------------------------
/aoc19/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 | use std::io::{self, Read, Write};
3 | use std::result;
4 | use std::str::{self, FromStr};
5 |
6 | use lazy_static::lazy_static;
7 | use regex::Regex;
8 |
9 | macro_rules! err {
10 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
11 | }
12 |
13 | type Result = result::Result>;
14 |
15 | fn main() -> Result<()> {
16 | let mut input = String::new();
17 | io::stdin().read_to_string(&mut input)?;
18 | let prog: Program = input.parse()?;
19 |
20 | part1(&prog)?;
21 | part2(&prog)?;
22 | Ok(())
23 | }
24 |
25 | fn part1(prog: &Program) -> Result<()> {
26 | let mut vm = VM::default();
27 | writeln!(io::stdout(), "result in register 0: {}", vm.exec(prog)?)?;
28 | Ok(())
29 | }
30 |
31 | fn part2(prog: &Program) -> Result<()> {
32 | let mut vm = VM::default();
33 | vm.registers.set(Register::R0, 1);
34 | writeln!(io::stdout(), "result in register 0, redux: {}", vm.exec(prog)?)?;
35 | Ok(())
36 | }
37 |
38 | #[derive(Clone, Debug, Default)]
39 | struct VM {
40 | registers: Registers,
41 | ip: usize,
42 | }
43 |
44 | impl VM {
45 | fn exec(&mut self, prog: &Program) -> Result {
46 | while let Some(op) = prog.ops.get(self.ip) {
47 | if self.ip == 3 {
48 | self.ip = self.fast();
49 | continue;
50 | }
51 | self.registers.set(prog.ipreg, self.ip as i64);
52 | op.exec(&mut self.registers);
53 | self.ip = self.registers.get(prog.ipreg) as usize + 1;
54 | }
55 | Ok(self.registers.get(Register::R0))
56 | }
57 |
58 | fn fast(&mut self) -> usize {
59 | use self::Register::*;
60 |
61 | // The code below optimizes this loop:
62 | //
63 | // R2 = ... # invariant below
64 | //
65 | // R3 = R4 * R1
66 | // if R3 == R2:
67 | // R3 = 1
68 | // R0 = R4 + R0
69 | // else:
70 | // R3 = 0
71 | // R1 = R1 + 1
72 | // if R1 > R2:
73 | // R3 = 1
74 | // goto beginning
75 | // else:
76 | // R3 = 0
77 | // continue to ip=12
78 | //
79 | // The above appears to be a very inefficient way of determining
80 | // whether R4 divides R2.
81 |
82 | if self.registers.get(R2) % self.registers.get(R4) == 0 {
83 | let sum = self.registers.get(R4) + self.registers.get(R0);
84 | self.registers.set(R0, sum);
85 | }
86 |
87 | let r2 = self.registers.get(R2);
88 | self.registers.set(R1, r2);
89 | self.registers.set(R3, 0);
90 | 12
91 | }
92 |
93 | }
94 |
95 | #[derive(Clone, Debug)]
96 | struct Program {
97 | ipreg: Register,
98 | ops: Vec,
99 | }
100 |
101 | impl FromStr for Program {
102 | type Err = Box;
103 |
104 | fn from_str(s: &str) -> Result {
105 | let mut prog = Program {
106 | ipreg: Register::R1,
107 | ops: vec![],
108 | };
109 | for line in s.lines() {
110 | if line.starts_with("#ip ") {
111 | let bound: i64 = line[4..].parse()?;
112 | prog.ipreg = Register::from_number(bound)?;
113 | } else {
114 | prog.ops.push(line.parse()?);
115 | }
116 | }
117 | Ok(prog)
118 | }
119 | }
120 |
121 | #[derive(Clone, Debug)]
122 | struct Op {
123 | output: Register,
124 | kind: OpKind,
125 | }
126 |
127 | #[derive(Clone, Debug)]
128 | enum OpKind {
129 | Addr { a: Register, b: Register },
130 | Addi { a: Register, b: Immediate },
131 | Mulr { a: Register, b: Register },
132 | Muli { a: Register, b: Immediate },
133 | Banr { a: Register, b: Register },
134 | Bani { a: Register, b: Immediate },
135 | Borr { a: Register, b: Register },
136 | Bori { a: Register, b: Immediate },
137 | Setr { a: Register },
138 | Seti { a: Immediate },
139 | Gtir { a: Immediate, b: Register },
140 | Gtri { a: Register, b: Immediate },
141 | Gtrr { a: Register, b: Register },
142 | Eqir { a: Immediate, b: Register },
143 | Eqri { a: Register, b: Immediate },
144 | Eqrr { a: Register, b: Register },
145 | }
146 |
147 | impl Op {
148 | fn exec(&self, regs: &mut Registers) {
149 | use self::OpKind::*;
150 |
151 | let value = match self.kind {
152 | Addr { a, b } => regs.get(a) + regs.get(b),
153 | Addi { a, b } => regs.get(a) + b,
154 | Mulr { a, b } => regs.get(a) * regs.get(b),
155 | Muli { a, b } => regs.get(a) * b,
156 | Banr { a, b } => regs.get(a) & regs.get(b),
157 | Bani { a, b } => regs.get(a) & b,
158 | Borr { a, b } => regs.get(a) | regs.get(b),
159 | Bori { a, b } => regs.get(a) | b,
160 | Setr { a } => regs.get(a),
161 | Seti { a } => a,
162 | Gtir { a, b } => if a > regs.get(b) { 1 } else { 0 },
163 | Gtri { a, b } => if regs.get(a) > b { 1 } else { 0 },
164 | Gtrr { a, b } => if regs.get(a) > regs.get(b) { 1 } else { 0 },
165 | Eqir { a, b } => if a == regs.get(b) { 1 } else { 0 },
166 | Eqri { a, b } => if regs.get(a) == b { 1 } else { 0 },
167 | Eqrr { a, b } => if regs.get(a) == regs.get(b) { 1 } else { 0 },
168 | };
169 | regs.set(self.output, value);
170 | }
171 | }
172 |
173 | type Immediate = i64;
174 |
175 | #[derive(Clone, Debug, Default, Eq, PartialEq)]
176 | struct Registers([i64; 6]);
177 |
178 | #[derive(Clone, Copy, Debug)]
179 | enum Register {
180 | R0,
181 | R1,
182 | R2,
183 | R3,
184 | R4,
185 | R5,
186 | }
187 |
188 | impl Registers {
189 | fn get(&self, r: Register) -> i64 {
190 | match r {
191 | Register::R0 => self.0[0],
192 | Register::R1 => self.0[1],
193 | Register::R2 => self.0[2],
194 | Register::R3 => self.0[3],
195 | Register::R4 => self.0[4],
196 | Register::R5 => self.0[5],
197 | }
198 | }
199 |
200 | fn set(&mut self, r: Register, v: i64) {
201 | match r {
202 | Register::R0 => self.0[0] = v,
203 | Register::R1 => self.0[1] = v,
204 | Register::R2 => self.0[2] = v,
205 | Register::R3 => self.0[3] = v,
206 | Register::R4 => self.0[4] = v,
207 | Register::R5 => self.0[5] = v,
208 | }
209 | }
210 | }
211 |
212 | impl Register {
213 | fn from_number(n: i64) -> Result {
214 | match n {
215 | 0 => Ok(Register::R0),
216 | 1 => Ok(Register::R1),
217 | 2 => Ok(Register::R2),
218 | 3 => Ok(Register::R3),
219 | 4 => Ok(Register::R4),
220 | 5 => Ok(Register::R5),
221 | _ => err!("invalid register number: {}", n),
222 | }
223 | }
224 | }
225 |
226 | impl FromStr for Op {
227 | type Err = Box;
228 |
229 | fn from_str(s: &str) -> Result {
230 | use self::OpKind::*;
231 |
232 | lazy_static! {
233 | static ref RE: Regex = Regex::new(
234 | r"(?P[a-z]+) (?P[0-9]+) (?P[0-9]+) (?P[0-9]+)"
235 | ).unwrap();
236 | }
237 |
238 | let caps = match RE.captures(s) {
239 | None => return err!("invalid instruction: '{:?}'", s),
240 | Some(caps) => caps,
241 | };
242 | let (a, b) = (caps["a"].parse()?, caps["b"].parse()?);
243 | let mkreg = Register::from_number;
244 | let kind = match &caps["name"] {
245 | "addr" => Addr { a: mkreg(a)?, b: mkreg(b)? },
246 | "addi" => Addi { a: mkreg(a)?, b },
247 | "mulr" => Mulr { a: mkreg(a)?, b: mkreg(b)? },
248 | "muli" => Muli { a: mkreg(a)?, b },
249 | "banr" => Banr { a: mkreg(a)?, b: mkreg(b)? },
250 | "bani" => Bani { a: mkreg(a)?, b },
251 | "borr" => Borr { a: mkreg(a)?, b: mkreg(b)? },
252 | "bori" => Bori { a: mkreg(a)?, b },
253 | "setr" => Setr { a: mkreg(a)? },
254 | "seti" => Seti { a },
255 | "gtir" => Gtir { a, b: mkreg(b)? },
256 | "gtri" => Gtri { a: mkreg(a)?, b },
257 | "gtrr" => Gtrr { a: mkreg(a)?, b: mkreg(b)? },
258 | "eqir" => Eqir { a, b: mkreg(b)? },
259 | "eqri" => Eqri { a: mkreg(a)?, b },
260 | "eqrr" => Eqrr { a: mkreg(a)?, b: mkreg(b)? },
261 | unk => return err!("unknown instruction name: {:?}", unk),
262 | };
263 | Ok(Op {
264 | output: Register::from_number(caps["c"].parse()?)?,
265 | kind: kind,
266 | })
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/aoc20/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc20"
3 | version = "0.1.0"
4 | dependencies = [
5 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
6 | ]
7 |
8 | [[package]]
9 | name = "regex-syntax"
10 | version = "0.6.4"
11 | source = "registry+https://github.com/rust-lang/crates.io-index"
12 | dependencies = [
13 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
14 | ]
15 |
16 | [[package]]
17 | name = "ucd-util"
18 | version = "0.1.3"
19 | source = "registry+https://github.com/rust-lang/crates.io-index"
20 |
21 | [metadata]
22 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
23 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
24 |
--------------------------------------------------------------------------------
/aoc20/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc20"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | regex-syntax = "0.6.4"
9 |
--------------------------------------------------------------------------------
/aoc20/input/me1.txt:
--------------------------------------------------------------------------------
1 | ^W(SWE|)N$
2 |
--------------------------------------------------------------------------------
/aoc20/input/me2.txt:
--------------------------------------------------------------------------------
1 | ^NESW$
2 |
--------------------------------------------------------------------------------
/aoc20/input/test1.txt:
--------------------------------------------------------------------------------
1 | ^WNE$
2 |
--------------------------------------------------------------------------------
/aoc20/input/test2.txt:
--------------------------------------------------------------------------------
1 | ^ENWWW(NEEE|SSE(EE|N))$
2 |
--------------------------------------------------------------------------------
/aoc20/input/test3.txt:
--------------------------------------------------------------------------------
1 | ^ENNWSWW(NEWS|)SSSEEN(WNSE|)EE(SWEN|)NNN$
2 |
--------------------------------------------------------------------------------
/aoc20/input/test4.txt:
--------------------------------------------------------------------------------
1 | ^ESSWWN(E|NNENN(EESS(WNSE|)SSS|WWWSSSSE(SW|NNNE)))$
2 |
--------------------------------------------------------------------------------
/aoc20/input/test5.txt:
--------------------------------------------------------------------------------
1 | ^WSSEESWWWNW(S|NENNEEEENN(ESSSSW(NWSW|SSEN)|WSWWN(E|WWS(E|SS))))$
2 |
--------------------------------------------------------------------------------
/aoc20/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::cmp;
2 | use std::collections::HashMap;
3 | use std::error::Error;
4 | use std::io::{self, Read, Write};
5 | use std::result;
6 |
7 | use regex_syntax::ParserBuilder;
8 | use regex_syntax::hir::{self, Hir, HirKind};
9 |
10 | macro_rules! err {
11 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
12 | }
13 |
14 | type Result = result::Result>;
15 |
16 | fn main() -> Result<()> {
17 | let mut input = String::new();
18 | io::stdin().read_to_string(&mut input)?;
19 |
20 | let expr = ParserBuilder::new()
21 | .nest_limit(1000)
22 | .build()
23 | .parse(input.trim())?;
24 |
25 | let mut dists = Distances::new();
26 | let origin = Coordinate { x: 0, y: 0 };
27 | dists.insert(origin, 0);
28 | distances(&expr, &mut dists, origin)?;
29 |
30 | let largest = dists.values().max().unwrap();
31 | writeln!(io::stdout(), "largest number of doors: {}", largest)?;
32 | let atleast = dists.values().filter(|&&d| d >= 1000).count();
33 | writeln!(io::stdout(), "pass through at least 1000 doors: {}", atleast)?;
34 | Ok(())
35 | }
36 |
37 | type Distances = HashMap;
38 |
39 | fn distances(
40 | expr: &Hir,
41 | dists: &mut Distances,
42 | c: Coordinate,
43 | ) -> Result {
44 | // N.B. Even though this works for my input, it is most certainly wrong in
45 | // the general case.
46 | //
47 | // See: https://github.com/BurntSushi/advent-of-code/issues/15
48 | match *expr.kind() {
49 | | HirKind::Empty
50 | | HirKind::Literal(hir::Literal::Byte(_))
51 | | HirKind::Class(_)
52 | | HirKind::Anchor(_)
53 | | HirKind::WordBoundary(_)
54 | | HirKind::Repetition(_) => Ok(c),
55 | HirKind::Literal(hir::Literal::Unicode(ch)) => {
56 | let nextc = c.mv(ch)?;
57 | let mut dist = dists[&c] + 1;
58 | if dists.contains_key(&nextc) {
59 | dist = cmp::min(dist, dists[&nextc])
60 | }
61 | dists.insert(nextc, dist);
62 | Ok(nextc)
63 | }
64 | HirKind::Group(ref g) => {
65 | distances(&g.hir, dists, c)
66 | }
67 | HirKind::Concat(ref exprs) => {
68 | let mut nextc = c;
69 | for e in exprs {
70 | nextc = distances(e, dists, nextc)?;
71 | }
72 | Ok(nextc)
73 | }
74 | HirKind::Alternation(ref exprs) => {
75 | for e in exprs {
76 | distances(e, dists, c)?;
77 | }
78 | Ok(c)
79 | }
80 | }
81 | }
82 |
83 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
84 | struct Coordinate {
85 | x: i64,
86 | y: i64,
87 | }
88 |
89 | impl Coordinate {
90 | fn mv(self, direction: char) -> Result {
91 | match direction {
92 | 'N' => Ok(Coordinate { x: self.x, y: self.y - 1 }),
93 | 'S' => Ok(Coordinate { x: self.x, y: self.y + 1 }),
94 | 'W' => Ok(Coordinate { x: self.x - 1, y: self.y }),
95 | 'E' => Ok(Coordinate { x: self.x + 1, y: self.y }),
96 | _ => err!("unknown direction: {:?}", direction),
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/aoc21/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc21"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
15 | ]
16 |
17 | [[package]]
18 | name = "cfg-if"
19 | version = "0.1.6"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 |
22 | [[package]]
23 | name = "lazy_static"
24 | version = "1.2.0"
25 | source = "registry+https://github.com/rust-lang/crates.io-index"
26 |
27 | [[package]]
28 | name = "libc"
29 | version = "0.2.45"
30 | source = "registry+https://github.com/rust-lang/crates.io-index"
31 |
32 | [[package]]
33 | name = "memchr"
34 | version = "2.1.2"
35 | source = "registry+https://github.com/rust-lang/crates.io-index"
36 | dependencies = [
37 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
38 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
39 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
40 | ]
41 |
42 | [[package]]
43 | name = "regex"
44 | version = "1.1.0"
45 | source = "registry+https://github.com/rust-lang/crates.io-index"
46 | dependencies = [
47 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
48 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
49 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
50 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
51 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
52 | ]
53 |
54 | [[package]]
55 | name = "regex-syntax"
56 | version = "0.6.4"
57 | source = "registry+https://github.com/rust-lang/crates.io-index"
58 | dependencies = [
59 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
60 | ]
61 |
62 | [[package]]
63 | name = "thread_local"
64 | version = "0.3.6"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | dependencies = [
67 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "ucd-util"
72 | version = "0.1.3"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 |
75 | [[package]]
76 | name = "utf8-ranges"
77 | version = "1.0.2"
78 | source = "registry+https://github.com/rust-lang/crates.io-index"
79 |
80 | [[package]]
81 | name = "version_check"
82 | version = "0.1.5"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 |
85 | [metadata]
86 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
87 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
88 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
89 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
90 | "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
91 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
92 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
93 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
94 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
95 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
96 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
97 |
--------------------------------------------------------------------------------
/aoc21/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc21"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1.2"
9 | regex = "1.1"
10 |
--------------------------------------------------------------------------------
/aoc21/input/input.txt:
--------------------------------------------------------------------------------
1 | #ip 1
2 | seti 123 0 5
3 | bani 5 456 5
4 | eqri 5 72 5
5 | addr 5 1 1
6 | seti 0 0 1
7 | seti 0 3 5
8 | bori 5 65536 4
9 | seti 13284195 4 5
10 | bani 4 255 3
11 | addr 5 3 5
12 | bani 5 16777215 5
13 | muli 5 65899 5
14 | bani 5 16777215 5
15 | gtir 256 4 3
16 | addr 3 1 1
17 | addi 1 1 1
18 | seti 27 1 1
19 | seti 0 5 3
20 | addi 3 1 2
21 | muli 2 256 2
22 | gtrr 2 4 2
23 | addr 2 1 1
24 | addi 1 1 1
25 | seti 25 2 1
26 | addi 3 1 3
27 | seti 17 1 1
28 | setr 3 7 4
29 | seti 7 3 1
30 | eqrr 5 0 3
31 | addr 3 1 1
32 | seti 5 3 1
33 |
--------------------------------------------------------------------------------
/aoc21/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashSet;
2 | use std::error::Error;
3 | use std::io::{self, Read, Write};
4 | use std::result;
5 | use std::str::{self, FromStr};
6 |
7 | use lazy_static::lazy_static;
8 | use regex::Regex;
9 |
10 | macro_rules! err {
11 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
12 | }
13 |
14 | type Result = result::Result>;
15 |
16 | fn main() -> Result<()> {
17 | let mut input = String::new();
18 | io::stdin().read_to_string(&mut input)?;
19 | let prog: Program = input.parse()?;
20 |
21 | part1(&prog)?;
22 | part2(&prog)?;
23 | Ok(())
24 | }
25 |
26 | fn part1(prog: &Program) -> Result<()> {
27 | let mut vm = VM::default();
28 | // Number was found by looking at the value of R5 the first time
29 | // instruction 28 was executed (`eqrr 5 0 3`). In particular, this is the
30 | // only instruction that mentions R0, so the smallest value of R0 must be
31 | // whatever R5 is the first time instruction 28 is executed.
32 | vm.registers.set(Register::R0, 7224964);
33 | writeln!(io::stdout(), "result in register 0: {}", vm.exec(prog)?)?;
34 | Ok(())
35 | }
36 |
37 | fn part2(prog: &Program) -> Result<()> {
38 | let mut vm = VM::default();
39 | writeln!(
40 | io::stdout(),
41 | "value for R0 to cause hault in most instructions: {}",
42 | vm.exec_part2(prog)?,
43 | )?;
44 | Ok(())
45 | }
46 |
47 | #[derive(Clone, Debug, Default)]
48 | struct VM {
49 | registers: Registers,
50 | ip: usize,
51 | }
52 |
53 | impl VM {
54 | fn exec(&mut self, prog: &Program) -> Result {
55 | while let Some(op) = prog.ops.get(self.ip) {
56 | self.registers.set(prog.ipreg, self.ip as i64);
57 | op.exec(&mut self.registers);
58 | self.ip = self.registers.get(prog.ipreg) as usize + 1;
59 | }
60 | Ok(self.registers.get(Register::R0))
61 | }
62 |
63 | // Like exec, but we collect all values of R5 at instruction 28. Assuming
64 | // there is a pattern, we collect all such values in the cycle in the order
65 | // in which they are seen. The last value in that cycle should be our
66 | // answer.
67 | //
68 | // If there is no pattern... Then ¯\_(ツ)_/¯
69 | fn exec_part2(&mut self, prog: &Program) -> Result {
70 | let mut cycle = vec![];
71 | let mut seen = HashSet::new();
72 | while let Some(op) = prog.ops.get(self.ip) {
73 | self.registers.set(prog.ipreg, self.ip as i64);
74 | op.exec(&mut self.registers);
75 | self.ip = self.registers.get(prog.ipreg) as usize + 1;
76 | if self.ip == 28 {
77 | let r5 = self.registers.get(Register::R5);
78 | if seen.contains(&r5) {
79 | return Ok(*cycle.last().unwrap());
80 | }
81 | seen.insert(r5);
82 | cycle.push(r5);
83 | }
84 | }
85 | err!("found no cycle")
86 | }
87 | }
88 |
89 | #[derive(Clone, Debug)]
90 | struct Program {
91 | ipreg: Register,
92 | ops: Vec,
93 | }
94 |
95 | impl FromStr for Program {
96 | type Err = Box;
97 |
98 | fn from_str(s: &str) -> Result {
99 | let mut prog = Program {
100 | ipreg: Register::R1,
101 | ops: vec![],
102 | };
103 | for line in s.lines() {
104 | if line.starts_with("#ip ") {
105 | let bound: i64 = line[4..].parse()?;
106 | prog.ipreg = Register::from_number(bound)?;
107 | } else {
108 | prog.ops.push(line.parse()?);
109 | }
110 | }
111 | Ok(prog)
112 | }
113 | }
114 |
115 | #[derive(Clone, Debug)]
116 | struct Op {
117 | output: Register,
118 | kind: OpKind,
119 | }
120 |
121 | #[derive(Clone, Debug)]
122 | enum OpKind {
123 | Addr { a: Register, b: Register },
124 | Addi { a: Register, b: Immediate },
125 | Mulr { a: Register, b: Register },
126 | Muli { a: Register, b: Immediate },
127 | Banr { a: Register, b: Register },
128 | Bani { a: Register, b: Immediate },
129 | Borr { a: Register, b: Register },
130 | Bori { a: Register, b: Immediate },
131 | Setr { a: Register },
132 | Seti { a: Immediate },
133 | Gtir { a: Immediate, b: Register },
134 | Gtri { a: Register, b: Immediate },
135 | Gtrr { a: Register, b: Register },
136 | Eqir { a: Immediate, b: Register },
137 | Eqri { a: Register, b: Immediate },
138 | Eqrr { a: Register, b: Register },
139 | }
140 |
141 | impl Op {
142 | fn exec(&self, regs: &mut Registers) {
143 | use self::OpKind::*;
144 |
145 | let value = match self.kind {
146 | Addr { a, b } => regs.get(a) + regs.get(b),
147 | Addi { a, b } => regs.get(a) + b,
148 | Mulr { a, b } => regs.get(a) * regs.get(b),
149 | Muli { a, b } => regs.get(a) * b,
150 | Banr { a, b } => regs.get(a) & regs.get(b),
151 | Bani { a, b } => regs.get(a) & b,
152 | Borr { a, b } => regs.get(a) | regs.get(b),
153 | Bori { a, b } => regs.get(a) | b,
154 | Setr { a } => regs.get(a),
155 | Seti { a } => a,
156 | Gtir { a, b } => if a > regs.get(b) { 1 } else { 0 },
157 | Gtri { a, b } => if regs.get(a) > b { 1 } else { 0 },
158 | Gtrr { a, b } => if regs.get(a) > regs.get(b) { 1 } else { 0 },
159 | Eqir { a, b } => if a == regs.get(b) { 1 } else { 0 },
160 | Eqri { a, b } => if regs.get(a) == b { 1 } else { 0 },
161 | Eqrr { a, b } => if regs.get(a) == regs.get(b) { 1 } else { 0 },
162 | };
163 | regs.set(self.output, value);
164 | }
165 | }
166 |
167 | type Immediate = i64;
168 |
169 | #[derive(Clone, Debug, Default, Eq, PartialEq)]
170 | struct Registers([i64; 6]);
171 |
172 | #[derive(Clone, Copy, Debug)]
173 | enum Register {
174 | R0,
175 | R1,
176 | R2,
177 | R3,
178 | R4,
179 | R5,
180 | }
181 |
182 | impl Registers {
183 | fn get(&self, r: Register) -> i64 {
184 | match r {
185 | Register::R0 => self.0[0],
186 | Register::R1 => self.0[1],
187 | Register::R2 => self.0[2],
188 | Register::R3 => self.0[3],
189 | Register::R4 => self.0[4],
190 | Register::R5 => self.0[5],
191 | }
192 | }
193 |
194 | fn set(&mut self, r: Register, v: i64) {
195 | match r {
196 | Register::R0 => self.0[0] = v,
197 | Register::R1 => self.0[1] = v,
198 | Register::R2 => self.0[2] = v,
199 | Register::R3 => self.0[3] = v,
200 | Register::R4 => self.0[4] = v,
201 | Register::R5 => self.0[5] = v,
202 | }
203 | }
204 | }
205 |
206 | impl Register {
207 | fn from_number(n: i64) -> Result {
208 | match n {
209 | 0 => Ok(Register::R0),
210 | 1 => Ok(Register::R1),
211 | 2 => Ok(Register::R2),
212 | 3 => Ok(Register::R3),
213 | 4 => Ok(Register::R4),
214 | 5 => Ok(Register::R5),
215 | _ => err!("invalid register number: {}", n),
216 | }
217 | }
218 | }
219 |
220 | impl FromStr for Op {
221 | type Err = Box;
222 |
223 | fn from_str(s: &str) -> Result {
224 | use self::OpKind::*;
225 |
226 | lazy_static! {
227 | static ref RE: Regex = Regex::new(
228 | r"(?P[a-z]+) (?P[0-9]+) (?P[0-9]+) (?P[0-9]+)"
229 | ).unwrap();
230 | }
231 |
232 | let caps = match RE.captures(s) {
233 | None => return err!("invalid instruction: '{:?}'", s),
234 | Some(caps) => caps,
235 | };
236 | let (a, b) = (caps["a"].parse()?, caps["b"].parse()?);
237 | let mkreg = Register::from_number;
238 | let kind = match &caps["name"] {
239 | "addr" => Addr { a: mkreg(a)?, b: mkreg(b)? },
240 | "addi" => Addi { a: mkreg(a)?, b },
241 | "mulr" => Mulr { a: mkreg(a)?, b: mkreg(b)? },
242 | "muli" => Muli { a: mkreg(a)?, b },
243 | "banr" => Banr { a: mkreg(a)?, b: mkreg(b)? },
244 | "bani" => Bani { a: mkreg(a)?, b },
245 | "borr" => Borr { a: mkreg(a)?, b: mkreg(b)? },
246 | "bori" => Bori { a: mkreg(a)?, b },
247 | "setr" => Setr { a: mkreg(a)? },
248 | "seti" => Seti { a },
249 | "gtir" => Gtir { a, b: mkreg(b)? },
250 | "gtri" => Gtri { a: mkreg(a)?, b },
251 | "gtrr" => Gtrr { a: mkreg(a)?, b: mkreg(b)? },
252 | "eqir" => Eqir { a, b: mkreg(b)? },
253 | "eqri" => Eqri { a: mkreg(a)?, b },
254 | "eqrr" => Eqrr { a: mkreg(a)?, b: mkreg(b)? },
255 | unk => return err!("unknown instruction name: {:?}", unk),
256 | };
257 | Ok(Op {
258 | output: Register::from_number(caps["c"].parse()?)?,
259 | kind: kind,
260 | })
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/aoc22/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc22"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc22/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc22"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc22/input/input.txt:
--------------------------------------------------------------------------------
1 | depth: 9171
2 | target: 7,721
3 |
--------------------------------------------------------------------------------
/aoc22/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::cmp::Reverse;
2 | use std::collections::{BinaryHeap, HashMap};
3 | use std::error::Error;
4 | use std::io::{self, Write};
5 | use std::result;
6 |
7 | const DEPTH: usize = 9171;
8 | const TARGET: Coordinate = Coordinate { x: 7, y: 721 };
9 |
10 | macro_rules! err {
11 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
12 | }
13 |
14 | type Result = result::Result>;
15 |
16 | fn main() -> Result<()> {
17 | let cave = Cave::new(DEPTH, TARGET)?;
18 | writeln!(io::stdout(), "risk level: {}", cave.risk_level())?;
19 | writeln!(io::stdout(), "time to target: {}", cave.shortest_time()?)?;
20 | Ok(())
21 | }
22 |
23 | #[derive(Clone, Debug)]
24 | struct Cave {
25 | depth: usize,
26 | target: Coordinate,
27 | bound: Coordinate,
28 | regions: Vec>,
29 | }
30 |
31 | impl Cave {
32 | fn new(depth: usize, target: Coordinate) -> Result {
33 | let mut scanner = CaveScanner::new(depth, target);
34 | scanner.scan();
35 | scanner.cave()
36 | }
37 |
38 | fn risk_level(&self) -> usize {
39 | let mut risk_level = 0;
40 | for y in 0..=self.target.y {
41 | for x in 0..=self.target.x {
42 | risk_level += self.regions[y][x].risk_level();
43 | }
44 | }
45 | risk_level
46 | }
47 |
48 | fn shortest_time(&self) -> Result {
49 | type Time = usize; // minutes
50 | type PriorityQueue = BinaryHeap>;
51 |
52 | let mut queue: PriorityQueue = BinaryHeap::new();
53 | let mut best: HashMap<(Coordinate, Equip), Time> = HashMap::new();
54 |
55 | queue.push(Reverse((0, Coordinate { x: 0, y: 0 }, Equip::Torch)));
56 | while let Some(Reverse((time, c, equip))) = queue.pop() {
57 | if best.contains_key(&(c, equip)) && best[&(c, equip)] <= time {
58 | continue;
59 | }
60 | best.insert((c, equip), time);
61 | if c == self.target && equip == Equip::Torch {
62 | return Ok(time);
63 | }
64 |
65 | // Try equipping different tools.
66 | for &e in &[Equip::Torch, Equip::Gear, Equip::Neither] {
67 | if self.regions[c.y][c.x].can_equip(e) {
68 | queue.push(Reverse((time + 7, c, e)));
69 | }
70 | }
71 | // Try visiting each neighbor.
72 | for &(x, y) in &[(0, -1), (1, 0), (0, 1), (-1, 0)] {
73 | if (x < 0 && c.x == 0) || (y < 0 && c.y == 0) {
74 | continue;
75 | }
76 |
77 | let x = (c.x as i64 + x) as usize;
78 | let y = (c.y as i64 + y) as usize;
79 | if x > self.bound.x || y > self.bound.y {
80 | continue;
81 | }
82 | if self.regions[y][x].can_equip(equip) {
83 | let neighbor = Coordinate { x, y };
84 | queue.push(Reverse((time + 1, neighbor, equip)));
85 | }
86 | }
87 | }
88 | err!("could not find a path to {:?}", self.target)
89 | }
90 | }
91 |
92 | #[derive(Clone, Debug)]
93 | struct CaveScanner {
94 | depth: usize,
95 | target: Coordinate,
96 | bound: Coordinate,
97 | regions: Vec>>,
98 | }
99 |
100 | impl CaveScanner {
101 | fn new(depth: usize, target: Coordinate) -> CaveScanner {
102 | // In part 2, we might need to travel outside the rectangle created
103 | // by the mouth and the target. We heuristic expand the bounds by a
104 | // factor of 2 in both directions. I don't think there is any guarantee
105 | // that this works in general, but ¯\_(ツ)_/¯.
106 | //
107 | // Actually, a factor of 2 wasn't enough! It gave us an answer of 1009,
108 | // which was too high. Bumping this up to a factor of 10 gave us the
109 | // correct answer of 986. Oof.
110 | let bound = Coordinate { x: target.x * 10, y: target.y * 10 };
111 | let regions = vec![vec![None; bound.x + 1]; bound.y + 1];
112 | CaveScanner { depth, target, bound, regions }
113 | }
114 |
115 | fn scan(&mut self) {
116 | self.regions[0][0] = Some(Region::new(self.depth, 0));
117 | self.regions[self.target.y][self.target.x] =
118 | Some(Region::new(self.depth, 0));
119 | for x in 0..=self.bound.x {
120 | self.regions[0][x] = Some(Region::new(self.depth, x * 16_807));
121 | }
122 | for y in 0..=self.bound.y {
123 | self.regions[y][0] = Some(Region::new(self.depth, y * 48_271));
124 | }
125 | for y in 1..=self.bound.y {
126 | for x in 1..=self.bound.x {
127 | if x == self.target.x && y == self.target.y {
128 | continue;
129 | }
130 |
131 | // These unwraps are OK because we are guaranteed to have
132 | // computed the region for left and above in a prior iteration.
133 | let left = self.regions[y][x-1].as_ref().unwrap();
134 | let above = self.regions[y-1][x].as_ref().unwrap();
135 | let geologic_index = left.erosion_level * above.erosion_level;
136 | let region = Region::new(self.depth, geologic_index);
137 | self.regions[y][x] = Some(region);
138 | }
139 | }
140 | }
141 |
142 | fn cave(&self) -> Result {
143 | let mut cave = Cave {
144 | depth: self.depth,
145 | target: self.target,
146 | bound: self.bound,
147 | regions: vec![],
148 | };
149 | for y in 0..=self.bound.y {
150 | let mut row = vec![];
151 | for x in 0..=self.bound.x {
152 | let region = match self.regions[y][x].clone() {
153 | None => return err!("unknown region at ({}, {})", x, y),
154 | Some(region) => region,
155 | };
156 | row.push(region);
157 | }
158 | cave.regions.push(row);
159 | }
160 | Ok(cave)
161 | }
162 | }
163 |
164 | #[derive(Clone, Debug)]
165 | struct Region {
166 | typ: RegionType,
167 | geologic_index: usize,
168 | erosion_level: usize,
169 | }
170 |
171 | #[derive(Clone, Copy, Debug)]
172 | enum RegionType {
173 | Rocky,
174 | Wet,
175 | Narrow,
176 | }
177 |
178 | impl Region {
179 | fn new(cave_depth: usize, geologic_index: usize) -> Region {
180 | let erosion_level = (geologic_index + cave_depth) % 20183;
181 | let typ = match erosion_level % 3 {
182 | 0 => RegionType::Rocky,
183 | 1 => RegionType::Wet,
184 | 2 => RegionType::Narrow,
185 | _ => unreachable!(),
186 | };
187 | Region { typ, geologic_index, erosion_level }
188 | }
189 |
190 | fn risk_level(&self) -> usize {
191 | match self.typ {
192 | RegionType::Rocky => 0,
193 | RegionType::Wet => 1,
194 | RegionType::Narrow => 2,
195 | }
196 | }
197 |
198 | fn can_equip(&self, equip: Equip) -> bool {
199 | use self::RegionType::*;
200 | use self::Equip::*;
201 |
202 | match (self.typ, equip) {
203 | (Rocky, Torch) | (Rocky, Gear) => true,
204 | (Wet, Gear) | (Wet, Neither) => true,
205 | (Narrow, Torch) | (Narrow, Neither) => true,
206 | _ => false,
207 | }
208 | }
209 | }
210 |
211 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
212 | enum Equip {
213 | Torch,
214 | Gear,
215 | Neither,
216 | }
217 |
218 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
219 | struct Coordinate {
220 | x: usize,
221 | y: usize,
222 | }
223 |
--------------------------------------------------------------------------------
/aoc23/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aho-corasick"
3 | version = "0.6.9"
4 | source = "registry+https://github.com/rust-lang/crates.io-index"
5 | dependencies = [
6 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
7 | ]
8 |
9 | [[package]]
10 | name = "aoc23"
11 | version = "0.1.0"
12 | dependencies = [
13 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
14 | "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
15 | "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
16 | ]
17 |
18 | [[package]]
19 | name = "bitflags"
20 | version = "1.0.4"
21 | source = "registry+https://github.com/rust-lang/crates.io-index"
22 |
23 | [[package]]
24 | name = "cfg-if"
25 | version = "0.1.6"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 |
28 | [[package]]
29 | name = "cloudabi"
30 | version = "0.0.3"
31 | source = "registry+https://github.com/rust-lang/crates.io-index"
32 | dependencies = [
33 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
34 | ]
35 |
36 | [[package]]
37 | name = "fuchsia-zircon"
38 | version = "0.3.3"
39 | source = "registry+https://github.com/rust-lang/crates.io-index"
40 | dependencies = [
41 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
42 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
43 | ]
44 |
45 | [[package]]
46 | name = "fuchsia-zircon-sys"
47 | version = "0.3.3"
48 | source = "registry+https://github.com/rust-lang/crates.io-index"
49 |
50 | [[package]]
51 | name = "lazy_static"
52 | version = "1.2.0"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 |
55 | [[package]]
56 | name = "libc"
57 | version = "0.2.45"
58 | source = "registry+https://github.com/rust-lang/crates.io-index"
59 |
60 | [[package]]
61 | name = "memchr"
62 | version = "2.1.2"
63 | source = "registry+https://github.com/rust-lang/crates.io-index"
64 | dependencies = [
65 | "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
66 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
67 | "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
68 | ]
69 |
70 | [[package]]
71 | name = "rand"
72 | version = "0.6.1"
73 | source = "registry+https://github.com/rust-lang/crates.io-index"
74 | dependencies = [
75 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
76 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
77 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
78 | "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
79 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
80 | "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
81 | "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
82 | "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
83 | "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
84 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
85 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
86 | ]
87 |
88 | [[package]]
89 | name = "rand_chacha"
90 | version = "0.1.0"
91 | source = "registry+https://github.com/rust-lang/crates.io-index"
92 | dependencies = [
93 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
94 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
95 | ]
96 |
97 | [[package]]
98 | name = "rand_core"
99 | version = "0.3.0"
100 | source = "registry+https://github.com/rust-lang/crates.io-index"
101 |
102 | [[package]]
103 | name = "rand_hc"
104 | version = "0.1.0"
105 | source = "registry+https://github.com/rust-lang/crates.io-index"
106 | dependencies = [
107 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
108 | ]
109 |
110 | [[package]]
111 | name = "rand_isaac"
112 | version = "0.1.1"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | dependencies = [
115 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
116 | ]
117 |
118 | [[package]]
119 | name = "rand_pcg"
120 | version = "0.1.1"
121 | source = "registry+https://github.com/rust-lang/crates.io-index"
122 | dependencies = [
123 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
124 | "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
125 | ]
126 |
127 | [[package]]
128 | name = "rand_xorshift"
129 | version = "0.1.0"
130 | source = "registry+https://github.com/rust-lang/crates.io-index"
131 | dependencies = [
132 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
133 | ]
134 |
135 | [[package]]
136 | name = "regex"
137 | version = "1.1.0"
138 | source = "registry+https://github.com/rust-lang/crates.io-index"
139 | dependencies = [
140 | "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
141 | "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
142 | "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
143 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
144 | "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
145 | ]
146 |
147 | [[package]]
148 | name = "regex-syntax"
149 | version = "0.6.4"
150 | source = "registry+https://github.com/rust-lang/crates.io-index"
151 | dependencies = [
152 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
153 | ]
154 |
155 | [[package]]
156 | name = "rustc_version"
157 | version = "0.2.3"
158 | source = "registry+https://github.com/rust-lang/crates.io-index"
159 | dependencies = [
160 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
161 | ]
162 |
163 | [[package]]
164 | name = "semver"
165 | version = "0.9.0"
166 | source = "registry+https://github.com/rust-lang/crates.io-index"
167 | dependencies = [
168 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
169 | ]
170 |
171 | [[package]]
172 | name = "semver-parser"
173 | version = "0.7.0"
174 | source = "registry+https://github.com/rust-lang/crates.io-index"
175 |
176 | [[package]]
177 | name = "thread_local"
178 | version = "0.3.6"
179 | source = "registry+https://github.com/rust-lang/crates.io-index"
180 | dependencies = [
181 | "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
182 | ]
183 |
184 | [[package]]
185 | name = "ucd-util"
186 | version = "0.1.3"
187 | source = "registry+https://github.com/rust-lang/crates.io-index"
188 |
189 | [[package]]
190 | name = "utf8-ranges"
191 | version = "1.0.2"
192 | source = "registry+https://github.com/rust-lang/crates.io-index"
193 |
194 | [[package]]
195 | name = "version_check"
196 | version = "0.1.5"
197 | source = "registry+https://github.com/rust-lang/crates.io-index"
198 |
199 | [[package]]
200 | name = "winapi"
201 | version = "0.3.6"
202 | source = "registry+https://github.com/rust-lang/crates.io-index"
203 | dependencies = [
204 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
205 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
206 | ]
207 |
208 | [[package]]
209 | name = "winapi-i686-pc-windows-gnu"
210 | version = "0.4.0"
211 | source = "registry+https://github.com/rust-lang/crates.io-index"
212 |
213 | [[package]]
214 | name = "winapi-x86_64-pc-windows-gnu"
215 | version = "0.4.0"
216 | source = "registry+https://github.com/rust-lang/crates.io-index"
217 |
218 | [metadata]
219 | "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
220 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
221 | "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
222 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
223 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
224 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
225 | "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
226 | "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
227 | "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
228 | "checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
229 | "checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
230 | "checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
231 | "checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
232 | "checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
233 | "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
234 | "checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
235 | "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
236 | "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
237 | "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
238 | "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
239 | "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
240 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
241 | "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
242 | "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
243 | "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
244 | "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
245 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
246 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
247 |
--------------------------------------------------------------------------------
/aoc23/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc23"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | lazy_static = "1.2"
9 | rand = "0.6.1"
10 | regex = "1.1"
11 |
--------------------------------------------------------------------------------
/aoc23/input/test1.txt:
--------------------------------------------------------------------------------
1 | pos=<0,0,0>, r=4
2 | pos=<1,0,0>, r=1
3 | pos=<4,0,0>, r=3
4 | pos=<0,2,0>, r=1
5 | pos=<0,5,0>, r=3
6 | pos=<0,0,3>, r=1
7 | pos=<1,1,1>, r=1
8 | pos=<1,1,2>, r=1
9 | pos=<1,3,1>, r=1
10 |
--------------------------------------------------------------------------------
/aoc23/input/test2.txt:
--------------------------------------------------------------------------------
1 | pos=<10,12,12>, r=2
2 | pos=<12,14,12>, r=2
3 | pos=<16,12,12>, r=4
4 | pos=<14,14,14>, r=6
5 | pos=<50,50,50>, r=200
6 | pos=<10,10,10>, r=5
7 |
--------------------------------------------------------------------------------
/aoc23/src/main.rs:
--------------------------------------------------------------------------------
1 | #![allow(warnings)]
2 |
3 | use std::collections::HashSet;
4 | use std::error::Error;
5 | use std::io::{self, Read, Write};
6 | use std::result;
7 | use std::str::{self, FromStr};
8 |
9 | use lazy_static::lazy_static;
10 | use rand::Rng;
11 | use rand::seq::SliceRandom;
12 | use regex::Regex;
13 |
14 | macro_rules! err {
15 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
16 | }
17 |
18 | type Result = result::Result>;
19 |
20 | fn main() -> Result<()> {
21 | let mut input = String::new();
22 | io::stdin().read_to_string(&mut input)?;
23 |
24 | let bots: Bots = input.parse()?;
25 |
26 | let largest = bots.largest_radius();
27 | let in_range = bots.in_range_of_bot(&largest);
28 | writeln!(io::stdout(), "nanobots in range: {}", in_range)?;
29 |
30 | // The solution to part 2 is very dissatisfying. We use a cobbled
31 | // together version of simulated annealing and combine it with a somewhat
32 | // intelligent initial sample of points. Specifically, for each bot, we
33 | // sample points along the edge of its sphere of influence. The thinking
34 | // here is that optimal coordinate is probably close to the edge of at
35 | // least one sphere.
36 | //
37 | // Running this program does not guarantee the correct answer each time,
38 | // and it probably takes too long to run to completion anyway. I guessed
39 | // a few numbers as the distance appeared to stabilize and eventually got
40 | // it right.
41 | //
42 | // guessed: 111_851_609 (too low), 832 in range
43 | // 111_789_973 also has 832 in range.
44 | // 111_770_929 also has 832 in range.
45 | // guessed: 118_995_681
46 | // guessed: 121_493_970 (853 in range)
47 | // guessed: 121_493_971 (correct)
48 | let best = search(&bots);
49 | writeln!(io::stdout(), "BEST: {:?}", best);
50 | let dist = Coordinate::origin().distance(&best);
51 | writeln!(io::stdout(), "shortest distance: {}", dist)?;
52 | Ok(())
53 | }
54 |
55 | fn search(bots: &Bots) -> Coordinate {
56 | const INIT_TEMPERATURE: f64 = 1_000.0;
57 | const COOLING_FACTOR: f64 = 0.9999;
58 | const ITERS: usize = 1_000;
59 |
60 | fn prob(iter: usize, in_range_old: u64, in_range_new: u64) -> f64 {
61 | let temp = COOLING_FACTOR.powi(iter as i32) * INIT_TEMPERATURE;
62 | ((in_range_new as f64 - in_range_old as f64) / temp).exp()
63 | }
64 |
65 | let mut rng = rand::thread_rng();
66 | let mut origins = vec![];
67 | for bot in bots.bots.iter() {
68 | for _ in 0..10000 {
69 | origins.push(bot.random_surface_coordinate(&mut rng));
70 | }
71 | }
72 | origins.shuffle(&mut rng);
73 |
74 | let mut best_in_range = bots.in_range(&origins[0]);
75 | let mut best: HashSet = HashSet::new();
76 | best.insert(origins[0]);
77 |
78 | for (i, &o) in origins.iter().enumerate() {
79 | let mut cur_pos = o;
80 | let mut cur_in_range = bots.in_range(&cur_pos);
81 |
82 | for i in 0..ITERS {
83 | let new_pos = cur_pos.random_neighbor(&mut rng);
84 | let new_in_range = bots.in_range(&new_pos);
85 | let p = prob(i, cur_in_range, new_in_range);
86 | if p >= 1.0 || rng.gen_bool(p) {
87 | cur_pos = new_pos;
88 | cur_in_range = new_in_range;
89 | }
90 | if new_in_range == best_in_range {
91 | best.insert(new_pos);
92 | } else if new_in_range > best_in_range {
93 | best.clear();
94 | best.insert(new_pos);
95 | best_in_range = new_in_range;
96 | }
97 | }
98 |
99 | // print out progress
100 | if i % 100 == 0 {
101 | let zzz = best.iter()
102 | .cloned()
103 | .min_by_key(|c| Coordinate::origin().distance(&c))
104 | .unwrap();
105 | println!(
106 | "origin ({}/{}): {:?} => {:?} (in range: {}, dist: {})",
107 | i, origins.len(), o, zzz, best_in_range,
108 | Coordinate::origin().distance(&zzz),
109 | );
110 | }
111 | }
112 | best.iter()
113 | .cloned()
114 | .min_by_key(|c| Coordinate::origin().distance(&c))
115 | .unwrap()
116 | }
117 |
118 | #[derive(Clone, Debug)]
119 | struct Bots {
120 | bots: Vec,
121 | }
122 |
123 | impl Bots {
124 | fn largest_radius(&self) -> &Bot {
125 | self.bots
126 | .iter()
127 | .max_by_key(|b| b.radius)
128 | .unwrap()
129 | }
130 |
131 | fn in_range_of_bot(&self, bot: &Bot) -> u64 {
132 | self.bots.iter().filter(|b| bot.in_range_of_bot(b)).count() as u64
133 | }
134 |
135 | fn in_range(&self, c: &Coordinate) -> u64 {
136 | self.bots.iter().filter(|b| b.in_range(c)).count() as u64
137 | }
138 |
139 | fn total_dist(&self, c: &Coordinate) -> i64 {
140 | self.bots.iter().map(|b| b.pos.distance(c) as i64).sum()
141 | }
142 | }
143 |
144 | impl FromStr for Bots {
145 | type Err = Box;
146 |
147 | fn from_str(s: &str) -> Result {
148 | let mut bots: Vec = vec![];
149 | for line in s.lines() {
150 | let bot = line.parse().or_else(|err| {
151 | err!("failed to parse '{:?}': {}", line, err)
152 | })?;
153 | bots.push(bot);
154 | }
155 | if bots.is_empty() {
156 | return err!("found no bots in input");
157 | }
158 | Ok(Bots { bots })
159 | }
160 | }
161 |
162 | #[derive(Clone, Debug)]
163 | struct Bot {
164 | pos: Coordinate,
165 | radius: i64,
166 | }
167 |
168 | impl Bot {
169 | fn in_range_of_bot(&self, other: &Bot) -> bool {
170 | self.pos.distance(&other.pos) <= self.radius
171 | }
172 |
173 | fn in_range(&self, c: &Coordinate) -> bool {
174 | self.pos.distance(c) <= self.radius
175 | }
176 |
177 | fn random_surface_coordinate(&self, mut rng: R) -> Coordinate {
178 | loop {
179 | let (x, y, z): (f64, f64, f64) = rng.gen();
180 | if x == 0.0 && y == 0.0 && z == 0.0 {
181 | continue;
182 | }
183 | let normal = 1.0 / (x*x + y*y + z*z).sqrt();
184 | let (x, y, z) = (x * normal, y * normal, z * normal);
185 | let radius = self.radius as f64;
186 | return Coordinate {
187 | x: (x * radius) as i32,
188 | y: (y * radius) as i32,
189 | z: (z * radius) as i32,
190 | };
191 | }
192 | }
193 | }
194 |
195 | impl FromStr for Bot {
196 | type Err = Box;
197 |
198 | fn from_str(s: &str) -> Result {
199 | lazy_static! {
200 | static ref RE: Regex = Regex::new(r"(?x)
201 | pos=<(?P-?[0-9]+),(?P-?[0-9]+),(?P-?[0-9]+)>,
202 | \s
203 | r=(?P[0-9]+)
204 | ").unwrap();
205 | }
206 |
207 | let caps = match RE.captures(s) {
208 | None => return err!("unrecognized position/radius"),
209 | Some(caps) => caps,
210 | };
211 | let pos = Coordinate {
212 | x: caps["x"].parse()?,
213 | y: caps["y"].parse()?,
214 | z: caps["z"].parse()?,
215 | };
216 | let radius = caps["radius"].parse()?;
217 | Ok(Bot { pos, radius })
218 | }
219 | }
220 |
221 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
222 | struct Coordinate {
223 | x: i32,
224 | y: i32,
225 | z: i32,
226 | }
227 |
228 | impl Coordinate {
229 | fn origin() -> Coordinate {
230 | Coordinate { x: 0, y: 0, z: 0 }
231 | }
232 |
233 | fn distance(&self, other: &Coordinate) -> i64 {
234 | (self.x as i64 - other.x as i64).abs()
235 | + (self.y as i64 - other.y as i64).abs()
236 | + (self.z as i64 - other.z as i64).abs()
237 | }
238 |
239 | fn random(mut rng: R) -> Coordinate {
240 | Coordinate { x: rng.gen(), y: rng.gen(), z: rng.gen() }
241 | }
242 |
243 | fn random_neighbor(&self, mut rng: R) -> Coordinate {
244 | // The commented out lines are for the test input, which has a
245 | // considerably smaller grid.
246 | // let dx = rng.gen_range(-1, 2);
247 | // let dy = rng.gen_range(-1, 2);
248 | // let dz = rng.gen_range(-1, 2);
249 | let dx = rng.gen_range(-10_000, 10_000);
250 | let dy = rng.gen_range(-10_000, 10_000);
251 | let dz = rng.gen_range(-10_000, 10_000);
252 | Coordinate { x: self.x + dx, y: self.y + dy, z: self.z + dz }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/aoc24/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc24"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc24/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc24"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 |
--------------------------------------------------------------------------------
/aoc24/input/input.txt:
--------------------------------------------------------------------------------
1 | Immune System:
2 | 479 units each with 3393 hit points (weak to radiation) with an attack that does 66 cold damage at initiative 8
3 | 2202 units each with 4950 hit points (weak to fire; immune to slashing) with an attack that does 18 cold damage at initiative 2
4 | 8132 units each with 9680 hit points (weak to bludgeoning, fire; immune to slashing) with an attack that does 9 radiation damage at initiative 7
5 | 389 units each with 13983 hit points (immune to bludgeoning) with an attack that does 256 cold damage at initiative 13
6 | 1827 units each with 5107 hit points with an attack that does 24 slashing damage at initiative 18
7 | 7019 units each with 2261 hit points (immune to radiation, slashing, cold) with an attack that does 3 fire damage at initiative 16
8 | 4736 units each with 8421 hit points (weak to cold) with an attack that does 17 slashing damage at initiative 3
9 | 491 units each with 3518 hit points (weak to cold; immune to fire, bludgeoning) with an attack that does 65 radiation damage at initiative 1
10 | 2309 units each with 7353 hit points (immune to radiation) with an attack that does 31 bludgeoning damage at initiative 20
11 | 411 units each with 6375 hit points (immune to slashing; weak to cold, fire) with an attack that does 151 bludgeoning damage at initiative 14
12 |
13 | Infection:
14 | 148 units each with 31914 hit points (immune to radiation, cold, fire; weak to bludgeoning) with an attack that does 416 cold damage at initiative 4
15 | 864 units each with 38189 hit points with an attack that does 72 slashing damage at initiative 6
16 | 2981 units each with 7774 hit points (immune to bludgeoning, cold) with an attack that does 4 fire damage at initiative 15
17 | 5259 units each with 22892 hit points with an attack that does 8 fire damage at initiative 5
18 | 318 units each with 16979 hit points (weak to fire) with an attack that does 106 bludgeoning damage at initiative 9
19 | 5017 units each with 32175 hit points (immune to radiation; weak to slashing) with an attack that does 11 bludgeoning damage at initiative 17
20 | 4308 units each with 14994 hit points (weak to slashing; immune to fire, cold) with an attack that does 5 fire damage at initiative 10
21 | 208 units each with 14322 hit points (weak to radiation) with an attack that does 133 cold damage at initiative 19
22 | 3999 units each with 48994 hit points (weak to cold, slashing) with an attack that does 20 cold damage at initiative 11
23 | 1922 units each with 34406 hit points (weak to slashing) with an attack that does 35 slashing damage at initiative 12
24 |
--------------------------------------------------------------------------------
/aoc24/input/test1.txt:
--------------------------------------------------------------------------------
1 | Immune System:
2 | 17 units each with 5390 hit points (weak to radiation, bludgeoning) with
3 | an attack that does 4507 fire damage at initiative 2
4 | 989 units each with 1274 hit points (immune to fire; weak to bludgeoning,
5 | slashing) with an attack that does 25 slashing damage at initiative 3
6 |
7 | Infection:
8 | 801 units each with 4706 hit points (weak to radiation) with an attack
9 | that does 116 bludgeoning damage at initiative 1
10 | 4485 units each with 2961 hit points (immune to radiation; weak to fire,
11 | cold) with an attack that does 12 slashing damage at initiative 4
12 |
--------------------------------------------------------------------------------
/aoc25/Cargo.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "aoc25"
3 | version = "0.1.0"
4 |
5 |
--------------------------------------------------------------------------------
/aoc25/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "aoc25"
3 | version = "0.1.0"
4 | authors = ["Andrew Gallant "]
5 | edition = "2018"
6 |
--------------------------------------------------------------------------------
/aoc25/input/test1.txt:
--------------------------------------------------------------------------------
1 | 0,0,0,0
2 | 3,0,0,0
3 | 0,3,0,0
4 | 0,0,3,0
5 | 0,0,0,3
6 | 0,0,0,6
7 | 9,0,0,0
8 | 12,0,0,0
9 |
--------------------------------------------------------------------------------
/aoc25/input/test2.txt:
--------------------------------------------------------------------------------
1 | -1,2,2,0
2 | 0,0,2,-2
3 | 0,0,0,-2
4 | -1,2,0,0
5 | -2,-2,-2,2
6 | 3,0,2,-1
7 | -1,3,2,2
8 | -1,0,-1,0
9 | 0,2,1,-2
10 | 3,0,0,0
11 |
--------------------------------------------------------------------------------
/aoc25/input/test3.txt:
--------------------------------------------------------------------------------
1 | 1,-1,0,1
2 | 2,0,-1,0
3 | 3,2,-1,0
4 | 0,0,3,1
5 | 0,0,-1,-1
6 | 2,3,-2,0
7 | -2,2,0,0
8 | 2,-2,0,-1
9 | 1,-1,0,-1
10 | 3,2,0,2
11 |
--------------------------------------------------------------------------------
/aoc25/input/test4.txt:
--------------------------------------------------------------------------------
1 | 1,-1,-1,-2
2 | -2,-2,0,1
3 | 0,2,1,3
4 | -2,3,-2,1
5 | 0,2,3,-2
6 | -1,-1,1,-2
7 | 0,-2,-1,0
8 | -2,2,3,-1
9 | 1,2,2,0
10 | -1,-2,0,-2
11 |
--------------------------------------------------------------------------------
/aoc25/src/main.rs:
--------------------------------------------------------------------------------
1 | use std::error::Error;
2 | use std::i32;
3 | use std::io::{self, Read, Write};
4 | use std::result;
5 | use std::str::{self, FromStr};
6 |
7 | macro_rules! err {
8 | ($($tt:tt)*) => { Err(Box::::from(format!($($tt)*))) }
9 | }
10 |
11 | type Result = result::Result>;
12 |
13 | fn main() -> Result<()> {
14 | let mut input = String::new();
15 | io::stdin().read_to_string(&mut input)?;
16 |
17 | let mut points: Vec = vec![];
18 | for line in input.lines() {
19 | let point = line.parse().or_else(|err| {
20 | err!("failed to parse '{:?}': {}", line, err)
21 | })?;
22 | points.push(point);
23 | }
24 |
25 | part1(&points)?;
26 | Ok(())
27 | }
28 |
29 | fn part1(points: &[Point]) -> Result<()> {
30 | let mut consts = Constellations::shatter_all(points);
31 | while consts.step() {}
32 | writeln!(io::stdout(), "constellations: {}", consts.groups.len())?;
33 | Ok(())
34 | }
35 |
36 | #[derive(Clone, Debug)]
37 | struct Constellations {
38 | groups: Vec,
39 | }
40 |
41 | impl Constellations {
42 | fn shatter_all(points: &[Point]) -> Constellations {
43 | let mut groups = vec![];
44 | for &p in points {
45 | groups.push(Constellation { points: vec![p] });
46 | }
47 | Constellations { groups }
48 | }
49 |
50 | fn step(&mut self) -> bool {
51 | for i in 0..self.groups.len() {
52 | for j in i+1..self.groups.len() {
53 | if self.groups[i].is_connected(&self.groups[j]) {
54 | self.merge(i, j);
55 | return true;
56 | }
57 | }
58 | }
59 | false
60 | }
61 |
62 | fn merge(&mut self, i1: usize, i2: usize) {
63 | let g2 = self.groups.swap_remove(i2);
64 | self.groups[i1].join(&g2);
65 | }
66 | }
67 |
68 | #[derive(Clone, Debug)]
69 | struct Constellation {
70 | points: Vec,
71 | }
72 |
73 | impl Constellation {
74 | fn join(&mut self, other: &Constellation) {
75 | self.points.extend(other.points.iter().cloned());
76 | }
77 |
78 | fn is_connected(&self, other: &Constellation) -> bool {
79 | for p in other.points.iter() {
80 | if self.is_point_connected(p) {
81 | return true;
82 | }
83 | }
84 | false
85 | }
86 |
87 | fn is_point_connected(&self, point: &Point) -> bool {
88 | for p in self.points.iter() {
89 | if point.distance(p) <= 3 {
90 | return true;
91 | }
92 | }
93 | false
94 | }
95 | }
96 |
97 | #[derive(Clone, Copy, Debug)]
98 | struct Point {
99 | x: i32,
100 | y: i32,
101 | z: i32,
102 | t: i32,
103 | }
104 |
105 | impl Point {
106 | fn distance(&self, other: &Point) -> i32 {
107 | (self.x - other.x).abs()
108 | + (self.y - other.y).abs()
109 | + (self.z - other.z).abs()
110 | + (self.t - other.t).abs()
111 | }
112 | }
113 |
114 | impl FromStr for Point {
115 | type Err = Box;
116 |
117 | fn from_str(s: &str) -> Result {
118 | let parts: Vec<&str> = s.trim().split(",").collect();
119 | if parts.len() != 4 {
120 | return err!("unrecognized point '{:?}'", s);
121 | }
122 | Ok(Point {
123 | x: parts[0].parse()?,
124 | y: parts[1].parse()?,
125 | z: parts[2].parse()?,
126 | t: parts[3].parse()?,
127 | })
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/setup-day:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ $# != 1 ]; then
4 | echo "Usage: $(basename "$0") " >&2
5 | exit 1
6 | fi
7 | if [ ! -d .git ]; then
8 | echo "must be run from root of advent-of-code repository" >&2
9 | exit 1
10 | fi
11 |
12 | name="$(printf "aoc%02d" "$1")"
13 | cargo new --bin "$name"
14 | mkdir "$name/input"
15 |
--------------------------------------------------------------------------------