├── .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 | \>+/ 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 | --------------------------------------------------------------------------------