Compare commits

..

23 Commits

Author SHA1 Message Date
48c96ce7ea day24: cheat solution used for part 2 submission 2024-12-11 09:54:10 -08:00
342f4c1883 day25: problem 1 solution - cheated with dot 2023-12-24 22:33:20 -08:00
881d0b08ad day24: problem 1 solution 2023-12-23 22:54:21 -08:00
c1eb7761e3 day23: problem 2 solution 2023-12-23 02:54:07 -08:00
98456ed98d day23: problem 1 solution 2023-12-22 22:33:34 -08:00
d6d4c0d056 gitignore 2023-12-22 20:58:14 -08:00
3bb3b3d6b6 day22: cleanup & nice output 2023-12-22 04:19:58 -08:00
dd91259fe2 day22: problem 2 solution.
no help but annoyingly i misread the hint about low/high and didn't submit my *correct* answer for over an hour while I bug hunted!
2023-12-22 03:47:01 -08:00
8495969877 day22: problem 1 solution 2023-12-22 01:32:08 -08:00
190fc92842 day21: problem 2 solution
not proud of this one either, completely cheated and it does not get
exactly the correct error (it was off by one for my input), but the
quadratic solver on Wolfram Alpha was able to do it.
2023-12-21 03:17:11 -08:00
eb6c1f42cd day 21: problem 1 solution
another unsatisfying one where i needed a visual hint to grok what i
should be doing.

a bunch of graph building stuff that wasn't needed for the part 1
solution at all.
2023-12-21 00:34:08 -08:00
512b05f624 day20: problem 2 solution - needed a hint 2023-12-20 01:48:09 -08:00
877101f9a2 day20: problem 1 solution 2023-12-20 00:13:23 -08:00
cdfecf821c day19: refactoring and cleanup 2023-12-19 02:01:47 -08:00
2b921b5fb2 day19: problem 2 solution 2023-12-19 01:01:36 -08:00
a5dea64b32 day19: problem 1 solution 2023-12-18 22:06:03 -08:00
8200c1a8cf root: add .rustfmt.toml 2023-12-18 02:04:22 -08:00
8d178ddfc6 day18: formatting and cleanup 2023-12-18 02:03:48 -08:00
3bc073f9b8 day18: problem 2 solution 2023-12-18 01:54:31 -08:00
0a9fa8e32f day18: problem 1 solution 2023-12-17 23:04:56 -08:00
e65c17c8e6 day17: don't revisit the start position 2023-12-17 20:55:42 -08:00
3ee26cefe5 day17: super obvious code cleanups, more to do 2023-12-17 15:18:22 -08:00
9e37b2ce66 day17: add heatmap visualization of final path 2023-12-17 15:11:43 -08:00
35 changed files with 6130 additions and 87 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
**/target
input

1
.rustfmt.toml Normal file
View File

@ -0,0 +1 @@
max_width = 120

80
17/Cargo.lock generated
View File

@ -12,46 +12,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "day17"
version = "0.1.0"
dependencies = [
"petgraph",
"itertools",
"termcolor",
"test-case",
]
[[package]]
name = "equivalent"
version = "1.0.1"
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "fixedbitset"
version = "0.4.2"
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
"either",
]
[[package]]
@ -83,6 +61,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
dependencies = [
"winapi-util",
]
[[package]]
name = "test-case"
version = "3.3.1"
@ -121,3 +108,34 @@ name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -6,5 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
petgraph = "0.6.4"
itertools = "0.12.0"
termcolor = "1.4.0"
test-case = "3.3.1"

531
17/src/colormap.rs Normal file
View File

@ -0,0 +1,531 @@
use termcolor::Color;
pub struct ColorMap(&'static [Color]);
#[allow(dead_code)]
pub const COLORMAP_SMOOTH_COOLWARM: ColorMap = ColorMap(&[
Color::Rgb(59, 76, 19),
Color::Rgb(60, 78, 19),
Color::Rgb(61, 80, 19),
Color::Rgb(62, 81, 19),
Color::Rgb(63, 83, 19),
Color::Rgb(64, 85, 20),
Color::Rgb(66, 87, 20),
Color::Rgb(67, 88, 20),
Color::Rgb(68, 90, 20),
Color::Rgb(69, 92, 20),
Color::Rgb(70, 94, 20),
Color::Rgb(72, 95, 20),
Color::Rgb(73, 97, 21),
Color::Rgb(74, 99, 21),
Color::Rgb(75, 100, 21),
Color::Rgb(76, 102, 21),
Color::Rgb(78, 104, 21),
Color::Rgb(79, 106, 21),
Color::Rgb(80, 107, 21),
Color::Rgb(81, 109, 21),
Color::Rgb(83, 111, 22),
Color::Rgb(84, 112, 22),
Color::Rgb(85, 114, 22),
Color::Rgb(86, 116, 22),
Color::Rgb(88, 117, 22),
Color::Rgb(89, 119, 22),
Color::Rgb(90, 120, 22),
Color::Rgb(91, 122, 22),
Color::Rgb(93, 124, 23),
Color::Rgb(94, 125, 23),
Color::Rgb(95, 127, 23),
Color::Rgb(97, 129, 23),
Color::Rgb(98, 130, 23),
Color::Rgb(99, 132, 23),
Color::Rgb(101, 133, 23),
Color::Rgb(102, 135, 23),
Color::Rgb(103, 136, 23),
Color::Rgb(105, 138, 23),
Color::Rgb(106, 140, 24),
Color::Rgb(107, 141, 24),
Color::Rgb(109, 143, 24),
Color::Rgb(110, 144, 24),
Color::Rgb(111, 146, 24),
Color::Rgb(113, 147, 24),
Color::Rgb(114, 149, 24),
Color::Rgb(115, 150, 24),
Color::Rgb(117, 152, 24),
Color::Rgb(118, 153, 24),
Color::Rgb(119, 154, 24),
Color::Rgb(121, 156, 24),
Color::Rgb(122, 157, 24),
Color::Rgb(123, 159, 24),
Color::Rgb(125, 160, 24),
Color::Rgb(126, 162, 25),
Color::Rgb(128, 163, 25),
Color::Rgb(129, 164, 25),
Color::Rgb(130, 166, 25),
Color::Rgb(132, 167, 25),
Color::Rgb(133, 168, 25),
Color::Rgb(134, 170, 25),
Color::Rgb(136, 171, 25),
Color::Rgb(137, 172, 25),
Color::Rgb(139, 174, 25),
Color::Rgb(140, 175, 25),
Color::Rgb(141, 176, 25),
Color::Rgb(143, 177, 25),
Color::Rgb(144, 178, 25),
Color::Rgb(146, 180, 25),
Color::Rgb(147, 181, 25),
Color::Rgb(148, 182, 25),
Color::Rgb(150, 183, 25),
Color::Rgb(151, 184, 25),
Color::Rgb(153, 186, 25),
Color::Rgb(154, 187, 25),
Color::Rgb(155, 188, 25),
Color::Rgb(157, 189, 25),
Color::Rgb(158, 190, 25),
Color::Rgb(159, 191, 25),
Color::Rgb(161, 192, 25),
Color::Rgb(162, 193, 25),
Color::Rgb(163, 194, 25),
Color::Rgb(165, 195, 25),
Color::Rgb(166, 196, 25),
Color::Rgb(168, 197, 25),
Color::Rgb(169, 198, 25),
Color::Rgb(170, 199, 25),
Color::Rgb(172, 200, 25),
Color::Rgb(173, 201, 25),
Color::Rgb(174, 201, 25),
Color::Rgb(176, 202, 25),
Color::Rgb(177, 203, 25),
Color::Rgb(178, 204, 25),
Color::Rgb(180, 205, 25),
Color::Rgb(181, 206, 25),
Color::Rgb(182, 206, 25),
Color::Rgb(183, 207, 24),
Color::Rgb(185, 208, 24),
Color::Rgb(186, 209, 24),
Color::Rgb(187, 209, 24),
Color::Rgb(189, 210, 24),
Color::Rgb(190, 211, 24),
Color::Rgb(191, 211, 24),
Color::Rgb(192, 212, 24),
Color::Rgb(193, 212, 24),
Color::Rgb(195, 213, 24),
Color::Rgb(196, 214, 24),
Color::Rgb(197, 214, 24),
Color::Rgb(198, 215, 24),
Color::Rgb(200, 215, 24),
Color::Rgb(201, 216, 24),
Color::Rgb(202, 216, 23),
Color::Rgb(203, 216, 23),
Color::Rgb(204, 217, 23),
Color::Rgb(205, 217, 23),
Color::Rgb(206, 218, 23),
Color::Rgb(208, 218, 23),
Color::Rgb(209, 218, 23),
Color::Rgb(210, 219, 23),
Color::Rgb(211, 219, 23),
Color::Rgb(212, 219, 23),
Color::Rgb(213, 219, 22),
Color::Rgb(214, 220, 22),
Color::Rgb(215, 220, 22),
Color::Rgb(216, 220, 22),
Color::Rgb(217, 220, 22),
Color::Rgb(218, 220, 22),
Color::Rgb(219, 221, 22),
Color::Rgb(220, 221, 22),
Color::Rgb(221, 220, 22),
Color::Rgb(222, 220, 21),
Color::Rgb(223, 220, 21),
Color::Rgb(225, 219, 21),
Color::Rgb(226, 218, 21),
Color::Rgb(227, 218, 21),
Color::Rgb(228, 217, 21),
Color::Rgb(229, 217, 21),
Color::Rgb(229, 216, 20),
Color::Rgb(230, 216, 20),
Color::Rgb(231, 215, 20),
Color::Rgb(232, 214, 20),
Color::Rgb(233, 214, 20),
Color::Rgb(234, 213, 20),
Color::Rgb(235, 212, 20),
Color::Rgb(235, 211, 19),
Color::Rgb(236, 211, 19),
Color::Rgb(237, 210, 19),
Color::Rgb(238, 209, 19),
Color::Rgb(238, 208, 19),
Color::Rgb(239, 207, 19),
Color::Rgb(239, 206, 18),
Color::Rgb(240, 206, 18),
Color::Rgb(241, 205, 18),
Color::Rgb(241, 204, 18),
Color::Rgb(242, 203, 18),
Color::Rgb(242, 202, 18),
Color::Rgb(243, 201, 18),
Color::Rgb(243, 200, 17),
Color::Rgb(244, 199, 17),
Color::Rgb(244, 198, 17),
Color::Rgb(244, 197, 17),
Color::Rgb(245, 196, 17),
Color::Rgb(245, 195, 17),
Color::Rgb(245, 193, 16),
Color::Rgb(246, 192, 16),
Color::Rgb(246, 191, 16),
Color::Rgb(246, 190, 16),
Color::Rgb(246, 189, 16),
Color::Rgb(247, 188, 16),
Color::Rgb(247, 186, 15),
Color::Rgb(247, 185, 15),
Color::Rgb(247, 184, 15),
Color::Rgb(247, 183, 15),
Color::Rgb(247, 181, 15),
Color::Rgb(247, 180, 15),
Color::Rgb(247, 179, 15),
Color::Rgb(247, 177, 14),
Color::Rgb(247, 176, 14),
Color::Rgb(247, 175, 14),
Color::Rgb(247, 173, 14),
Color::Rgb(247, 172, 14),
Color::Rgb(247, 171, 14),
Color::Rgb(247, 169, 13),
Color::Rgb(247, 168, 13),
Color::Rgb(247, 166, 13),
Color::Rgb(246, 165, 13),
Color::Rgb(246, 163, 13),
Color::Rgb(246, 162, 13),
Color::Rgb(246, 160, 12),
Color::Rgb(245, 159, 12),
Color::Rgb(245, 157, 12),
Color::Rgb(245, 156, 12),
Color::Rgb(244, 154, 12),
Color::Rgb(244, 153, 12),
Color::Rgb(244, 151, 12),
Color::Rgb(243, 149, 11),
Color::Rgb(243, 148, 11),
Color::Rgb(242, 146, 11),
Color::Rgb(242, 144, 11),
Color::Rgb(241, 143, 11),
Color::Rgb(241, 141, 11),
Color::Rgb(240, 139, 11),
Color::Rgb(240, 138, 10),
Color::Rgb(239, 136, 10),
Color::Rgb(238, 134, 10),
Color::Rgb(238, 133, 10),
Color::Rgb(237, 131, 10),
Color::Rgb(237, 129, 10),
Color::Rgb(236, 127, 99),
Color::Rgb(235, 125, 98),
Color::Rgb(234, 124, 96),
Color::Rgb(234, 122, 95),
Color::Rgb(233, 120, 93),
Color::Rgb(232, 118, 92),
Color::Rgb(231, 116, 91),
Color::Rgb(230, 114, 89),
Color::Rgb(229, 112, 88),
Color::Rgb(229, 111, 86),
Color::Rgb(228, 109, 85),
Color::Rgb(227, 107, 84),
Color::Rgb(226, 105, 82),
Color::Rgb(225, 103, 81),
Color::Rgb(224, 101, 79),
Color::Rgb(223, 99, 78),
Color::Rgb(222, 97, 77),
Color::Rgb(221, 95, 75),
Color::Rgb(220, 93, 74),
Color::Rgb(219, 91, 73),
Color::Rgb(218, 89, 71),
Color::Rgb(216, 86, 70),
Color::Rgb(215, 84, 69),
Color::Rgb(214, 82, 68),
Color::Rgb(213, 80, 66),
Color::Rgb(212, 78, 65),
Color::Rgb(211, 76, 64),
Color::Rgb(209, 73, 62),
Color::Rgb(208, 71, 61),
Color::Rgb(207, 69, 60),
Color::Rgb(206, 67, 59),
Color::Rgb(204, 64, 57),
Color::Rgb(203, 62, 56),
Color::Rgb(202, 59, 55),
Color::Rgb(200, 57, 54),
Color::Rgb(199, 54, 53),
Color::Rgb(198, 52, 52),
Color::Rgb(196, 49, 50),
Color::Rgb(195, 46, 49),
Color::Rgb(193, 43, 48),
Color::Rgb(192, 40, 47),
Color::Rgb(191, 37, 46),
Color::Rgb(189, 34, 45),
Color::Rgb(188, 30, 44),
Color::Rgb(186, 26, 43),
Color::Rgb(185, 22, 41),
Color::Rgb(183, 17, 40),
Color::Rgb(182, 11, 39),
Color::Rgb(180, 4, 38),
]);
#[allow(dead_code)]
pub const COLORMAP_INFERNO: ColorMap = ColorMap(&[
Color::Rgb(0, 0, 4),
Color::Rgb(1, 0, 5),
Color::Rgb(1, 1, 6),
Color::Rgb(1, 1, 8),
Color::Rgb(2, 1, 10),
Color::Rgb(2, 2, 12),
Color::Rgb(2, 2, 14),
Color::Rgb(3, 2, 16),
Color::Rgb(4, 3, 18),
Color::Rgb(4, 3, 20),
Color::Rgb(5, 4, 23),
Color::Rgb(6, 4, 25),
Color::Rgb(7, 5, 27),
Color::Rgb(8, 5, 29),
Color::Rgb(9, 6, 31),
Color::Rgb(10, 7, 34),
Color::Rgb(11, 7, 36),
Color::Rgb(12, 8, 38),
Color::Rgb(13, 8, 41),
Color::Rgb(14, 9, 43),
Color::Rgb(16, 9, 45),
Color::Rgb(17, 10, 48),
Color::Rgb(18, 10, 50),
Color::Rgb(20, 11, 52),
Color::Rgb(21, 11, 55),
Color::Rgb(22, 11, 57),
Color::Rgb(24, 12, 60),
Color::Rgb(25, 12, 62),
Color::Rgb(27, 12, 65),
Color::Rgb(28, 12, 67),
Color::Rgb(30, 12, 69),
Color::Rgb(31, 12, 72),
Color::Rgb(33, 12, 74),
Color::Rgb(35, 12, 76),
Color::Rgb(36, 12, 79),
Color::Rgb(38, 12, 81),
Color::Rgb(40, 11, 83),
Color::Rgb(41, 11, 85),
Color::Rgb(43, 11, 87),
Color::Rgb(45, 11, 89),
Color::Rgb(47, 10, 91),
Color::Rgb(49, 10, 92),
Color::Rgb(50, 10, 94),
Color::Rgb(52, 10, 95),
Color::Rgb(54, 9, 97),
Color::Rgb(56, 9, 98),
Color::Rgb(57, 9, 99),
Color::Rgb(59, 9, 100),
Color::Rgb(61, 9, 101),
Color::Rgb(62, 9, 102),
Color::Rgb(64, 10, 103),
Color::Rgb(66, 10, 104),
Color::Rgb(68, 10, 104),
Color::Rgb(69, 10, 105),
Color::Rgb(71, 11, 106),
Color::Rgb(73, 11, 106),
Color::Rgb(74, 12, 107),
Color::Rgb(76, 12, 107),
Color::Rgb(77, 13, 108),
Color::Rgb(79, 13, 108),
Color::Rgb(81, 14, 108),
Color::Rgb(82, 14, 109),
Color::Rgb(84, 15, 109),
Color::Rgb(85, 15, 109),
Color::Rgb(87, 16, 110),
Color::Rgb(89, 16, 110),
Color::Rgb(90, 17, 110),
Color::Rgb(92, 18, 110),
Color::Rgb(93, 18, 110),
Color::Rgb(95, 19, 110),
Color::Rgb(97, 19, 110),
Color::Rgb(98, 20, 110),
Color::Rgb(100, 21, 110),
Color::Rgb(101, 21, 110),
Color::Rgb(103, 22, 110),
Color::Rgb(105, 22, 110),
Color::Rgb(106, 23, 110),
Color::Rgb(108, 24, 110),
Color::Rgb(109, 24, 110),
Color::Rgb(111, 25, 110),
Color::Rgb(113, 25, 110),
Color::Rgb(114, 26, 110),
Color::Rgb(116, 26, 110),
Color::Rgb(117, 27, 110),
Color::Rgb(119, 28, 109),
Color::Rgb(120, 28, 109),
Color::Rgb(122, 29, 109),
Color::Rgb(124, 29, 109),
Color::Rgb(125, 30, 109),
Color::Rgb(127, 30, 108),
Color::Rgb(128, 31, 108),
Color::Rgb(130, 32, 108),
Color::Rgb(132, 32, 107),
Color::Rgb(133, 33, 107),
Color::Rgb(135, 33, 107),
Color::Rgb(136, 34, 106),
Color::Rgb(138, 34, 106),
Color::Rgb(140, 35, 105),
Color::Rgb(141, 35, 105),
Color::Rgb(143, 36, 105),
Color::Rgb(144, 37, 104),
Color::Rgb(146, 37, 104),
Color::Rgb(147, 38, 103),
Color::Rgb(149, 38, 103),
Color::Rgb(151, 39, 102),
Color::Rgb(152, 39, 102),
Color::Rgb(154, 40, 101),
Color::Rgb(155, 41, 100),
Color::Rgb(157, 41, 100),
Color::Rgb(159, 42, 99),
Color::Rgb(160, 42, 99),
Color::Rgb(162, 43, 98),
Color::Rgb(163, 44, 97),
Color::Rgb(165, 44, 96),
Color::Rgb(166, 45, 96),
Color::Rgb(168, 46, 95),
Color::Rgb(169, 46, 94),
Color::Rgb(171, 47, 94),
Color::Rgb(173, 48, 93),
Color::Rgb(174, 48, 92),
Color::Rgb(176, 49, 91),
Color::Rgb(177, 50, 90),
Color::Rgb(179, 50, 90),
Color::Rgb(180, 51, 89),
Color::Rgb(182, 52, 88),
Color::Rgb(183, 53, 87),
Color::Rgb(185, 53, 86),
Color::Rgb(186, 54, 85),
Color::Rgb(188, 55, 84),
Color::Rgb(189, 56, 83),
Color::Rgb(191, 57, 82),
Color::Rgb(192, 58, 81),
Color::Rgb(193, 58, 80),
Color::Rgb(195, 59, 79),
Color::Rgb(196, 60, 78),
Color::Rgb(198, 61, 77),
Color::Rgb(199, 62, 76),
Color::Rgb(200, 63, 75),
Color::Rgb(202, 64, 74),
Color::Rgb(203, 65, 73),
Color::Rgb(204, 66, 72),
Color::Rgb(206, 67, 71),
Color::Rgb(207, 68, 70),
Color::Rgb(208, 69, 69),
Color::Rgb(210, 70, 68),
Color::Rgb(211, 71, 67),
Color::Rgb(212, 72, 66),
Color::Rgb(213, 74, 65),
Color::Rgb(215, 75, 63),
Color::Rgb(216, 76, 62),
Color::Rgb(217, 77, 61),
Color::Rgb(218, 78, 60),
Color::Rgb(219, 80, 59),
Color::Rgb(221, 81, 58),
Color::Rgb(222, 82, 56),
Color::Rgb(223, 83, 55),
Color::Rgb(224, 85, 54),
Color::Rgb(225, 86, 53),
Color::Rgb(226, 87, 52),
Color::Rgb(227, 89, 51),
Color::Rgb(228, 90, 49),
Color::Rgb(229, 92, 48),
Color::Rgb(230, 93, 47),
Color::Rgb(231, 94, 46),
Color::Rgb(232, 96, 45),
Color::Rgb(233, 97, 43),
Color::Rgb(234, 99, 42),
Color::Rgb(235, 100, 41),
Color::Rgb(235, 102, 40),
Color::Rgb(236, 103, 38),
Color::Rgb(237, 105, 37),
Color::Rgb(238, 106, 36),
Color::Rgb(239, 108, 35),
Color::Rgb(239, 110, 33),
Color::Rgb(240, 111, 32),
Color::Rgb(241, 113, 31),
Color::Rgb(241, 115, 29),
Color::Rgb(242, 116, 28),
Color::Rgb(243, 118, 27),
Color::Rgb(243, 120, 25),
Color::Rgb(244, 121, 24),
Color::Rgb(245, 123, 23),
Color::Rgb(245, 125, 21),
Color::Rgb(246, 126, 20),
Color::Rgb(246, 128, 19),
Color::Rgb(247, 130, 18),
Color::Rgb(247, 132, 16),
Color::Rgb(248, 133, 15),
Color::Rgb(248, 135, 14),
Color::Rgb(248, 137, 12),
Color::Rgb(249, 139, 11),
Color::Rgb(249, 140, 10),
Color::Rgb(249, 142, 9),
Color::Rgb(250, 144, 8),
Color::Rgb(250, 146, 7),
Color::Rgb(250, 148, 7),
Color::Rgb(251, 150, 6),
Color::Rgb(251, 151, 6),
Color::Rgb(251, 153, 6),
Color::Rgb(251, 155, 6),
Color::Rgb(251, 157, 7),
Color::Rgb(252, 159, 7),
Color::Rgb(252, 161, 8),
Color::Rgb(252, 163, 9),
Color::Rgb(252, 165, 10),
Color::Rgb(252, 166, 12),
Color::Rgb(252, 168, 13),
Color::Rgb(252, 170, 15),
Color::Rgb(252, 172, 17),
Color::Rgb(252, 174, 18),
Color::Rgb(252, 176, 20),
Color::Rgb(252, 178, 22),
Color::Rgb(252, 180, 24),
Color::Rgb(251, 182, 26),
Color::Rgb(251, 184, 29),
Color::Rgb(251, 186, 31),
Color::Rgb(251, 188, 33),
Color::Rgb(251, 190, 35),
Color::Rgb(250, 192, 38),
Color::Rgb(250, 194, 40),
Color::Rgb(250, 196, 42),
Color::Rgb(250, 198, 45),
Color::Rgb(249, 199, 47),
Color::Rgb(249, 201, 50),
Color::Rgb(249, 203, 53),
Color::Rgb(248, 205, 55),
Color::Rgb(248, 207, 58),
Color::Rgb(247, 209, 61),
Color::Rgb(247, 211, 64),
Color::Rgb(246, 213, 67),
Color::Rgb(246, 215, 70),
Color::Rgb(245, 217, 73),
Color::Rgb(245, 219, 76),
Color::Rgb(244, 221, 79),
Color::Rgb(244, 223, 83),
Color::Rgb(244, 225, 86),
Color::Rgb(243, 227, 90),
Color::Rgb(243, 229, 93),
Color::Rgb(242, 230, 97),
Color::Rgb(242, 232, 101),
Color::Rgb(242, 234, 105),
Color::Rgb(241, 236, 109),
Color::Rgb(241, 237, 113),
Color::Rgb(241, 239, 117),
Color::Rgb(241, 241, 121),
Color::Rgb(242, 242, 125),
Color::Rgb(242, 244, 130),
Color::Rgb(243, 245, 134),
Color::Rgb(243, 246, 138),
Color::Rgb(244, 248, 142),
Color::Rgb(245, 249, 146),
Color::Rgb(246, 250, 150),
Color::Rgb(248, 251, 154),
Color::Rgb(249, 252, 157),
Color::Rgb(250, 253, 161),
Color::Rgb(252, 255, 164),
]);
impl ColorMap {
pub fn apply(&self, val: f64) -> Color {
assert!(val >= 0.0 && val <= 1.0);
let position = (val * 255.0).ceil() as usize;
self.0[position]
}
}

View File

@ -1,13 +1,15 @@
use colormap::ColorMap;
use std::collections::hash_map::RandomState;
use std::collections::{BinaryHeap, HashMap};
use std::fmt::{Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::io::{BufRead, BufReader, Lines, Write};
use std::iter::repeat;
use std::time::Instant;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
use petgraph::algo::dijkstra;
use petgraph::prelude::*;
mod colormap;
const COLORMAP: &ColorMap = &colormap::COLORMAP_INFERNO;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
@ -30,7 +32,10 @@ fn main() {
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
// DATA
const UNPATH_CHAR: char = '█';
const UNVISITED_CHAR: char = ' ';
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
enum Direction {
@ -59,6 +64,17 @@ impl Direction {
}
}
impl From<&Direction> for char {
fn from(dir: &Direction) -> Self {
match dir {
Direction::Left => '←',
Direction::Right => '→',
Direction::Up => '↑',
Direction::Down => '↓',
}
}
}
struct CityMap {
map: Vec<Vec<u64>>,
}
@ -73,6 +89,25 @@ impl CityMap {
_ => None,
}
}
#[allow(dead_code)]
fn print(&self) -> Result<(), Box<dyn std::error::Error>> {
let cost_max = *self.map.iter().flat_map(|row| row.iter()).max().unwrap() as f64;
let mut so_lock = StandardStream::stdout(ColorChoice::Always);
for y in 0..self.map.len() {
for val in &self.map[y] {
so_lock.set_color(
ColorSpec::new()
.set_bg(Some(COLORMAP.apply(*val as f64 / cost_max)))
.set_fg(Some(Color::Black)),
)?;
so_lock.write_fmt(format_args!("{}", val))?;
}
so_lock.reset()?;
writeln!(so_lock)?;
}
Ok(())
}
}
type Position = (usize, usize);
@ -80,7 +115,6 @@ type Position = (usize, usize);
struct WalkCost<'a> {
start: Position,
cost_from: Vec<Vec<HashMap<(Direction, usize), u64>>>,
dir_to: HashMap<Position, Direction>,
map: &'a CityMap,
}
@ -122,7 +156,6 @@ impl<'a> WalkCost<'a> {
.iter()
.map(|row| repeat(HashMap::new()).take(row.len()).collect())
.collect(),
dir_to: HashMap::new(),
}
}
fn compute(&mut self) {
@ -195,38 +228,6 @@ impl<'a> WalkCost<'a> {
}
}
}
fn shortest_path_to(&self, to: Position) -> Vec<(Position, Direction)> {
let mut path = Vec::new();
let mut cur_pos = to;
// start at the end, walk backwards
while cur_pos != self.start {
let dir = self.dir_to.get(&cur_pos).unwrap();
path.push((cur_pos, *dir));
cur_pos = self.map.offset_pos(cur_pos, &dir.opposite()).unwrap();
}
path
}
fn print_shortest_path(&self, to: Position) {
let path = self.shortest_path_to(to);
let map: HashMap<_, _, RandomState> = HashMap::from_iter(path.into_iter());
// for y in 0..self.cost_to.len() {
// for x in 0..self.map.map[y].len() {
// if let Some(to_dir) = map.get(&(x, y)) {
// let c = match to_dir {
// Direction::Left => '<',
// Direction::Right => '>',
// Direction::Up => '^',
// Direction::Down => 'v',
// };
// print!("{}", c);
// } else {
// print!("{}", self.map.map[y][x]);
// }
// }
// println!();
// }
}
}
struct WalkCost2<'a> {
@ -247,8 +248,10 @@ impl<'a> WalkCost2<'a> {
.collect(),
}
}
fn min_cost_at(&self, pos: Position) -> Option<&u64> {
self.cost_from[pos.1][pos.0].values().min()
}
fn compute(&mut self, to: Position) {
println!("computing to: {:?}", to);
let mut unvisited_next_moves: BinaryHeap<Move> = BinaryHeap::new();
let valid_start_moves: Vec<Move> = Direction::all()
.iter()
@ -308,6 +311,7 @@ impl<'a> WalkCost2<'a> {
weight: cur_move.weight + self.map.map[new_pos.1][new_pos.0],
})
})
.filter(|m| m.new_pos != self.start)
.filter(|m| *m.dir != cur_move.dir.opposite())
.filter(|m| {
if m.dir == cur_move.dir {
@ -324,7 +328,7 @@ impl<'a> WalkCost2<'a> {
.unwrap_or(&u64::MAX)
})
}); // valid positions
// println!("valid moves with {:?}", cur_move);
// println!("valid moves with {:?}", cur_move);
for next_move in valid_moves {
// println!(" {:?}", next_move);
unvisited_next_moves.push(next_move);
@ -332,37 +336,63 @@ impl<'a> WalkCost2<'a> {
}
}
fn shortest_path_to(&self, to: Position) -> Vec<(Position, Direction)> {
let mut path = Vec::new();
let mut cur_pos = to;
// start at the end, walk backwards
while cur_pos != self.start {
let (m, val) = self.cost_from[cur_pos.1][cur_pos.0].iter().min_by(|a, b| a.1.cmp(b.1)).unwrap();
let (m, _val) = self.cost_from[cur_pos.1][cur_pos.0]
.iter()
.min_by(|a, b| a.1.cmp(b.1))
.unwrap();
path.push((cur_pos, m.0));
cur_pos = self.map.offset_pos(cur_pos, &m.0.opposite()).unwrap();
}
path
}
fn print_shortest_path(&self, to: Position) {
fn print_path(&self, to: Position) -> Result<(), Box<dyn std::error::Error>> {
let path = self.shortest_path_to(to);
let map: HashMap<_, _, RandomState> = HashMap::from_iter(path.into_iter());
let cost_max_of_min = *self
.cost_from
.iter()
.flat_map(|row| row.iter().filter_map(|cell| cell.values().min()))
.max()
.unwrap() as f64;
let mut so_lock = StandardStream::stdout(ColorChoice::Always);
for y in 0..self.cost_from.len() {
for x in 0..self.map.map[y].len() {
if let Some(to_dir) = map.get(&(x, y)) {
let c = match to_dir {
Direction::Left => '<',
Direction::Right => '>',
Direction::Up => '^',
Direction::Down => 'v',
let mut color = ColorSpec::new();
let c = if let Some(to_dir) = map.get(&(x, y)) {
let normalized_cost =
*self.min_cost_at((x, y)).unwrap() as f64 / cost_max_of_min;
let bg_color = COLORMAP.apply(normalized_cost);
let fg_color = if let Color::Rgb(r, g, b) = bg_color {
Color::Rgb(255 - r, 255 - g, 255 - b)
} else {
Color::Black
};
print!("{}", c);
color.set_fg(Some(fg_color)).set_bg(Some(bg_color)).bold();
to_dir.into()
} else {
print!("{}", self.map.map[y][x]);
}
if let Some(cost) = &self.min_cost_at((x, y)) {
color.set_fg(Some(COLORMAP.apply(**cost as f64 / cost_max_of_min)));
UNPATH_CHAR
} else {
color.set_fg(Some(Color::Black));
UNVISITED_CHAR
}
};
so_lock.set_color(&color)?;
let mut char_buf = [0u8; 4];
c.encode_utf8(&mut char_buf);
so_lock.write_all(&char_buf)?;
}
println!();
so_lock.reset()?;
writeln!(so_lock)?;
}
Ok(())
}
}
@ -395,11 +425,14 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let map = CityMap::from(input);
// map.print().unwrap();
let mut costs = WalkCost2::from_map(&map, (0, 0));
costs.compute((map.map[0].len() - 1, map.map.len() - 1));
// println!("{}", costs);
costs.print_shortest_path((costs.cost_from[0].len() - 1, costs.cost_from.len() - 1));
costs
.print_path((costs.cost_from[0].len() - 1, costs.cost_from.len() - 1))
.unwrap();
*costs.cost_from[costs.cost_from.len() - 1][costs.cost_from[0].len() - 1]
.values()

25
18/Cargo.lock generated Normal file
View File

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "day18"
version = "0.1.0"
dependencies = [
"itertools",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]

9
18/Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "day18"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"

640
18/input Normal file
View File

@ -0,0 +1,640 @@
L 4 (#248a72)
U 3 (#295c13)
L 4 (#49e802)
U 5 (#5ff8b3)
R 3 (#0df642)
U 2 (#15b973)
R 5 (#6cfb92)
U 11 (#4e3cd3)
L 4 (#422dd0)
D 5 (#303fe3)
L 7 (#6225e0)
D 2 (#58bd63)
L 9 (#6225e2)
D 8 (#2b1703)
L 6 (#422dd2)
D 9 (#2eb9b3)
L 2 (#10f270)
D 10 (#0677b3)
L 4 (#190450)
U 11 (#55b4e3)
L 4 (#361732)
U 3 (#034663)
L 4 (#37f902)
U 6 (#19bb73)
R 8 (#6e1030)
U 8 (#4cad93)
L 4 (#2509d0)
U 6 (#1e1e61)
L 5 (#3d3b90)
U 8 (#28ec81)
L 2 (#3f9200)
U 5 (#437871)
L 6 (#174bc0)
D 5 (#3b58a1)
L 11 (#064a60)
U 5 (#539da3)
L 4 (#1ea0d0)
D 7 (#1909b3)
L 2 (#296a52)
D 9 (#6780c3)
L 3 (#296a50)
U 11 (#356893)
L 2 (#0eb430)
U 5 (#019203)
L 5 (#4eea10)
U 4 (#436533)
L 4 (#20aeb0)
U 4 (#2fb883)
L 6 (#5046c0)
U 11 (#392293)
R 8 (#2567a0)
U 3 (#57ee03)
R 12 (#270540)
U 2 (#358173)
R 6 (#289e40)
U 2 (#77b991)
R 4 (#016c10)
U 11 (#351091)
R 2 (#714f90)
U 6 (#4add71)
L 11 (#00eab0)
D 5 (#2f8361)
L 8 (#192200)
U 5 (#1416c1)
L 13 (#775d30)
U 3 (#3d3d63)
L 8 (#6e4710)
U 3 (#28cfd3)
L 8 (#389880)
U 5 (#28cfd1)
L 7 (#4cd750)
D 5 (#1d56e3)
L 3 (#2789e2)
D 5 (#5d7573)
R 9 (#1b1372)
D 5 (#1965a3)
R 4 (#111052)
D 2 (#5a2861)
R 10 (#37f1c2)
D 3 (#0b7ad1)
R 5 (#012162)
U 5 (#226f43)
R 4 (#583cd2)
D 5 (#4333f3)
R 5 (#0eb952)
D 3 (#22a273)
L 2 (#34c5d0)
D 3 (#355f11)
L 5 (#13a460)
D 4 (#355f13)
L 6 (#3e2900)
U 4 (#28d423)
L 8 (#042432)
D 3 (#4378f3)
L 6 (#18a200)
D 5 (#16a843)
L 5 (#18a202)
D 7 (#46fdd3)
L 5 (#2a4802)
D 5 (#2a4d23)
L 5 (#6049d2)
D 4 (#0a4023)
L 4 (#09afb2)
U 4 (#687713)
L 5 (#4b85f2)
D 4 (#0e1a53)
L 8 (#331b60)
U 6 (#270d83)
L 6 (#331b62)
U 6 (#25dba3)
L 6 (#412e82)
D 6 (#5e2e73)
L 8 (#1de802)
U 6 (#1e7143)
L 4 (#4e7e82)
U 3 (#160d63)
L 5 (#19d230)
U 3 (#36a4a3)
R 3 (#19d232)
U 6 (#4c63b3)
R 10 (#0d2862)
U 5 (#0ec351)
R 11 (#593c62)
U 6 (#12de21)
R 2 (#08d482)
U 4 (#277081)
R 3 (#10eee0)
U 4 (#382b91)
L 4 (#512200)
U 7 (#1279f1)
L 3 (#229312)
U 6 (#422721)
R 6 (#71ddd2)
U 6 (#093871)
R 13 (#0d5622)
D 5 (#093873)
R 7 (#4c34e2)
U 6 (#5b1743)
R 2 (#4779a2)
D 6 (#1cc6e3)
R 7 (#3c13a0)
U 5 (#3b60f3)
R 4 (#3c13a2)
U 9 (#229f83)
R 3 (#0a6ff2)
D 2 (#06d231)
R 8 (#0298b2)
D 11 (#4eb6a1)
R 7 (#0298b0)
U 8 (#438ce1)
R 7 (#0e69e2)
U 5 (#2d3e73)
R 8 (#40b2c2)
U 6 (#64cb53)
L 2 (#40b2c0)
U 3 (#0215c3)
L 3 (#2cbf42)
U 4 (#00c523)
L 7 (#1fc3d2)
D 4 (#68a633)
L 5 (#1fc3d0)
U 5 (#149853)
L 5 (#2cbf40)
U 9 (#0b9613)
L 3 (#1e7f00)
U 8 (#2fc533)
L 7 (#3d7180)
U 3 (#33f3a3)
R 9 (#66d340)
U 2 (#0fa1f1)
R 4 (#0546c0)
D 6 (#4c00b3)
R 8 (#483b50)
U 6 (#4c00b1)
R 4 (#2fc7c0)
U 2 (#0fa1f3)
R 7 (#377280)
U 6 (#200c43)
R 5 (#429ef0)
U 3 (#3a90f1)
R 5 (#226142)
U 4 (#2740c1)
R 5 (#41ac62)
U 3 (#275423)
R 2 (#3ba072)
U 11 (#275421)
R 4 (#27ea92)
D 8 (#402001)
R 2 (#3456c0)
D 6 (#5a4541)
R 3 (#4d1400)
U 4 (#5a4543)
R 6 (#462de0)
U 8 (#3fcdb1)
R 6 (#1f2d90)
U 6 (#4e7851)
R 3 (#41eb30)
D 6 (#4e7853)
R 4 (#1aa1c0)
D 10 (#0a4073)
R 6 (#2134b2)
D 7 (#164e43)
R 4 (#200c72)
D 5 (#3beca3)
R 6 (#7445e0)
U 3 (#2cda83)
R 4 (#7445e2)
U 3 (#475553)
L 5 (#200c70)
U 10 (#23fb33)
R 5 (#2134b0)
U 7 (#3c98d3)
L 4 (#4cfc80)
U 5 (#0f0e71)
R 8 (#3c1d22)
U 10 (#23a311)
R 6 (#05da32)
D 5 (#6229c1)
R 5 (#41f750)
D 8 (#1f4191)
R 3 (#3530f0)
D 13 (#4d3941)
R 3 (#6215e0)
D 9 (#2fe911)
R 6 (#05cf90)
U 4 (#25c901)
R 2 (#335232)
U 12 (#271371)
R 2 (#335230)
U 5 (#2bd1d1)
L 9 (#1ffad0)
U 6 (#6f6bc1)
R 9 (#099600)
U 5 (#13bee1)
R 3 (#5aa820)
U 3 (#3546c1)
R 6 (#4dd5f0)
D 11 (#495be1)
R 6 (#60c8e0)
U 11 (#4bd201)
R 4 (#367f30)
D 4 (#43dbc1)
R 6 (#4bb740)
U 12 (#07ce11)
R 6 (#4b4d70)
D 12 (#3e29e1)
R 5 (#313fc0)
U 4 (#293d51)
R 4 (#1b4860)
U 11 (#514571)
R 4 (#5c11a2)
U 4 (#444841)
R 8 (#5c11a0)
U 2 (#3e6c51)
R 11 (#16dab0)
U 7 (#26baf1)
R 2 (#32e240)
U 5 (#26baf3)
R 6 (#2f0a70)
U 6 (#11a821)
R 3 (#527fc0)
U 7 (#468b31)
R 6 (#0641a0)
U 10 (#20f351)
R 7 (#547a90)
U 12 (#1dbf11)
R 2 (#17d0d2)
U 10 (#159981)
R 6 (#634e12)
U 5 (#4ff021)
R 10 (#006122)
D 6 (#6589a3)
R 3 (#1a74f2)
D 2 (#19b911)
R 12 (#3ed5f2)
D 5 (#28ab31)
R 3 (#09dd82)
D 5 (#614c01)
R 6 (#284d32)
D 9 (#06b521)
R 8 (#4dfad2)
D 7 (#5b0f61)
R 2 (#0fff22)
D 5 (#1e6ff1)
R 2 (#47b352)
D 9 (#29c291)
R 5 (#352ef2)
D 3 (#042d11)
R 4 (#262542)
D 7 (#042d13)
R 4 (#21e8f2)
D 3 (#18cc21)
R 3 (#16ed70)
U 9 (#270ea1)
R 7 (#4391e0)
U 9 (#270ea3)
R 4 (#22bdd0)
D 3 (#3b72b1)
R 7 (#1fd4b2)
D 8 (#01bfe1)
L 7 (#3d18a2)
D 7 (#20dac1)
R 4 (#67d990)
D 4 (#1acd93)
L 7 (#017410)
D 2 (#6e4923)
L 7 (#10bdd0)
D 13 (#0650b3)
L 2 (#072660)
U 13 (#221151)
L 6 (#41d720)
D 3 (#5bc831)
L 4 (#252450)
D 9 (#118de1)
R 6 (#05b1d0)
D 4 (#247721)
R 9 (#2b6b10)
U 4 (#60da41)
R 10 (#470c42)
D 3 (#4fdc91)
R 2 (#470c40)
D 8 (#189db1)
L 5 (#4a1f70)
D 7 (#07e141)
L 3 (#2524b0)
D 5 (#2a6ce1)
L 11 (#1a2e50)
D 2 (#4f08c1)
L 3 (#3b7ec0)
D 9 (#65e011)
L 8 (#24b9a0)
D 6 (#196c61)
L 5 (#540b20)
D 6 (#243801)
L 2 (#397ce0)
D 3 (#411a41)
L 12 (#397ce2)
D 2 (#526d51)
L 2 (#5b5810)
D 5 (#2e96d3)
L 8 (#6c4f80)
U 6 (#274453)
L 3 (#077010)
U 5 (#37f603)
R 5 (#73bf92)
U 4 (#29ee73)
R 11 (#5ffd40)
U 5 (#3e12d3)
L 4 (#4b4e32)
U 12 (#114e01)
L 2 (#45d0b2)
U 4 (#114e03)
L 6 (#4b8b02)
U 9 (#3eff03)
L 4 (#6968d0)
U 4 (#3d4d33)
L 6 (#1dd0e0)
D 2 (#436023)
L 9 (#58b812)
D 6 (#423003)
L 7 (#2e81a2)
D 3 (#3d9e13)
L 11 (#305450)
D 6 (#2667f3)
L 7 (#3a8470)
D 8 (#293191)
R 3 (#207ee0)
D 4 (#293193)
R 3 (#2c4360)
D 4 (#2667f1)
R 7 (#250ee0)
U 4 (#70e1c3)
R 4 (#2907d0)
D 6 (#0b69a1)
R 6 (#3babc0)
D 6 (#7698c3)
R 2 (#2752c0)
D 3 (#7698c1)
L 4 (#2dc550)
D 11 (#4ec121)
L 8 (#199d00)
U 11 (#33f261)
L 7 (#3b4e30)
D 5 (#24e991)
L 6 (#0aaeb2)
U 9 (#15a993)
L 5 (#59ed02)
U 2 (#15a991)
L 5 (#2c0432)
U 10 (#337e81)
L 7 (#248670)
D 2 (#511d31)
L 3 (#52ebc0)
D 13 (#511d33)
L 4 (#192db0)
D 7 (#3dcf11)
L 2 (#285030)
D 3 (#15acc1)
L 6 (#3a6c70)
D 5 (#36f281)
L 4 (#44fd02)
U 5 (#4e8821)
L 4 (#473972)
U 5 (#2ab9f1)
L 8 (#60aaa0)
U 5 (#35c651)
L 3 (#4736b2)
U 10 (#4741d1)
L 3 (#1973f2)
U 5 (#1b5031)
L 3 (#3c6532)
D 5 (#49bd31)
L 4 (#5e3f60)
D 3 (#4a2011)
L 4 (#2ba190)
D 6 (#4a2013)
L 3 (#653430)
D 7 (#10dda1)
L 11 (#2532d2)
D 3 (#675311)
L 4 (#27d642)
D 6 (#02cae1)
L 10 (#466652)
D 7 (#34cef3)
R 3 (#263692)
D 7 (#34cef1)
R 8 (#2c59d2)
D 5 (#31afe1)
L 8 (#091562)
D 5 (#12c861)
R 4 (#47a772)
D 5 (#68a5b1)
L 7 (#1f79a2)
D 4 (#3b3c13)
R 10 (#0e7122)
D 4 (#087363)
R 4 (#3e26c2)
U 13 (#4953f3)
R 3 (#5ac862)
U 3 (#4953f1)
R 3 (#13cfe2)
D 5 (#087361)
R 11 (#57f7a2)
D 4 (#3b3c11)
L 11 (#6dcb32)
D 7 (#35e653)
R 6 (#525282)
D 5 (#3dac93)
R 9 (#377082)
D 6 (#2afda1)
R 10 (#3e6422)
D 10 (#220471)
R 10 (#37ebf2)
D 2 (#4d0213)
R 5 (#2fa2f2)
D 4 (#30df53)
R 5 (#617e00)
U 6 (#536393)
R 5 (#496510)
D 6 (#5f6733)
R 4 (#3bf9b0)
D 3 (#5f6731)
R 4 (#48d940)
D 3 (#35a5d3)
R 4 (#284a82)
D 9 (#445b23)
L 4 (#2535c2)
D 4 (#3f8b43)
R 2 (#41a002)
D 7 (#20b143)
R 3 (#63ae42)
U 8 (#328b03)
R 9 (#383210)
U 3 (#1ce143)
R 7 (#383212)
U 6 (#2ed603)
R 5 (#00c512)
U 6 (#6dea43)
R 6 (#00c510)
U 7 (#0f0643)
R 3 (#4292a2)
U 7 (#684961)
R 3 (#03de80)
U 5 (#46e0b1)
R 6 (#3d4ae2)
D 5 (#1d5fc1)
R 7 (#3d4ae0)
U 13 (#470ab1)
R 5 (#03de82)
U 4 (#282c71)
R 6 (#5520f2)
D 5 (#2f0981)
R 2 (#5520f0)
D 12 (#4081a1)
R 6 (#22a272)
U 5 (#284c53)
R 8 (#081202)
D 5 (#0b7831)
R 4 (#3e4552)
D 13 (#0b7833)
R 5 (#38e092)
U 11 (#284c51)
R 2 (#550082)
U 2 (#175033)
R 6 (#036dc2)
D 3 (#068791)
R 9 (#3ff6f2)
D 5 (#4a5783)
R 13 (#4d5f92)
D 6 (#3e3423)
L 12 (#4f62d2)
D 7 (#5e4363)
L 3 (#0f7032)
U 7 (#285261)
L 12 (#4ea392)
D 5 (#4c2dc1)
L 4 (#0d2822)
D 5 (#4c2dc3)
R 9 (#518da2)
D 2 (#41b3b1)
R 7 (#023270)
D 3 (#13d6e1)
R 11 (#0874c0)
D 3 (#2026c1)
R 4 (#68f3a0)
D 7 (#2026c3)
L 8 (#208470)
D 4 (#09bd81)
L 3 (#193a10)
D 7 (#5f3491)
L 4 (#08cad2)
D 10 (#068793)
L 10 (#48acb2)
D 4 (#20e341)
L 7 (#6137a2)
D 8 (#20e343)
L 7 (#0bf292)
D 7 (#5b5f13)
L 8 (#2b2532)
D 3 (#034073)
L 5 (#3c7bb2)
D 11 (#09c643)
L 3 (#0d6e52)
U 3 (#569d23)
L 5 (#63fed0)
U 4 (#162193)
L 4 (#2eb310)
U 3 (#655e23)
L 10 (#42f8e0)
U 4 (#341983)
R 14 (#3dbbc0)
U 5 (#551b13)
L 5 (#2a94f0)
U 6 (#05ae53)
L 2 (#09db90)
U 5 (#2c9e13)
L 8 (#4c6d10)
D 10 (#40ffb3)
L 3 (#306c30)
D 2 (#338911)
L 6 (#4bed00)
D 7 (#338913)
R 7 (#12aae0)
D 4 (#459613)
L 7 (#75d3a2)
D 7 (#37ad61)
L 6 (#6dcf92)
D 4 (#3b7703)
R 9 (#0ee2b2)
D 4 (#514c13)
R 6 (#0ee2b0)
D 3 (#2863b3)
L 8 (#52eee2)
D 8 (#2fc771)
L 7 (#5be782)
D 4 (#2f39a1)
L 6 (#5be780)
U 4 (#5625b1)
L 2 (#5588c2)
U 14 (#37ad63)
L 3 (#0681e2)
U 4 (#085481)
L 3 (#037432)
U 12 (#31ed51)
L 4 (#6791e2)
D 5 (#3546d3)
L 6 (#4419e2)
D 10 (#3546d1)
L 4 (#2c1a72)
D 10 (#5bead1)
L 3 (#3b3252)
D 9 (#3390c1)
L 3 (#1d78c2)
D 5 (#488d71)
R 10 (#244042)
D 2 (#10d1e1)
R 5 (#3fd582)
D 5 (#5025a1)
L 3 (#381b70)
D 11 (#771831)
L 7 (#2abf50)
U 11 (#17be81)
L 5 (#1eb3c0)
D 5 (#3d9c61)
L 5 (#5f4de2)
D 8 (#278361)
L 2 (#2b95b2)
D 9 (#2cb0b1)
L 4 (#4a0182)
D 6 (#2c6bb3)
L 2 (#499642)
D 5 (#4c76d3)
L 8 (#2fe412)
U 9 (#07b893)
L 2 (#236d52)
U 8 (#3956a3)
L 7 (#34f252)
U 3 (#52bbf1)
L 7 (#189470)
U 4 (#54b661)
L 9 (#189472)
U 7 (#0110f1)
L 3 (#409bf2)
U 3 (#46d3e3)
L 5 (#4c0f82)
U 11 (#215e73)
L 4 (#4c0f80)
U 7 (#4050f3)
L 9 (#39b192)
U 7 (#28b043)
L 3 (#011270)
U 7 (#1f6693)
R 7 (#73c390)
U 2 (#2c4113)
R 5 (#0321c0)
U 8 (#2f2903)
L 12 (#5ab560)
U 3 (#4dd983)
L 6 (#38b7d2)
U 12 (#03c8c3)

304
18/src/main.rs Normal file
View File

@ -0,0 +1,304 @@
use itertools::Itertools;
use std::collections::LinkedList;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// DATA
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
enum Direction {
Left,
Right,
Up,
Down,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
enum Turn {
LeftNinety,
RightNinety,
OneEighty,
None,
}
impl Direction {
const fn all() -> &'static [Self; 4] {
&[Direction::Left, Direction::Right, Direction::Up, Direction::Down]
}
const fn opposite(&self) -> Self {
match self {
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
}
}
const fn offset(&self) -> (isize, isize) {
match self {
Direction::Left => (-1, 0),
Direction::Right => (1, 0),
Direction::Up => (0, -1),
Direction::Down => (0, 1),
}
}
fn turn_kind(&self, next_dir: Direction) -> Turn {
if *self == next_dir {
Turn::None
} else if self.opposite() == next_dir {
Turn::OneEighty
} else {
match self {
Direction::Left if next_dir == Direction::Up => Turn::RightNinety,
Direction::Left if next_dir == Direction::Down => Turn::LeftNinety,
Direction::Right if next_dir == Direction::Up => Turn::LeftNinety,
Direction::Right if next_dir == Direction::Down => Turn::RightNinety,
Direction::Up if next_dir == Direction::Left => Turn::LeftNinety,
Direction::Up if next_dir == Direction::Right => Turn::RightNinety,
Direction::Down if next_dir == Direction::Right => Turn::LeftNinety,
Direction::Down if next_dir == Direction::Left => Turn::RightNinety,
_ => unreachable!(),
}
}
}
}
impl From<&str> for Direction {
fn from(s: &str) -> Self {
match s {
"L" => Direction::Left,
"R" => Direction::Right,
"U" => Direction::Up,
"D" => Direction::Down,
s => panic!("{} is not a valid direction", s),
}
}
}
impl From<&Direction> for char {
fn from(dir: &Direction) -> Self {
match dir {
Direction::Left => '←',
Direction::Right => '→',
Direction::Up => '↑',
Direction::Down => '↓',
}
}
}
#[derive(Debug, Clone)]
struct DigInstruction {
dir: Direction,
count: usize,
color: String,
}
impl From<&str> for DigInstruction {
fn from(s: &str) -> Self {
let mut parts = s.split_ascii_whitespace();
let (dir, count, color) = (
parts.next().unwrap(),
parts.next().unwrap(),
parts.next().unwrap().chars().skip(2).take(6).collect::<String>(),
);
Self {
dir: dir.into(),
count: count.parse().unwrap(),
color: color.into(),
}
}
}
impl DigInstruction {
fn part2_transform(&mut self) {
let (distance_s, direction_s) = self.color.split_at(5);
self.count = usize::from_str_radix(distance_s, 16).unwrap();
self.dir = match direction_s {
"0" => Direction::Right,
"1" => Direction::Down,
"2" => Direction::Left,
"3" => Direction::Up,
s => panic!("`{}` is not a valid direction code", s),
};
}
}
#[derive(Debug)]
struct DigPlan {
instructions: Vec<DigInstruction>,
}
impl DigPlan {
fn part2_transform(&mut self) {
for i in &mut self.instructions {
i.part2_transform();
}
}
}
#[derive(Debug, Clone)]
struct DigTile {
position: Position,
}
impl Default for DigTile {
fn default() -> Self {
Self { position: (0, 0) }
}
}
type Position = (isize, isize);
#[derive(Debug)]
struct DigHole {
tiles_loop: LinkedList<DigTile>,
area: u64,
}
// determinant of positions p1 and p2
fn det(p1: Position, p2: Position) -> i64 {
((p1.0 * p2.1) - (p1.1 * p2.0)) as i64
}
impl DigHole {
fn new() -> Self {
DigHole {
tiles_loop: LinkedList::new(),
area: 0,
}
}
fn pos_offset_n(&self, pos: Position, offset: (isize, isize), n: usize) -> Position {
(pos.0 + offset.0 * n as isize, pos.1 + offset.1 * n as isize)
}
fn run_plan(&mut self, plan: &DigPlan) {
let mut cur_pos = (0, 0);
self.tiles_loop.push_back(DigTile { position: cur_pos });
let mut move_offset;
for (idx, i) in plan.instructions.iter().enumerate() {
let prev_instruction = if idx > 0 {
&plan.instructions[idx - 1]
} else {
&plan.instructions[plan.instructions.len() - 1]
};
let Some(next_instruction) = plan.instructions.get(idx + 1).or(Some(&plan.instructions[0])) else {
panic!()
};
let cur_turn = prev_instruction.dir.turn_kind(i.dir);
let next_turn = i.dir.turn_kind(next_instruction.dir);
// Point needs to live on the 'outside' corner of the character. to achieve this we need to offset the move
// by the following. Found this empirically but there's probably some mathematical principle behind it...
move_offset = match (cur_turn, next_turn) {
(Turn::RightNinety, Turn::RightNinety) => 1,
(Turn::RightNinety, Turn::LeftNinety) => 0,
(Turn::LeftNinety, Turn::LeftNinety) => -1,
(Turn::LeftNinety, Turn::RightNinety) => 0,
t => panic!("turn {:?} not allowed here", t),
};
cur_pos = self.pos_offset_n(cur_pos, i.dir.offset(), (i.count as isize + move_offset) as usize);
self.tiles_loop.push_back(DigTile { position: cur_pos });
}
// Shoelace formula
// https://en.wikipedia.org/wiki/Shoelace_formula
let double_area: i64 = self
.tiles_loop
.iter()
.tuple_windows()
.map(|(a, b)| det(a.position, b.position))
.sum();
self.area = (double_area / 2).abs() as u64;
}
}
impl<T: BufRead> From<Lines<T>> for DigPlan {
fn from(lines: Lines<T>) -> Self {
Self {
instructions: lines.map(|line| DigInstruction::from(line.unwrap().as_str())).collect(),
}
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let plan = DigPlan::from(input);
let mut dig = DigHole::new();
dig.run_plan(&plan);
dig.area
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut plan = DigPlan::from(input);
let mut dig = DigHole::new();
plan.part2_transform();
dig.run_plan(&plan);
dig.area
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"R 6 (#70c710)
D 5 (#0dc571)
L 2 (#5713f0)
D 2 (#d2c081)
R 2 (#59c680)
D 2 (#411b91)
L 5 (#8ceee2)
U 2 (#caa173)
L 1 (#1b58a2)
U 2 (#caa171)
R 2 (#7807d2)
U 3 (#a77fa3)
L 2 (#015232)
U 2 (#7a21e3)";
const AREA16_SQUARE: &str = &"R 3 (#000000)
D 3 (#000000)
L 3 (#000000)
U 4 (#000000";
#[test]
fn area16_square() {
let c = Cursor::new(AREA16_SQUARE);
assert_eq!(problem1(c.lines()), 16);
}
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 62);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 952408144115);
}
}

25
19/Cargo.lock generated Normal file
View File

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day19"
version = "0.1.0"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]

7
19/Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "day19"
version = "0.1.0"
edition = "2021"
[dependencies]
num-traits = "0.2.17"

775
19/input Normal file
View File

@ -0,0 +1,775 @@
kzq{m<596:R,x<1149:R,A}
in{s<2440:cd,tgz}
rs{m<2118:zx,s>3630:A,m<2297:cx,vfc}
hzz{m<2478:A,R}
ss{a<692:R,A}
sv{x<877:A,m<2320:R,A}
ph{x<2510:mf,x>3150:R,m>624:xtc,fr}
rg{s>440:hj,a>434:lg,ljm}
hdr{m<1158:R,m>1494:vfl,a<1353:A,R}
zhp{x>633:A,x<248:R,A}
fd{s<1838:A,m<3087:A,a>3495:R,jsg}
qvh{x<3002:crs,m<2331:A,s<3294:dh,R}
qbr{m>3556:R,m<3200:R,R}
sql{m<3219:A,zf}
zf{a<633:A,s<1348:A,A}
rr{x<3035:A,s<3096:A,a<2451:R,A}
bng{s>3079:R,a<724:jd,R}
lv{a<3333:tqj,hth}
bcp{a>1296:dtp,m>2875:dnq,x>1453:xjl,gp}
tz{m>3020:R,R}
gnz{m<692:R,m>1130:R,m>877:R,R}
xqg{x>3462:A,s<3298:A,s>3449:R,R}
pxd{s>1539:jvr,m<2976:frj,x>1748:mr,fts}
tj{m>3165:R,m<3044:R,a>1557:R,A}
jqm{x<2410:xf,m>3048:zm,zbl}
pv{a>2330:R,a>2259:A,x>3069:A,A}
qnz{m>1947:bz,x<2553:jr,x<3256:prq,bx}
cn{x>2438:nl,s<193:A,R}
kkb{s>3372:A,a>295:A,R}
rgr{a>707:R,a>400:hrd,npv}
klp{s<2137:jzp,x>1502:qnz,xj}
fm{m>3258:db,qd}
scf{a>490:A,m>3260:R,R}
btz{a>3068:dr,a>2668:hfb,m<2408:cs,fgr}
xqr{m>2286:A,a<2390:tf,x>3254:R,fkk}
tp{x<1044:A,a>2510:A,s<1735:A,A}
frj{x>2659:vhd,s>1415:lfl,sll}
kcn{s<1967:R,R}
vrm{a>1620:R,x>1345:A,s>3219:A,A}
vm{x<2777:gnz,m<614:hrg,a>903:shm,R}
bqq{a<994:A,m<2065:vrm,s<3419:bnv,zqt}
dtp{a>2018:cp,tbq}
qk{m>3174:slm,m>2948:jqm,gz}
gzq{m>1755:R,A}
nl{s>174:A,a<3022:A,m>2165:R,A}
mkk{a>2954:A,R}
gp{x>808:bng,nmr}
khf{m<1278:R,a>264:A,x>3812:A,A}
xxf{m>1340:A,s>2282:R,R}
bbk{a>1074:gqc,a>616:mdv,pgv}
nrp{x<2414:R,mjc}
kpt{m<3158:bcp,clt}
snl{m<868:km,m<1182:A,x<715:R,A}
cfm{s<3832:R,A}
pt{m>3354:rkx,A}
bz{a>3073:jz,s>2285:A,a>2653:tt,sgd}
zp{s>3328:R,m<981:R,A}
zkg{a<910:kjc,a<1539:hzz,s<2149:xdc,R}
jxm{x>2135:R,m>1114:R,a<668:A,ghm}
szq{s<450:cn,s>694:sdd,s>567:cm,lk}
vlz{s>584:R,s<287:R,A}
fr{s>1707:A,m<281:A,a<1488:R,R}
cdh{x>1427:R,x>1191:R,A}
brs{s>2803:A,a<821:A,s>2565:R,R}
ghm{s<3298:R,m>738:A,x>868:R,R}
cdn{s<745:R,a<1738:R,R}
vgc{s>3045:A,s>2796:R,A}
mjc{m<1443:R,a<1751:R,a<2183:A,R}
rhs{a<1453:A,m>933:kg,A}
vgh{m>3481:zmk,x>2384:ng,s<3473:pt,xvj}
lfb{x>1099:A,A}
cb{a<2973:qkv,x>2753:knq,R}
vd{a<1578:zbh,x>2560:A,s<1772:R,A}
dh{m<2492:R,m>2532:A,A}
tqj{m>1459:R,m>649:R,m>261:A,A}
sch{m<1650:R,A}
brm{x<2245:rm,vsm}
xqc{x>1906:R,s<2586:R,m>1404:R,A}
vmx{m<2123:qsd,s<2169:R,s<2335:R,A}
nrj{s>2051:ml,a<3354:A,x<772:tjm,czr}
zpq{a<805:R,s<2733:A,x<2371:R,R}
lq{m<2008:nxh,m<2296:vmx,zkg}
lfn{m>2015:R,A}
vhv{s>2798:A,m<2650:R,hsz}
rxq{a>2045:A,a>2002:A,R}
gv{x>3571:R,m>3216:A,R}
kjg{x>2443:R,s>3563:A,A}
qv{a>903:A,a<755:A,m<2822:R,A}
dqb{s<1310:A,s<1331:R,s<1351:R,A}
pll{s<1875:R,s<1928:A,a>3979:A,R}
cct{x>2015:cb,m<1524:vqc,a>3121:sdz,rs}
dq{a>3084:R,a>2989:A,x<1241:A,R}
xzx{a>2077:A,a<2023:A,a>2046:A,R}
nmr{s>3420:R,A}
klr{m<1060:A,m>1274:A,ms}
dr{s>1739:qvk,m<2207:R,a>3629:R,gk}
qvk{s<1772:A,a>3599:R,R}
gbj{a>855:R,m>2778:R,A}
drj{s<2725:R,A}
th{s<219:R,s>361:R,R}
dd{x<1745:zgq,a<2006:R,s<3396:tlq,vn}
cpr{x<2287:R,a>3768:R,A}
ntn{a<1603:R,x<3513:A,x<3736:R,A}
zlg{a<2254:xbz,m>1915:xqr,qhg}
mv{m<1685:A,s<2921:A,s<3099:nrm,A}
tnz{m>1738:R,R}
hql{m<1446:rj,qn}
bxk{x>1683:A,m<3826:R,x>1019:A,R}
pg{x>2110:A,A}
vsm{m<1847:R,x<2870:A,R}
qcs{m>3409:xh,x<948:A,x>1273:R,R}
zxz{a<961:gzj,x>900:bg,m<796:nn,A}
mp{a>2922:R,x<1561:A,A}
nb{m>1334:lq,a<942:kjq,s<2216:vcc,zd}
kjc{x>2597:R,m<2416:R,s<2114:R,R}
nz{a>349:A,x<1677:R,A}
cj{x<2805:kjg,x<3336:A,R}
kq{m<1306:R,s<3407:gmg,A}
jp{x<2530:hd,pmn}
vhj{s<2857:A,s<3029:A,x<3204:A,A}
zcr{s>3421:R,A}
kgq{m<2273:bqq,m>2432:mmf,x>1209:zxp,dzs}
crs{a<302:R,A}
fq{m>2037:vgj,m<1874:kfg,m>1968:jx,dtq}
rjx{x<2876:sf,xlc}
qsg{x>2668:A,s<2509:R,R}
gz{a<1575:tvn,a<1927:ljt,a>2208:vmj,nqk}
fzj{s>2118:A,R}
jtn{s<3102:A,m<2129:R,s>3609:A,R}
htb{x<1752:lf,s<2690:zb,a>3052:ntg,chm}
nqk{m>2784:A,x>2006:rxq,xzx}
ktz{a>1145:lnr,zz}
cl{s>2245:R,A}
pp{x>3144:R,a>1054:A,A}
km{s<3596:R,a<217:R,a<378:A,A}
btd{a>328:R,x>3186:R,s<2776:R,R}
nv{a<3170:A,x<1822:A,R}
zqt{a<1992:R,s>3757:A,R}
zvh{x<3431:A,x<3695:R,a<181:A,khf}
rjc{s>3558:rxs,ngt}
fvr{s<1819:A,a>3820:R,A}
gdh{a<1044:A,m>3889:R,a<1313:A,R}
xjl{m>2748:zfc,x>2350:hkv,qdk}
dzs{a<1516:tbs,m<2379:zcr,x>802:A,lvn}
hnk{m<1382:cqm,x<618:R,m>2969:A,ck}
sl{s<2067:R,rq}
bn{m>2044:R,x>1947:R,s>2494:A,A}
klj{m>595:zs,a>1587:A,A}
hd{m<814:rt,hdr}
pm{x>1330:rjf,s>3318:snl,m<757:lgm,jhh}
xn{m>894:R,s<486:R,A}
zxp{s>3057:mnb,A}
bt{x>2338:R,s<2515:R,A}
qdh{x<1435:dfm,m>3320:sn,m<2950:knb,xcq}
zsf{x>3119:R,s<3965:A,R}
lxs{x<1891:R,R}
fp{a>839:A,a<370:A,x>3697:R,A}
fb{x>1576:A,x>1383:R,R}
ssq{x>1389:R,x<683:A,R}
psl{s>1912:nb,mdx}
gzd{a<286:R,m>2048:R,A}
cm{x<2249:R,R}
ps{m<3057:A,a<192:A,R}
ql{x<1455:R,s<2316:A,dz}
tl{s>2746:R,a<2925:jth,s<2631:R,dq}
fl{m<871:R,R}
dnd{s<3340:R,a<1631:A,R}
hpk{x<2148:nbd,kq}
hrd{m>2977:A,R}
cqm{a<3300:A,s<2326:R,R}
vhx{a>264:sx,czq}
hpb{m<1744:bkc,x<1249:cf,vkm}
ms{x>3633:R,a>279:A,x>3536:A,A}
qkk{a<2687:A,s<2740:R,A}
vfc{a<2771:R,A}
rjf{s<3472:br,x<1970:A,a>216:R,A}
xbz{x<3190:lkc,gvr}
mx{s>1027:R,A}
fxr{a<3952:R,R}
sz{m>3436:R,a>3677:R,a<3578:A,R}
qsd{s>2211:A,s>2062:A,x<1916:R,R}
jxd{a>3568:A,x>2134:R,hr}
xlc{m<3646:ntn,m>3749:vb,a<829:hmk,gc}
zmb{x<854:A,tv}
dxx{a>796:brs,m<1206:gq,R}
jj{m<1020:xhs,a<758:ghs,dxx}
jd{s>2702:A,a>437:A,x<1198:A,A}
vb{a<1183:A,x>3584:R,R}
zg{a<1774:R,rp}
fjl{m<1926:A,A}
sx{m>412:rlx,m<224:hsm,ts}
clt{m>3707:kbk,vgh}
sdd{x>1333:A,s>792:ffv,qc}
ltl{s<2747:A,A}
qd{a<1088:A,a>1543:R,R}
lnr{s>1491:A,tgm}
hr{s<2837:A,R}
tbn{m>1412:A,x<1334:ltl,bbq}
db{x<1961:R,R}
dfm{m>3324:R,A}
mdx{s>1504:cc,sb}
xvj{x<1523:R,R}
zm{x>3061:txs,x<2826:R,R}
bkc{s<3439:R,s<3712:R,R}
sp{a>996:A,R}
ftd{x>2631:R,m>2447:R,x>2241:R,R}
qxl{m>591:R,x>508:A,R}
ngs{x<1410:R,A}
nvl{m>836:R,A}
kbd{m>2655:jxd,shz}
bzz{m>2969:R,A}
hth{x>532:R,x<263:A,A}
dtq{x<1785:fjl,qpt}
qbz{m<1913:kkb,m<2018:R,x<2884:tvj,gzd}
tr{x<1816:zxz,s<3159:gj,a>942:vtz,vm}
pdr{a>3495:R,x<960:A,A}
sfd{m<1983:R,a>3730:A,A}
qm{a<3400:A,R}
vlx{x<1628:R,x>2516:A,R}
ztb{s<1848:A,s<1862:R,A}
tlq{m<3961:A,A}
mtx{x<628:R,s<2839:A,R}
dp{m<631:R,x>3424:R,A}
bch{x>544:R,s<1473:A,A}
qkv{s>3614:A,a<2756:R,A}
tlz{s<1526:R,A}
nr{a<1406:R,s>1965:ls,a<1804:R,kmj}
rn{m<3158:R,x>1428:R,A}
fn{s>2334:R,R}
fv{s>496:R,m>2847:A,R}
kdt{x<1651:qx,mn}
dt{s<1907:R,A}
fxp{s>1444:R,x<1151:R,s<1432:R,A}
skp{s<2684:ff,a>3661:grc,kbd}
sf{a<1342:R,R}
vn{x<2642:A,m>3964:A,x>3188:R,R}
cc{m<1326:ph,vd}
st{m>2033:kv,x>2199:mh,qs}
tt{s<2228:R,m>2870:A,x<2943:R,A}
zxc{x<2549:A,a<790:fl,a>821:hs,rv}
nzg{a>721:R,a<709:R,m<1312:R,R}
dpm{x<2029:dvz,tkb}
xzp{x<1033:zhp,m<3650:vk,s>1404:cdh,R}
kmt{x>1508:A,m>3316:sdt,ngs}
jr{x<2063:zvg,x<2340:A,m>945:cl,R}
slv{m<658:A,m>699:R,x<1549:R,R}
txs{x>3387:R,x>3223:A,A}
jbj{x<1080:R,R}
slm{s>2107:qgv,x>1425:pbp,nr}
xj{s<2246:lv,hnk}
rxs{a>3208:R,s>3711:tz,bvt}
sgd{m>2799:R,m<2302:A,s>2226:A,A}
gng{m>1061:xqc,m>382:A,s<2565:A,rkh}
tq{m>3700:R,m>3578:A,m<3552:R,R}
gqc{m<751:vc,m<1228:trs,qln}
bzg{a<3173:A,x>2040:A,A}
gr{s>1874:A,A}
sm{x>1443:fc,m<3252:pqp,lh}
vt{x<324:sch,m<2405:zdb,s<2751:A,mtx}
rrz{a<2925:A,m<1807:A,A}
dvm{s>3793:R,a<714:R,a>723:A,R}
ns{a>216:fzj,s<2226:A,pl}
tsn{m<1180:R,x<1442:ss,a<693:A,dvm}
lgm{m<295:mt,qxl}
gq{a>772:R,s>2860:A,A}
tv{x<1247:R,m>2373:A,R}
shm{a>923:R,R}
tc{m>1326:pd,x<2176:A,A}
zs{a>956:R,x<1552:A,m<1047:A,A}
kbk{a<1245:zr,m>3892:dd,a>1823:hf,rkz}
gc{s>1625:A,x<3456:R,A}
vv{m<1670:jp,a<1470:tvs,dx}
dv{a<3900:dt,x<2401:R,a>3952:pll,A}
rh{x>1522:md,zmb}
bv{s<813:R,x<1898:A,m<2291:A,A}
dfs{a>2165:A,x<2625:A,m<905:A,R}
sq{m<1537:A,a>3700:R,s>3518:R,R}
zbh{m>1984:R,s>1714:A,s>1589:R,A}
pmn{x>3353:mxr,xn}
fc{m<3293:A,R}
hj{m>2607:nz,bv}
cjt{s<3222:R,a>778:A,R}
md{a<2755:A,a<2974:bqz,m<1812:A,R}
jcn{x<3486:A,A}
tm{x<1626:R,x>2473:A,R}
rp{s<2043:A,A}
dtt{x<2469:A,x>2652:A,R}
rkh{a<3899:R,a<3939:A,a>3972:A,R}
vtz{x<3222:jnj,A}
dz{a>1273:R,x<2592:R,a>1109:R,A}
zbl{x<3369:A,a<1301:A,A}
xcc{x<1386:R,x>2717:A,A}
snz{x>1513:R,A}
pnp{m>2870:R,s<1615:R,A}
nzs{x<2228:svh,a>3290:drm,qck}
dx{m<2704:st,bnq}
ml{m>2093:A,m<723:R,x>705:A,A}
nrm{x<2693:R,x>3329:R,a<1508:A,R}
ls{a>2023:R,x<645:A,x<1062:R,R}
zbk{s<3432:A,R}
pcx{a>133:A,R}
xc{a<2881:A,m>2239:R,R}
bvt{x>2521:R,m>3250:A,A}
vfl{a>1549:A,A}
gb{s>2827:fxr,a<3925:vp,tk}
nt{a<3562:R,s>1880:R,A}
qh{s<2766:R,m>1823:R,m<1032:R,R}
vkb{m>3657:R,a<1604:A,R}
qdk{a>446:vrt,a>237:R,A}
nx{s<3053:A,x>2661:A,s<3652:A,A}
ffv{m>2118:A,x<591:A,R}
gnx{m>3550:A,s>340:R,A}
xdc{s<2011:R,a<1886:A,m<2525:R,R}
rnx{x>1741:A,R}
vgj{a<1194:R,a>1316:A,mjf}
rz{m<245:A,R}
jzp{x<1378:nrj,qm}
dhz{m<2327:dnd,x<3029:ftd,m<2461:R,A}
mmf{x>1349:R,m>2512:A,R}
chm{m<1360:sh,a>2753:dmf,x<2850:zdz,rd}
bb{a<1712:R,s<1716:A,fgd}
lfl{s>1480:R,fxp}
vkm{a<944:fb,cdz}
shz{x>2518:R,s>2824:pdr,x<1098:A,R}
lvn{a<1975:A,A}
ng{m<3372:pjq,R}
ntg{x<2896:bvx,hql}
qck{a>2892:R,x>2827:gx,m<1542:njr,R}
xg{m<274:R,R}
qs{a<1914:cdn,R}
bd{m>3657:jbj,m<3576:R,x>715:hk,R}
mnb{m>2328:A,s<3622:A,a<1275:A,A}
lkc{s>3247:R,A}
jz{a<3649:A,a<3878:R,R}
pgv{x<2543:pm,m<747:vhx,s<3269:kb,hx}
vfk{s>2335:sd,R}
mxr{s<525:pz,fp}
qln{s<3057:tbn,m>1379:nrp,tc}
hsm{a<391:A,R}
mdv{a>842:tr,s<3143:jj,mfx}
tgm{x<1435:R,A}
bj{x>2353:R,A}
frk{s>1770:A,lfb}
tnt{a>228:R,x<307:A,A}
bx{s<2240:R,m>928:gf,x<3724:gm,fn}
vr{s<3307:R,m>3549:R,A}
zd{a<1856:ql,vfk}
jth{s<2601:A,A}
tbq{s>3089:A,s<2692:bzz,A}
zz{x>1544:tlz,bch}
mq{m>1048:R,s<3046:dfs,m<905:nvl,R}
xsb{a>968:A,s>3282:R,R}
ctf{m>2182:dhz,s>3326:cj,m<1769:mv,xxb}
cd{a>2405:qjl,s<1247:vv,m<2651:psl,cz}
sdt{m<3413:A,s<1386:A,R}
nm{m>1061:A,m<861:R,a<1430:R,zp}
kbp{a>257:R,R}
czq{s>3301:A,pcx}
rv{x<3177:R,x<3631:R,m>605:R,R}
lg{x<2664:A,fqp}
rq{s<2141:R,x<1452:A,x<2322:A,A}
rkx{m>3406:R,s>2856:R,a>1356:R,R}
rxz{a>3491:ft,m>1104:R,s<1879:A,A}
tqc{s>2638:A,R}
kb{m<1115:btd,a<356:zvh,nh}
svh{m>2131:A,s>1205:A,sdj}
gzj{s>3413:R,m<563:R,R}
tgz{a>2498:nc,m<1536:bbk,m>2606:kpt,dpm}
nc{s>2973:mml,a>3390:skp,htb}
zr{m>3877:A,a<786:zbk,sp}
hf{m<3830:A,R}
rj{s>2812:A,R}
lvf{s<2217:ghz,m<352:fg,s<2328:R,R}
tvn{x<1573:A,ct}
pqp{a<1606:A,s>1424:R,x>1211:A,A}
fpm{a<3321:A,a>3380:A,R}
lpq{a<2688:A,m>550:A,R}
skb{a>496:R,a>370:A,m>3052:A,R}
vqc{a>2824:A,mdj}
sk{s>3032:R,a>1895:kzq,A}
fqp{x<3283:R,x>3734:R,m>2573:A,A}
kv{x<1403:R,A}
jhh{a<365:zv,s>2937:R,a<532:A,A}
qx{m<216:A,m<345:A,R}
tvj{m>2055:R,x>2595:R,R}
fkk{s<3440:A,R}
nh{s>2904:R,x>3474:A,R}
bnq{x<1726:qcs,x>2609:xjj,pg}
bhv{s<1815:btz,a<3302:rh,m<1426:xs,dfq}
grc{a>3883:gb,a>3753:brm,a<3710:vhv,xr}
pl{x<1731:R,a<138:R,m<1085:R,R}
ngt{x>2573:xqg,a>3461:sz,m>3259:vr,vmf}
jnq{m<2176:R,m>2968:R,x>3137:A,R}
sb{m>1679:lfn,s>1408:snz,klj}
jvr{a>1008:bb,s<1695:np,ln}
kjq{m<593:lvf,m<873:bcs,a>378:djc,ns}
hh{s>2869:R,A}
kj{x>1634:rrz,s<2750:A,m>2365:mp,R}
xf{m<3098:A,m<3128:cr,a<995:rn,vj}
zv{x<860:A,a>221:R,x>1038:A,R}
rm{a>3836:A,A}
tbs{a>927:A,R}
gm{m<317:A,A}
sdz{s>3569:R,m<1986:tnz,fpm}
lf{x<960:vt,x<1466:tl,kj}
qhg{m<1782:vgc,a<2401:A,rr}
nxh{a<1275:R,gzq}
tk{x<2449:A,R}
cp{s<3395:R,s>3741:cfm,tm}
zdb{x>591:A,s<2781:A,R}
mhm{x<3419:R,x<3710:A,m>2686:A,A}
pz{m>885:R,a<1395:A,R}
jxl{a<1824:rz,A}
vmr{a>570:jqd,m>2081:qvh,m>1814:qbz,dkx}
kmj{s<1935:R,A}
prq{m>920:xxf,R}
cf{s>3313:A,x>700:A,m>1872:A,R}
qgv{a>1042:vlx,a>596:rqd,R}
mr{x>3011:gv,x>2379:mjx,fm}
cx{a>2892:A,A}
xz{m>2929:R,s<2814:R,s>2986:A,A}
ck{x<994:A,s>2364:A,m>2064:A,R}
zdd{s>911:nzs,szq}
vj{s>2138:A,m>3156:A,R}
jnj{a<996:A,s<3488:R,R}
kfg{s<786:qtq,A}
tjm{s<2018:R,s<2038:R,R}
dkx{x>2834:R,x<2537:R,nx}
zfc{m<2823:gbj,crk}
ff{m>1893:dc,a>3780:gng,xjz}
vmf{x<1019:A,A}
ghs{a<703:A,a>738:R,nzg}
gs{a<393:R,a<477:R,s<3909:R,zsf}
zvg{s>2319:A,x>1801:A,A}
gk{x<2559:R,A}
xjz{s<2581:R,x>2364:R,a<3606:A,tqc}
sd{x<2625:R,A}
mdj{s>3552:A,x>946:A,R}
nbd{x<1042:sq,m<915:A,tmj}
tmj{m>1880:A,x>1503:R,A}
drm{x>3299:R,s<1171:mx,R}
cs{m>1405:A,x>2157:A,tp}
xtc{x<2884:R,A}
gj{a>929:vhj,a<885:R,A}
mml{m>2426:rjc,a>3449:hpk,cct}
hfb{x<1738:xc,R}
vk{m<3572:A,m>3601:A,s>1380:R,R}
dmf{a<2866:A,R}
jx{m<1993:R,R}
vz{m<408:R,A}
cr{m>3114:R,x<927:A,R}
rqd{a>827:R,m<3616:A,R}
hxm{a<2012:A,a<2147:R,s>1974:R,R}
fgr{m>3145:A,R}
ljm{a>175:th,x>2267:R,s>203:A,R}
sh{x<2556:A,s<2802:qkk,x>3244:R,lpq}
mh{x<3136:R,jcn}
jcv{x>3293:A,A}
xh{m<3719:A,x<825:A,R}
nn{s>3158:R,a>1035:A,m>397:A,A}
np{m<3051:pnp,scf}
xd{m<3074:A,s<2935:A,a>2618:A,R}
njr{m>623:R,x<2518:A,R}
vmj{x>1396:pv,a<2288:bfk,R}
rt{a>991:xg,a>395:R,a>238:A,vz}
lmf{a<941:tq,s<1601:R,R}
zdz{s>2837:R,m<2501:cvx,a<2586:R,A}
gmg{a>3638:A,a>3540:R,A}
zmk{m>3609:vkb,jc}
skj{a>908:R,a>564:R,x<489:tnt,R}
mb{m>3811:ktz,x<1811:gl,rjx}
rl{x<2034:R,m>1003:A,a<3236:A,R}
dvz{m>1954:kgq,hpb}
hmk{x>3265:R,s>1639:A,s>1393:R,R}
dfq{s>1884:hjr,a>3606:bq,m<2391:hvz,fd}
jqd{x<2830:cjt,jtn}
vhd{m>2806:pp,a<962:dsd,A}
knb{a>1140:R,qv}
br{x<1778:R,R}
zkt{s>3133:R,m<3860:R,A}
zgq{a>1715:R,m<3933:R,A}
xjj{a<1879:R,x>3421:qbr,x<3014:R,R}
bg{m<731:A,m<1261:R,a<1026:A,A}
ct{x<2963:A,s<2119:R,x>3312:A,A}
bvx{x>2177:R,m<1910:rl,x>1943:bzg,nv}
hg{x>2696:jnq,x>2077:bt,bn}
hs{x<3271:R,A}
tvs{a<654:rg,m<2741:fq,qdh}
qtq{a<982:R,a<1234:A,R}
gcg{m>3101:R,a<480:R,A}
vp{a>3910:R,s>2776:A,a>3898:A,A}
pd{x>1921:A,A}
xt{a>967:R,m>3516:A,a>776:R,A}
qc{a>3321:R,A}
tkb{a<1063:vmr,a>1959:zlg,ctf}
bcs{s>2230:lxs,s>2030:R,m<743:slv,A}
qn{a<3266:A,a>3348:R,m<2667:R,A}
cvx{x>2282:A,m<2005:R,a<2628:A,R}
hrg{m>356:R,s>3696:R,A}
hvz{s<1848:A,a<3490:A,s<1870:A,xcc}
lh{s>1362:R,x>1231:R,a>1747:R,R}
vc{m<422:jxl,x>2315:zdg,sk}
fts{x<952:skj,a>1143:sm,x<1335:sql,kmt}
jsg{x>2596:R,x>1393:A,m>3518:R,A}
hk{x>1083:R,m>3604:R,A}
mt{s>2812:R,x<595:R,s>2688:R,A}
cdz{x>1644:R,m>1862:R,A}
pxv{a<3725:nt,a<3830:A,A}
njd{m<2225:R,mkk}
mjx{s<1366:dqb,tj}
bfk{a>2248:R,R}
xs{m<518:pxv,a<3589:rxz,a>3783:dv,gr}
djc{s>2174:A,js}
zx{x<1016:A,x<1528:R,R}
mzd{a>2015:R,R}
knq{a>3143:R,x>3377:R,R}
bq{s>1846:A,s>1827:cpr,fvr}
zdg{s>3443:A,a>1681:mzd,s<2909:dp,A}
sll{m<2763:R,s<1342:ssq,R}
ft{m<1121:A,s>1882:A,s>1847:R,R}
dnq{x>1487:bl,s<3256:tpk,m<3063:rgr,gmd}
bqz{a>2865:R,A}
sdj{m>1268:A,s<1037:R,a<3341:A,R}
dc{s>2554:A,m<2684:R,qsg}
bbq{s>2709:A,s>2586:A,R}
js{m<1039:A,A}
hjr{x<1882:R,s>1927:R,jcv}
lk{a>3257:bj,a>2920:A,m>1832:fv,R}
ts{a>401:R,a<339:A,s<3429:A,R}
xr{x<1991:sv,s<2852:qh,s<2893:hh,sfd}
bl{m<2988:A,a>784:xsb,a>295:skb,ps}
kg{x<1065:R,x>1534:A,R}
gf{x<3539:A,a<3041:R,R}
ckj{s<2886:A,s>2893:A,A}
fg{s<2314:A,m<139:R,x>1421:A,R}
mn{x<3000:A,m>315:R,m<177:A,R}
vrt{s>3263:A,a<878:R,x>2044:A,R}
npv{x<687:R,A}
ljt{x<2374:R,A}
pbp{a>1398:hxm,a>588:xt,a<333:kcn,R}
fgd{s>1796:R,x>2219:R,m<3069:A,R}
xhs{a<752:R,zpq}
tf{m<2132:A,m>2187:A,A}
qjl{s<1624:zdd,s>1981:klp,bhv}
gx{s<1192:A,s>1376:R,R}
hsz{m>3435:R,s<2747:A,A}
rkz{a>1486:bxk,m<3808:R,a>1388:A,zkt}
mjf{x>2320:A,x<1139:A,R}
gl{a>1425:bd,s>1660:frk,s>1500:lmf,xzp}
gvr{m<2241:R,R}
bnv{s<3071:R,R}
vcc{m<618:sl,x>2269:zg,rhs}
ghz{x>1927:A,a<394:A,m<231:A,R}
crk{m<2842:R,x>2970:A,x<2213:R,R}
mfx{a>731:zxc,m<547:kdt,s>3502:tsn,jxm}
pjq{m<3273:A,a<945:A,A}
tpk{m>2975:drj,xz}
qpt{x>2909:A,a>991:A,a<810:R,R}
jc{m>3552:R,R}
rd{s<2850:R,x>3613:A,s>2904:xd,ckj}
rlx{x>3373:R,A}
hkv{a>502:mhm,s>3324:R,A}
dsd{s<1357:R,x<3340:R,R}
ln{s>1813:ztb,m>3116:rnx,A}
czr{m>2538:R,A}
xxb{s>2993:R,x>2787:R,dtt}
mf{s<1679:A,m<538:A,R}
zb{s>2579:njd,hg}
sn{m>3668:gdh,x>2740:A,s>624:R,gnx}
trs{a<1933:nm,mq}
xcq{m>3184:vlz,x<2483:R,R}
hx{s>3738:gs,x<3448:kbp,klr}
cz{s>1882:qk,m<3508:pxd,mb}
gmd{x>924:A,x<437:A,gcg}
{x=363,m=218,a=594,s=411}
{x=973,m=37,a=2533,s=132}
{x=2768,m=1537,a=645,s=580}
{x=2079,m=168,a=502,s=656}
{x=38,m=145,a=3452,s=1162}
{x=70,m=1238,a=99,s=560}
{x=1261,m=1540,a=283,s=28}
{x=1194,m=780,a=1538,s=2681}
{x=1236,m=1847,a=1403,s=1086}
{x=2227,m=163,a=716,s=410}
{x=9,m=369,a=572,s=1418}
{x=201,m=1472,a=943,s=3541}
{x=2730,m=1777,a=127,s=3031}
{x=329,m=139,a=1310,s=2983}
{x=54,m=981,a=1378,s=783}
{x=658,m=410,a=482,s=490}
{x=1555,m=1246,a=365,s=2769}
{x=1075,m=95,a=806,s=750}
{x=408,m=125,a=2296,s=1599}
{x=715,m=1257,a=1287,s=1638}
{x=63,m=1295,a=169,s=1802}
{x=2068,m=4,a=768,s=365}
{x=1390,m=3366,a=1730,s=1631}
{x=1900,m=622,a=761,s=1108}
{x=1857,m=678,a=136,s=965}
{x=3295,m=47,a=303,s=63}
{x=2853,m=700,a=315,s=647}
{x=379,m=304,a=914,s=59}
{x=2591,m=385,a=1410,s=1473}
{x=140,m=1237,a=769,s=2670}
{x=747,m=395,a=1988,s=633}
{x=30,m=381,a=1966,s=2597}
{x=1969,m=1191,a=501,s=328}
{x=127,m=1200,a=47,s=2408}
{x=1409,m=2136,a=205,s=3122}
{x=150,m=88,a=1082,s=3059}
{x=320,m=2917,a=1371,s=22}
{x=2237,m=1186,a=466,s=2252}
{x=2460,m=7,a=1758,s=874}
{x=421,m=126,a=1338,s=1479}
{x=150,m=675,a=2555,s=267}
{x=1007,m=1178,a=2927,s=2435}
{x=902,m=190,a=77,s=1279}
{x=1393,m=716,a=686,s=569}
{x=339,m=1199,a=275,s=338}
{x=116,m=1997,a=739,s=142}
{x=1332,m=69,a=74,s=1953}
{x=2324,m=2089,a=101,s=1595}
{x=2966,m=696,a=305,s=1221}
{x=810,m=1860,a=141,s=49}
{x=995,m=382,a=1020,s=756}
{x=899,m=133,a=746,s=952}
{x=325,m=1880,a=554,s=1749}
{x=506,m=1478,a=897,s=597}
{x=659,m=424,a=20,s=1447}
{x=4,m=310,a=1121,s=286}
{x=3357,m=396,a=1204,s=2886}
{x=1209,m=614,a=2397,s=408}
{x=505,m=339,a=72,s=709}
{x=3478,m=1412,a=1777,s=1}
{x=2271,m=136,a=83,s=477}
{x=3008,m=280,a=2167,s=1522}
{x=543,m=772,a=433,s=3560}
{x=227,m=2037,a=1444,s=272}
{x=869,m=80,a=2585,s=1212}
{x=893,m=1207,a=124,s=126}
{x=2178,m=2030,a=3000,s=1438}
{x=450,m=1798,a=1307,s=2147}
{x=469,m=1554,a=1080,s=393}
{x=49,m=389,a=813,s=39}
{x=3041,m=607,a=379,s=1435}
{x=62,m=121,a=47,s=2394}
{x=1589,m=2474,a=1599,s=3}
{x=439,m=1493,a=284,s=3}
{x=619,m=1377,a=1107,s=3452}
{x=1802,m=1132,a=851,s=111}
{x=2154,m=464,a=2611,s=2147}
{x=843,m=423,a=355,s=838}
{x=3,m=423,a=139,s=857}
{x=42,m=843,a=2219,s=3695}
{x=1068,m=575,a=88,s=222}
{x=3097,m=238,a=913,s=2452}
{x=109,m=206,a=291,s=728}
{x=3635,m=1815,a=26,s=19}
{x=49,m=115,a=2998,s=26}
{x=2093,m=391,a=267,s=2896}
{x=282,m=2364,a=62,s=62}
{x=361,m=719,a=1679,s=137}
{x=631,m=1131,a=203,s=2281}
{x=794,m=1004,a=868,s=636}
{x=1702,m=415,a=35,s=236}
{x=63,m=1690,a=682,s=2009}
{x=1815,m=3459,a=144,s=374}
{x=91,m=590,a=1651,s=1681}
{x=1561,m=2303,a=307,s=1463}
{x=842,m=927,a=868,s=455}
{x=51,m=23,a=427,s=247}
{x=2431,m=187,a=267,s=184}
{x=1596,m=1904,a=668,s=1553}
{x=922,m=1435,a=336,s=1301}
{x=1038,m=1632,a=220,s=117}
{x=1935,m=1433,a=2988,s=316}
{x=21,m=1158,a=1988,s=1242}
{x=840,m=1272,a=418,s=1198}
{x=1227,m=1811,a=158,s=10}
{x=1836,m=56,a=19,s=1573}
{x=461,m=621,a=532,s=92}
{x=2082,m=1292,a=1032,s=322}
{x=2094,m=249,a=865,s=2030}
{x=1092,m=265,a=1745,s=1720}
{x=2361,m=1087,a=209,s=1934}
{x=92,m=111,a=3188,s=2172}
{x=248,m=2163,a=1351,s=1194}
{x=1675,m=2194,a=324,s=1764}
{x=2148,m=3124,a=854,s=1823}
{x=520,m=617,a=649,s=49}
{x=2497,m=12,a=270,s=161}
{x=248,m=2,a=1026,s=1196}
{x=367,m=860,a=1141,s=61}
{x=411,m=90,a=3401,s=2156}
{x=284,m=265,a=421,s=866}
{x=2152,m=2245,a=1206,s=1056}
{x=1578,m=74,a=93,s=1494}
{x=225,m=50,a=2223,s=1888}
{x=2448,m=372,a=587,s=820}
{x=1222,m=215,a=31,s=1753}
{x=2906,m=1170,a=1120,s=291}
{x=1253,m=329,a=478,s=2489}
{x=563,m=150,a=103,s=1051}
{x=212,m=980,a=1325,s=882}
{x=580,m=754,a=555,s=1470}
{x=522,m=397,a=1018,s=763}
{x=2327,m=1100,a=975,s=511}
{x=324,m=1719,a=48,s=569}
{x=1872,m=3353,a=54,s=1330}
{x=2082,m=1209,a=2390,s=747}
{x=1510,m=2290,a=591,s=45}
{x=60,m=1482,a=924,s=598}
{x=775,m=675,a=1635,s=1560}
{x=316,m=170,a=168,s=1335}
{x=1821,m=79,a=2073,s=92}
{x=612,m=1814,a=1651,s=3496}
{x=116,m=2643,a=1073,s=301}
{x=313,m=92,a=2892,s=109}
{x=1109,m=279,a=701,s=763}
{x=86,m=214,a=1910,s=99}
{x=1049,m=320,a=530,s=216}
{x=192,m=366,a=887,s=1516}
{x=353,m=107,a=257,s=225}
{x=117,m=72,a=810,s=147}
{x=341,m=17,a=190,s=2748}
{x=1397,m=572,a=2275,s=1494}
{x=386,m=1691,a=346,s=985}
{x=603,m=584,a=1464,s=291}
{x=49,m=46,a=2277,s=1200}
{x=3116,m=1,a=500,s=166}
{x=310,m=1054,a=1271,s=1423}
{x=1690,m=721,a=1555,s=232}
{x=1713,m=2002,a=1132,s=611}
{x=229,m=1721,a=1590,s=42}
{x=16,m=1444,a=2296,s=144}
{x=371,m=339,a=1073,s=1585}
{x=1025,m=10,a=491,s=196}
{x=58,m=1161,a=492,s=1401}
{x=305,m=1366,a=919,s=236}
{x=1427,m=654,a=2052,s=2399}
{x=1312,m=723,a=408,s=173}
{x=589,m=703,a=29,s=126}
{x=2609,m=1461,a=266,s=129}
{x=299,m=253,a=857,s=2220}
{x=259,m=600,a=494,s=546}
{x=2978,m=988,a=1660,s=2661}
{x=193,m=478,a=213,s=1631}
{x=3378,m=2275,a=534,s=2317}
{x=654,m=2641,a=284,s=1768}
{x=50,m=76,a=672,s=120}
{x=85,m=630,a=1446,s=1321}
{x=306,m=986,a=808,s=1724}
{x=782,m=1554,a=741,s=2220}
{x=1115,m=48,a=3283,s=958}
{x=214,m=2498,a=996,s=981}
{x=1437,m=53,a=2052,s=914}
{x=3256,m=441,a=47,s=108}
{x=689,m=1954,a=60,s=133}
{x=1,m=2270,a=903,s=13}
{x=2373,m=956,a=2739,s=860}
{x=1270,m=357,a=2115,s=1868}
{x=114,m=587,a=349,s=62}
{x=186,m=491,a=464,s=46}
{x=2001,m=99,a=649,s=1854}
{x=408,m=386,a=681,s=774}
{x=697,m=775,a=2740,s=1582}
{x=525,m=2075,a=11,s=822}
{x=621,m=1069,a=233,s=255}
{x=979,m=2750,a=241,s=199}
{x=2006,m=665,a=2211,s=1776}
{x=483,m=213,a=2042,s=67}
{x=1181,m=1199,a=1513,s=919}
{x=1378,m=274,a=438,s=546}
{x=59,m=224,a=3034,s=254}

392
19/src/main.rs Normal file
View File

@ -0,0 +1,392 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::ops::{Range, IndexMut, Index};
use std::time::Instant;
use num_traits::Num;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// DATA
const INPUT_RANGE: Range<u64> = 1..4001;
#[derive(Debug)]
struct RulePredicate {
op: PredicateOperator,
var: char,
}
#[derive(Debug, Clone)]
enum RuleAction {
Terminate(bool),
Jump(String),
}
impl From<&str> for RuleAction {
fn from(s: &str) -> Self {
match s {
"A" => Self::Terminate(true),
"R" => Self::Terminate(false),
s => Self::Jump(s.into()),
}
}
}
#[derive(Debug)]
struct Rule {
pred: RulePredicate,
action: RuleAction,
}
#[derive(Debug)]
enum PredicateOperator {
Always,
Lt(u64),
Gt(u64),
}
#[derive(Debug, Clone)]
struct XmasRanges {
ranges: [Vec<Range<u64>>; 4],
}
impl XmasRanges {
fn none() -> XmasRanges {
Self {
ranges: [vec![0..0], vec![0..0], vec![0..0], vec![0..0]],
}
}
fn all() -> XmasRanges {
Self {
ranges: [
vec![INPUT_RANGE],
vec![INPUT_RANGE],
vec![INPUT_RANGE],
vec![INPUT_RANGE],
],
}
}
fn idx(c: char) -> usize {
match c {
'x' => 0,
'm' => 1,
'a' => 2,
's' => 3,
c => panic!("`{}` is not a valid xmas char", c),
}
}
fn insert(&mut self, c: char, v: Vec<Range<u64>>) {
self.ranges[Self::idx(c)] = v;
}
fn count_states(&self) -> u64 {
self.ranges
.iter()
.map(|ranges| ranges.iter().map(|range| range.end - range.start).sum::<u64>())
.product()
}
}
impl Index<char> for XmasRanges {
type Output = Vec<Range<u64>>;
fn index(&self, index: char) -> &Self::Output {
&self.ranges[Self::idx(index)]
}
}
impl IndexMut<char> for XmasRanges {
fn index_mut(&mut self, index: char) -> &mut Self::Output {
&mut self.ranges[Self::idx(index)]
}
}
fn range_overlap<T: Num + Ord + Copy>(r1: &Range<T>, r2: &Range<T>) -> Option<Range<T>> {
let new_start = std::cmp::max(r1.start, r2.start);
let new_end = std::cmp::min(r1.end - T::one(), r2.end - T::one());
if new_start <= std::cmp::min(r1.end - T::one(), r2.end - T::one()) && new_end >= std::cmp::max(r1.start, r2.start)
{
Some(new_start..new_end + T::one())
} else {
None
}
}
fn range_exclude<T: Num + Ord + Copy>(keep: &Range<T>, exclude: &Range<T>) -> Vec<Range<T>> {
let mut residual = Vec::new();
if let Some(overlap) = range_overlap(keep, exclude) {
if keep.start < overlap.start {
residual.push(keep.start..overlap.start);
}
if keep.end > overlap.end {
residual.push(overlap.end..keep.end);
}
} else {
residual.push(keep.clone());
}
residual
}
impl From<&str> for PredicateOperator {
fn from(s: &str) -> Self {
let (op_s, val_s) = s.split_at(1);
match op_s {
"<" => PredicateOperator::Lt(val_s.parse().unwrap()),
">" => PredicateOperator::Gt(val_s.parse().unwrap()),
s => panic!("unknown operator {}", s),
}
}
}
impl From<&str> for RulePredicate {
fn from(s: &str) -> Self {
let (var_s, pred_s) = s.split_at(1);
Self {
op: pred_s.into(),
var: var_s.chars().next().unwrap(),
}
}
}
impl RulePredicate {
fn check(&self, part: &Part) -> bool {
match self.op {
PredicateOperator::Always => true,
PredicateOperator::Gt(val) => part.0[&self.var] > val,
PredicateOperator::Lt(val) => part.0[&self.var] < val,
}
}
fn matching_range(&self) -> Range<u64> {
match self.op {
PredicateOperator::Always => INPUT_RANGE,
PredicateOperator::Gt(val) => val + 1..INPUT_RANGE.end,
PredicateOperator::Lt(val) => INPUT_RANGE.start..val,
}
}
}
impl From<&str> for Rule {
fn from(s: &str) -> Self {
if let Some((predicate_s, action_s)) = s.split_once(':') {
Self {
pred: predicate_s.into(),
action: action_s.into(),
}
} else {
Self {
pred: RulePredicate {
op: PredicateOperator::Always,
var: '\0',
},
action: s.into(),
}
}
}
}
impl Rule {
fn predicate_result(&self, ranges: XmasRanges) -> (XmasRanges, XmasRanges) {
if let PredicateOperator::Always = self.pred.op {
(ranges, XmasRanges::none())
} else {
let (mut matching, mut unmatching) = (ranges.clone(), ranges.clone());
let relevant_ranges = &ranges[self.pred.var];
matching.insert(
self.pred.var,
relevant_ranges
.iter()
.filter_map(|range| range_overlap(range, &self.pred.matching_range()))
.collect(),
);
unmatching.insert(
self.pred.var,
relevant_ranges
.iter()
.flat_map(|range| range_exclude(range, &self.pred.matching_range()))
.collect(),
);
(matching, unmatching)
}
}
fn possible_ranges(&self, wfs: &Workflows, ranges: XmasRanges) -> (u64, XmasRanges) {
let (matching, unmatching) = self.predicate_result(ranges);
match &self.action {
RuleAction::Terminate(true) => (matching.count_states(), unmatching),
RuleAction::Terminate(false) => (0, unmatching),
RuleAction::Jump(wf) => (wfs.0[wf].possible_ranges(wfs, matching), unmatching),
}
}
}
#[derive(Debug)]
struct Workflow {
name: String,
rules: Vec<Rule>,
}
impl From<&str> for Workflow {
fn from(s: &str) -> Self {
let (name_s, rest_s) = s.split_once('{').unwrap();
let rules = rest_s.split_once('}').unwrap().0.split(',').map(|r| r.into()).collect();
Self {
name: name_s.into(),
rules,
}
}
}
impl Workflow {
fn execute(&self, part: &Part) -> RuleAction {
for r in &self.rules {
if r.pred.check(part) {
return r.action.clone();
}
}
panic!("unhandled part {:?}", part);
}
fn possible_ranges(&self, wfs: &Workflows, mut ranges: XmasRanges) -> u64 {
let mut accum = 0u64;
for r in &self.rules {
let count;
(count, ranges) = r.possible_ranges(wfs, ranges);
accum += count
}
accum
}
}
impl Workflows {
fn execute(&self, part: &Part) -> bool {
let mut action = RuleAction::Jump("in".into());
loop {
match &action {
RuleAction::Terminate(b) => return *b,
RuleAction::Jump(j) => {
let next_workflow = &self.0[j];
action = next_workflow.execute(part)
}
}
}
}
fn count_possible_states(&self) -> u64 {
self.0["in".into()].possible_ranges(self, XmasRanges::all())
}
}
#[derive(Debug)]
struct Workflows(HashMap<String, Workflow>);
#[derive(Debug)]
struct Part(HashMap<char, u64>);
impl From<&str> for Part {
fn from(s: &str) -> Self {
let (_, vars_s) = s.split_once('{').unwrap();
let vars = vars_s.split_once('}').unwrap().0.split(',');
let mut part = HashMap::new();
for var in vars {
let (name, val) = var.split_once('=').unwrap();
part.insert(name.chars().next().unwrap(), val.parse().unwrap());
}
Self(part)
}
}
type Parts = Vec<Part>;
// PROBLEM 1 solution
fn problem1<T: BufRead>(mut input: Lines<T>) -> u64 {
let mut wfs = Workflows(HashMap::new());
let mut parts: Parts = Vec::new();
while let Some(Ok(line)) = input.next() {
if line != "" {
let wf: Workflow = line.as_str().into();
wfs.0.insert(wf.name.clone(), wf);
} else {
break;
}
}
while let Some(Ok(line)) = input.next() {
parts.push(line.as_str().into());
}
parts
.iter()
.filter(|part| wfs.execute(part))
.map(|part| part.0.values().sum::<u64>())
.sum::<u64>()
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(mut input: Lines<T>) -> u64 {
let mut wfs = Workflows(HashMap::new());
let mut parts: Parts = Vec::new();
while let Some(Ok(line)) = input.next() {
if line != "" {
let wf: Workflow = line.as_str().into();
wfs.0.insert(wf.name.clone(), wf);
} else {
break;
}
}
while let Some(Ok(line)) = input.next() {
parts.push(line.as_str().into());
}
wfs.count_possible_states()
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"px{a<2006:qkq,m>2090:A,rfg}
pv{a>1716:R,A}
lnx{m>1548:A,A}
rfg{s<537:gd,x>2440:R,A}
qs{s>3448:A,lnx}
qkq{x<1416:A,crn}
crn{x>2662:A,R}
in{s<1351:px,qqz}
qqz{s>2770:qs,m<1801:hdj,R}
gd{a>3333:R,R}
hdj{m>838:A,pv}
{x=787,m=2655,a=1222,s=2876}
{x=1679,m=44,a=2067,s=496}
{x=2036,m=264,a=79,s=2244}
{x=2461,m=1339,a=466,s=291}
{x=2127,m=1623,a=2188,s=1013}";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 19114);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 167409079868000);
}
}

201
20/Cargo.lock generated Normal file
View File

@ -0,0 +1,201 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day20"
version = "0.1.0"
dependencies = [
"lazy-regex",
"num",
]
[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

10
20/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "day20"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
lazy-regex = "3.1.0"
num = "0.4.1"

58
20/input Normal file
View File

@ -0,0 +1,58 @@
%hm -> cr
%qc -> nd
&dh -> rm
%ph -> zz
%ps -> kc, dt
%qb -> dt
%jl -> vt, tb
%fh -> dm, gr
broadcaster -> np, mg, vd, xr
%zz -> sq
&rm -> rx
%nd -> br
%nx -> vr, vt
%qf -> dt, dv
%np -> xm, ph
%dm -> nf, gr
%sq -> kj
%bv -> fp, xm
%br -> kt
%mg -> dz, gr
&dt -> vd, dv, dh, hm, ks, hd, kq
%ks -> qf
&qd -> rm
%xr -> vt, rn
%vr -> tg, vt
%lc -> xm
%tq -> gr, fh
%cr -> kq, dt
%vd -> dt, ks
%tb -> nx
%dz -> gr, fd
&gr -> dp, mg, fd, qn
%nf -> gr
%dv -> hm
%qj -> lc, xm
%kc -> dt, gf
%gf -> dt, qb
%vh -> xm, sv
%sr -> vt
%fp -> qg, xm
%kj -> vh
%pc -> tq, gr
%kq -> hd
%xd -> xg, gr
%tg -> sr, vt
&bb -> rm
%rn -> vt, qc
%hd -> ps
%qg -> xm, qj
&dp -> rm
%qn -> pc
%kt -> jl
%sv -> bv
&vt -> bb, nd, qc, xr, br, tb, kt
%fd -> mx
&xm -> zz, sv, sq, ph, kj, np, qd
%xg -> gr, qn
%mx -> gr, xd

362
20/src/main.rs Normal file
View File

@ -0,0 +1,362 @@
use std::collections::{BinaryHeap, HashMap, VecDeque};
use std::fmt::Display;
use std::fs::File;
use std::hash::{Hash, Hasher};
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
use lazy_regex::lazy_regex;
use num::integer::lcm;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
const MODULE_PATTERN: lazy_regex::Lazy<lazy_regex::Regex> = lazy_regex!("^([%&]?)([a-z]+) -> ([a-z, ]+)$");
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
enum PulseType {
Low,
High,
}
impl From<bool> for PulseType {
fn from(value: bool) -> Self {
match value {
true => PulseType::High,
false => PulseType::Low,
}
}
}
impl From<PulseType> for bool {
fn from(value: PulseType) -> Self {
match value {
PulseType::High => true,
PulseType::Low => false,
}
}
}
impl Display for PulseType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PulseType::Low => f.write_str("low"),
PulseType::High => f.write_str("high"),
}
}
}
#[derive(Debug, Clone, Copy)]
enum ModuleKind {
FlipFlop,
Conjunction,
Broadcast,
Button,
}
#[derive(Debug, Clone)]
struct Module {
name: String,
kind: Option<ModuleKind>,
inputs: Vec<String>,
outputs: Vec<String>,
on: bool,
last_input_states: Vec<PulseType>,
}
impl Hash for Module {
fn hash<H: Hasher>(&self, state: &mut H) {
// the only states that change are 'on' and 'last_input_states', the rest are constant once created
self.on.hash(state);
self.last_input_states.hash(state);
}
}
impl Module {
fn new(name: String) -> Self {
Self {
name,
kind: None,
inputs: Vec::new(),
outputs: Vec::new(),
on: false,
last_input_states: Vec::new(),
}
}
fn source_idx(&self, name: &str) -> usize {
self.inputs.iter().position(|in_name| in_name == name).unwrap()
}
fn with_kind(name: String, kind: ModuleKind) -> Self {
let mut m = Self::new(name);
m.kind = Some(kind);
m
}
fn and_destinations(mut self, destinations: &str) -> Self {
for dest in destinations.split(", ") {
self.outputs.push(dest.into());
}
self
}
fn and_source(mut self, source: String) -> Self {
self.add_source(source);
self
}
fn add_source(&mut self, source: String) {
self.inputs.push(source);
self.last_input_states.push(PulseType::Low);
}
fn reset(&mut self) {
// relevant state is on and last_input_states
self.on = false;
for input in &mut self.last_input_states {
*input = PulseType::Low;
}
}
fn state_space(&self) -> u128 {
match self.kind {
Some(ModuleKind::Broadcast) | Some(ModuleKind::Button) => 1,
Some(ModuleKind::FlipFlop) => 2,
Some(ModuleKind::Conjunction) => 1 << self.last_input_states.len(), // 2^n
None => 1
}
}
}
impl From<&str> for Module {
fn from(s: &str) -> Self {
let Some(re_result) = MODULE_PATTERN.captures(s) else {
panic!("unparseable module: {}", s);
};
let (_, [kind, name, destinations]) = re_result.extract();
if name == "broadcaster" {
return Module::with_kind(name.into(), ModuleKind::Broadcast).and_destinations(destinations);
};
match kind {
"%" => Module::with_kind(name.into(), ModuleKind::FlipFlop).and_destinations(destinations),
"&" => Module::with_kind(name.into(), ModuleKind::Conjunction).and_destinations(destinations),
_ => panic!("invalid module kind {}", kind),
}
}
}
#[derive(Debug)]
struct Job {
signal: PulseType,
targets: Vec<String>,
from: String,
priority: usize,
}
impl PartialEq for Job {
fn eq(&self, other: &Self) -> bool {
self.priority == other.priority
}
}
impl Eq for Job {}
impl PartialOrd for Job {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
other.priority.partial_cmp(&self.priority)
}
}
impl Ord for Job {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
}
#[derive(Debug)]
struct Machine {
modules: HashMap<String, Module>,
work_queue: BinaryHeap<Job>,
count_low: u64,
count_high: u64,
}
impl<T: BufRead> From<Lines<T>> for Machine {
fn from(mut lines: Lines<T>) -> Self {
let mut modules = HashMap::from([(
"button".into(),
Module::with_kind("button".into(), ModuleKind::Button).and_destinations("broadcaster"),
)]);
while let Some(Ok(line)) = lines.next() {
let mut module = Module::from(line.as_str());
for output in &module.outputs {
if let Some(output_mod) = modules.get_mut(output) {
// already exists, push to input list
output_mod.add_source(module.name.to_owned());
} else {
// forward declaration of 'unknown' modules
modules.insert(
output.clone(),
Module::new(output.clone()).and_source(module.name.clone()),
);
}
}
if let Some(existing_mod) = modules.get(&module.name) {
module.inputs = existing_mod.inputs.clone(); // might have been pre-prepared, the rest we take ours
module.last_input_states = existing_mod.last_input_states.clone();
}
modules.insert(module.name.clone(), module);
}
Machine {
modules,
work_queue: BinaryHeap::new(),
count_low: 0,
count_high: 0,
}
}
}
impl Machine {
fn press_button(&mut self) {
self.send_pulse(PulseType::Low, "broadcaster", "button", 1);
}
fn send_pulse(&mut self, signal: PulseType, target: &str, from: &str, priority: usize) {
// count pulse when it is received
match signal {
PulseType::Low => self.count_low += 1,
PulseType::High => self.count_high += 1,
};
match self.modules[target].kind {
Some(ModuleKind::Button) | Some(ModuleKind::Broadcast) => {
self.send_all_outputs(PulseType::Low, target, priority)
}
Some(ModuleKind::FlipFlop) => match signal {
PulseType::High => (),
PulseType::Low => {
let new_state = !self.modules.get_mut(target).unwrap().on;
self.modules.get_mut(target).unwrap().on = new_state;
self.send_all_outputs(new_state.into(), target, priority)
}
},
Some(ModuleKind::Conjunction) => {
let target_m = self.modules.get_mut(target).unwrap();
let source_idx = target_m.source_idx(from);
target_m.last_input_states[source_idx] = signal;
if target_m.last_input_states.iter().all(|state| *state == PulseType::High) {
self.send_all_outputs(PulseType::Low, target, priority)
} else {
self.send_all_outputs(PulseType::High, target, priority)
}
}
None => (),
}
}
fn send_all_outputs(&mut self, signal: PulseType, from: &str, priority: usize) {
self.work_queue.push(Job {
signal,
targets: self.modules[from].outputs.iter().map(|op| op.to_owned()).collect(),
from: from.to_owned(),
priority: priority + 1,
})
}
fn run(&mut self) {
while let Some(job) = self.work_queue.pop() {
for target in job.targets {
self.send_pulse(job.signal, &target, &job.from, job.priority);
}
}
}
// the number of cycles it took until the target sends a low pulse, or None if the target node sent no low pulses but the job finished
fn time_to_state(&mut self, goal:&str, state: PulseType) -> Option<u64> {
while let Some(job) = self.work_queue.pop() {
if job.from == goal && job.signal == state {
return Some(self.count_high + self.count_low);
}
for target in job.targets {
self.send_pulse(job.signal, &target, &job.from, job.priority);
}
}
None
}
fn reset(&mut self) {
for (_name, m) in &mut self.modules {
m.reset();
}
self.count_low = 0;
self.count_high = 0;
}
fn state_space(&self) -> u128 {
self.modules.values().map(|m| m.state_space()).product()
}
}
// PROBLEM 1 solution
const PROBLEM1_ITERATIONS: usize = 1000;
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut machine = Machine::from(input);
for _ in 0..PROBLEM1_ITERATIONS {
machine.press_button();
machine.run();
}
machine.count_low * machine.count_high
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u128 {
let mut machine = Machine::from(input);
println!("STATES: {}", machine.state_space());
// Find the rx module and look at its parent(s)
let rx = &machine.modules["rx"];
// my input has a single conjunction as parent, todo implement other possibilities
let con = &machine.modules[&rx.inputs[0]];
// for each input, find how long it takes for it to be High, so the conjunction sends a low to rx
let mut cycles: Vec<_> = Vec::new();
for input in con.inputs.clone() {
print!("searching distance to {}...", input);
machine.reset();
let mut button_count = 0;
loop {
machine.press_button();
button_count += 1;
if let Some(distance) = machine.time_to_state(&input, PulseType::High) {
println!("got {} pulses, {} button presses", distance, button_count);
cycles.push(button_count as u128);
break;
}
}
}
cycles.iter().fold(1, |accum, cycle| lcm(accum, *cycle))
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 11687500);
}
}

465
21/Cargo.lock generated Normal file
View File

@ -0,0 +1,465 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
dependencies = [
"cfg-if",
]
[[package]]
name = "day21"
version = "0.1.0"
dependencies = [
"indicatif",
"polyfit-rs",
"primes",
"rayon",
"test-case",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "nalgebra"
version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20bd243ab3dbb395b39ee730402d2e5405e448c75133ec49cc977762c4cba3d1"
dependencies = [
"approx",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01fcc0b8149b4632adc89ac3b7b31a12fb6099a0317a4eb2ebff574ef7de7218"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "polyfit-rs"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab98d9704c7300e37472a6790a447eaf501d664a1889737faa53c26790d2b697"
dependencies = [
"nalgebra",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "primes"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a61082d8bceecd71a3870e9162002bb75f7ba9c7aa8b76227e887782fef9c8"
[[package]]
name = "proc-macro2"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "safe_arch"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354"
dependencies = [
"bytemuck",
]
[[package]]
name = "simba"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "test-case"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8"
dependencies = [
"test-case-macros",
]
[[package]]
name = "test-case-core"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.42",
]
[[package]]
name = "test-case-macros"
version = "3.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.42",
"test-case-core",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "wide"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

13
21/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "day21"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indicatif = "0.17.7"
polyfit-rs = "0.2.1"
primes = "0.3.0"
rayon = "1.8.0"
test-case = "3.3.1"

131
21/input Normal file
View File

@ -0,0 +1,131 @@
...................................................................................................................................
..............#...#........#...............#.#.......#....##...........#.#..#......#.....#.........#..##...........................
.#..##..##..#......#.#.#...##......###...##......#.#..#...................#.....#..........#..#................#...#..........#....
.....................#..#.......#.....##.........#.......................................#........#....#.#..........#.#......#.#...
......##...#.....#.......#...#..###..........#..........#......................#........#.............#.....#........#....#........
.................##.................#...#.........#................................#........#...........#............#........#....
..............#.#....#........##.....##..........#.............##.............#...#......#..##..........#.........#.....#...#..#...
..#....#.......#...##..#......#.........#.#..#....#.#..............#........#...#...#...............#.#.....#..#..##......#...#....
...#......##...##..#...#..........#...#..........#..............#...#..........#..#..#..#........#.#.#.#.#...........###...........
.#.#.#...#.#...##............................................#......................#...#......#......##.#.........................
.##.#.........#...#..................##...........#............#..................###...#...........#.................#....#.......
.....................#...#.#................#................#.#........................#...#...##....##....#.#...#.........#...#..
...#.......#...#.........................#.#..................#........##..........................#......##....##............#....
......................#.....................#...............#.......#.#...................#...............#...#........#.##......#.
............................#..........................#........#....#.##..#.......#.##..#...#.#..#...............#.#..#....#......
.......................................................#.#..#.#....#......................#............#....#..#.......#.........#.
.....##........#...............#.......#...............#.#..........##..#...................#................#..#...#.#....#...###.
....#.....................##................#.......#.......#...#.#...#...............#................#...............##..........
....#........#..#.......#.#.#........................#.#...........#.......#..#.........#....#.......#........#.#.#................
......###.....#..#...........#.....#..#..#........#......#.#........###.....##............#..#...........#.........................
.....#...........#...................#..#..........#..#...###.#.........#.##...............#..##..........#.........#..............
.#.........#....#............#.##..........................##.....#........#....#.............#.......#...#..............##.#......
.#..#...#.......#...#.......##.......#.#.............................#...#...#.#...........#...#.................#.........#.#.#...
...#....................#.....##..............#.................#.................#............#......#....#.......#.........#.....
.......#...#.............#.##...#.#...................#.....#..#........#................................#.............#..#...##...
...#.###..............#....#........................#......#...#.........#......#.#....................#........#...#...#.......#..
............#.#...#...........................#....###.###...#........#..#........#...................##.....###......#........#...
.....#...#................#.##..##..................#....#.........#............##...................##................#.....#.....
.#....#..............##...#..................#......#..........##.#....#......##.#...............#....##..............#....#.......
....#.#.##...........##.....#...........#.........#...#........#..#.......#......#................#.#...#....#...#.................
.......#...#...#...#..#.......#..........#..................#..#........#..#.#.....#.#.....................##......................
.....##.........#.......##...#.............#.##.#.#...#.##.#...#......#...#....#.......#..#.........#...........#.##.###.#.....#...
...................#.#.......................#.....#.#......#.....##...#.#....#..#..........#...........#..##....#...#.......#.#...
.....#..#..#.........#...#.#.........#...#.#...##...#......##........#...............#.......#................#.....#...#.#........
........#.....###.....................#.........#.........#.........#....##............#..##.............#.........#..#.......#....
.#.........#....##..##.................#...#................................#...#.....#.........#.......#............#....#......#.
..#.........#.......#..................#..#.#...#........#.#.......#...##.#........#..........#.#..........#..#.....#.........#....
..#...............#...................##.#.......#...#..........#....................##.........#.................#..#......##..#..
.#........#.......#............##...........##........#.......................#................#....................#...###......#.
........###..........#..............#.............#..#.#.#............##...............#........#...#........#...................#.
...#............................#...##.##........#......#..#...................#.#.....#...#.##...............#........#...#.......
..#...##....................#.....##......#.......#..........#......#.........#...#..............#..............#..................
.....#..#....................#....###.....#..#.............#...#....#................#.....#.....................#........#........
.....#.#...#.....#..............##............#....##.#....#.#........##...........#....#.........................#....#......##...
....#..#.....##...........#.#.#.#............#....#.....#.........#........................##.......#.#..............##...#........
...............#..............#................................#....#........#.......#.......##......#.#............#......#....#..
.#......#....##........#.............#..#.#.............#...#..#.........#.........#................#..................#...........
.........#..............#....#......#.....##......#............#....#.........#....#...................#....#.........#......#..#..
..............................#....#.#........#.#.....#..#.................#....#...#..........#............##.......#.#.#....#....
.#..................#....#..#.......#.##..............#...#.......#................#...............................................
.#..##.........................#.....###......#....#......#.....#............#.#......##......##......##....#..........##.....#....
..........................##.#.....#..........................................#.#..#....#..#..................#...........#........
..##.....#.......#....#.........#..........#....................#.........#........###........................#..........#.........
.#.#...............#..........#.......#.....#....##.#..#.##...............#...#...#.#......#.......#.......#.#.............#....##.
..................#...............##.........##..#........#..#..........#.....................#............................#.......
.....#................#...##.......##.........#.....#.#....#.#........#...#..#....#..............#..#....#....#....#...............
..##.#........#...............#.#..#...................##...#......#.#...#..#.....##.................##...#.#...................#..
...#...............#......#.....#.......#......#.........#...#......#...#..#....#..#......#.......#.......#...#.##.#...........##..
..............##..........##..........#..##...............#...........##..#..#..#.#.......#...#.............#......................
..........#.#........................#...#.#..#.#...##..#.#.#.#.......#....#.#.#..............#...#...#........###..##...........#.
......................#...#.#..##.....#....#.....###.#..#.....#..........#........#...........##...#..#...........##..#............
..................................####.#..............#.................#........#.#............##......#.#..........#.............
..................#..........#.............................#...........#..#..#......#................#.......##...##..#............
....................#...#....#.#.#.##......#................#.................#...........#....#..........#....#...##.###..........
.................#.......#...#............###.###..#.......#......#.......#..##......#.#.....#.....................#...............
.................................................................S.................................................................
.......##...........#.........###.......#....###.#.#....#.#....#....#........#.......#...#...#.#.........#...................#.....
.......#...............#............#...#..#.......##...#...#.#............#.............#......##..........#..............#.......
.......##.........#.....................##...#...#.................#...#.......#..####......#.....#..............#..##.............
........#..#.............#.#..##..............#...#.............#.....#......#...#.#.......#...............##...##.................
.........#..#...#..#.#..#..#...................##......#.....#..#...##..............#......#..........#.##...#.#.##.#..............
..............#..........#....................................#.........#.......#..#............##................#................
...........#...#.....#.........#....#..###....#...#.......#....#...............##.....#.......#.#.....#.#....#.#..#................
..#...................#........###..............#.#................#...###...##.............#.###......#...#...#.....#..........#..
.#.#............#.....#....###......................................#..........................#..................#................
................#..#...#................#.................##.................##..........#.......#...#.............#...............
.....##...........#....#.......#......#.#.....#.....#................#.....#.........#..#.................#........#........#......
.#.#.............#..#.#..............#.....#..#.#.....#..#......#.........#.....#.#......#..#.......#.#......##............#..#....
...................#............#....#...........#........#...................#.....##..............#........###...............#...
......###..........###.#.......#.................................................#..#.........#.#...#.........#.#..........#.#.....
..#.................#..#..#...#............#...#...#..##.#....##..#..................##.....#......#.........................#...#.
......................#.#.....#.#...#.......##...........#...#....#.............#.......#.............#.#.#.#......................
......#..#.#..........#....#.........#......#................#.........##.#....#.......#........#.#....................#......#....
..#....#....#...............#..........#...#.#......#...................#.........#..#.#......#..#.........................##....#.
..............#..........##......#.##...#...#.#................#...#..#.......#.#.....#........#...###..##.........#..##.........#.
........##...........................##.......#.#..................#........#.......#.#........#.........................##....#...
..#.#...........#.......................#.#.....#....#.....#...............####...#..............#...#....................#.#......
.......#...#....#.#.......#####...##.....#.#.......#....#.#........#................................#.##..........##....#..#.......
.........#.....#................##.#..................#.....................#...#................#.###.#.......###....#............
............#.#....#........#....#..................#...##.............#............#.......#.....#..#............##...............
....#.....#..........#..............#...#...#........##.....#.#....#....#........##.............................#.#.......#........
..#..#.#.......#.................#................................#......##.#..#..............#.#................#...#.#...........
.....#.....#........................#..#................#......#.......#.........###.#.#.........#.........#.....#.......#.........
.......#.......................................#.......#...............#.#..###...#.#.##.......#...........#.#.....#.##........#...
......##.........#.................................#.........#........##.#................#.#...#.............#...#..#........#....
......#.....#.#.......#...........#..#...........#.........#..#.#.......#.........##.....#....#.#...............#...#...#....#...#.
..#...#.#....#....#..............................#..#...#.......#.#.#..##..................#...............#......#..###..#.#......
.....#...#..#......#.....##...............#..........#..#.#####.......#...........#...................#.....#...#.........#..#.#...
.................#.......#..................#........#.......##...##.#....#.#.............#.............#....#....#................
...........####..#.....#....#.............#.......#...#..#.....#.............#...#....#.............#.#....#.#.............#.......
...#..#..#...#...#.#.....#.......................#....#..###.###.....#.........#....................#....#..#............#.....#...
.#.##....##...#......#.#.....#......................#.##..................#........#..#................#.#.......#.............#...
....##........#.......#..#..#............#..#..#..#...#......###..#..#..#...##...............................#.#........#......#.#.
..#..#..........#..........#..#.#.............#.....#...........#.##.......#............................#...........#...#..........
..#.#..#...#....#.....#.....#...#................#.....#.....#....#..#...#..#....#....#.....................#....#.................
.......#........#...#.............#.................#...#.#.....#.#....#..........#......................#..#............#.........
....#........#........#..............#.........................#...................#..........#.........#..#.........#.............
................#........#.........#............................#.#.............#..##.......#......#..#.................#......#.#.
..#.....#.........#...#..#.#.........................#...#...#...........#.....#...........#........#.............#.....#........#.
....#...#....#......#....#...#..#.................#...##.....#.#............................##...#...#..##.#.....................#.
...#..##...............#............................#...........#......#.#.....#............#........#.##................#.........
................#..#.............#..#..............#.#....#...#.#...#...#.#.##.............................#..#...........#........
.......#.......#....###............#.#.#.....................#.#..#....#.................##.....#.#..........##..#......#..........
.......#..........##.#.......#.##...#...............##.....#..........#................#............#...#........#........#........
...###.#.............................#.....#.#............##..#...#.....###..........#...............................#.....#....#..
....###.#...#.......#.##........#......#..##.#..........#.#...#.#....#.#..............##....#..#...#...........#...#.#..#..........
.....#....#.#..##.....#..........#..#...##..............#.......#.......#.............#..#.....#.......#.....#...##...###.......#..
........##...#...##..#.#.....#..........................#.#.#..#..#...................#....#....#..#....#..........#....##.........
...........##.....#...#........##..#........#....#..........#..#...............................................#......#.#.......#..
...#..##.........##....#...........##...#.......##.........#........#...........#........#.#.##...........#....#.....###...#.......
...#...#........................#..#...#..#........................................#......#..#...#.#....#...........#..............
...#..#.......#..#......##...................#...............#.....#.#.............#.....#.........#.#..##.....#..........#..#.....
....###....#..#.....#.#........#.##.......##...#...............#...............###.###.#...#............##.....#.................#.
....#...#.........#.##............#.....#........#............................................##.#......#..##.........#....###..#..
............#...##..#..........#........#.......#.#..........................#..#.#.......#......#.......................##.#..#...
....#......#....#....#..#.....#........#......#....#..#.#..............................#..#....#............#....#.................
..#.............#...#......#...#.....................#.##...............................##...#.......#........................#.##.
....#....##........#..............#....#.......#.....................................#........#........#...##..........#...........
....#.#.##..#..#....................#.................#.##.............#....#....#.............###...#...#..##....#.....##.........
.............#.......#...............#........#.#.....#..#.#................#.....#..##.....#....#...............#....#......#...#.
...................................................................................................................................

230
21/src/main.rs Normal file
View File

@ -0,0 +1,230 @@
use core::panic;
use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::collections::HashSet;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
type Position = (isize, isize);
type WrappedPosition = (usize, usize);
type Offset = (isize, isize);
#[derive(Debug, Clone)]
struct MapTile {
c: char,
}
impl MapTile {
fn new(c: char) -> Self {
Self { c }
}
}
struct GardenMap {
map: Vec<Vec<MapTile>>,
start: Position,
}
impl<T: BufRead> From<Lines<T>> for GardenMap {
fn from(lines: Lines<T>) -> Self {
let map = lines
.map(|line| line.unwrap().chars().map(|c| MapTile::new(c)).collect())
.collect();
let mut new = Self { map, start: (0, 0) };
new.find_start();
new
}
}
const ADJACENCIES: [Offset; 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
impl GardenMap {
fn wrap_pos(&self, pos: &Position) -> WrappedPosition {
let (width, height) = (self.width() as isize, self.height() as isize);
(
if pos.0 < 0 {
(pos.0 + (-pos.0 / width + 1) * width) as usize % self.width()
} else {
pos.0 as usize % self.width()
},
if pos.1 < 0 {
(pos.1 + (-pos.1 / height + 1) * height) as usize % self.height()
} else {
pos.1 as usize % self.height()
},
)
}
fn width(&self) -> usize {
self.map[0].len()
}
fn height(&self) -> usize {
self.map.len()
}
fn at(&self, pos: &Position) -> &MapTile {
let pos = self.wrap_pos(pos);
&self.map[pos.1][pos.0]
}
// return the valid 'moves' from pos
fn adjacent_to(&self, pos: &Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| self.offset_pos(pos, ofs))
.filter(|pos| self.at(pos).c != '#')
.collect()
}
fn offset_pos(&self, pos: &Position, ofs: &Offset) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
return Some((new_pos.0, new_pos.1));
}
fn find_start(&mut self) {
for (y, row) in self.map.iter().enumerate() {
for (x, tile) in row.iter().enumerate() {
if tile.c == 'S' {
self.start = (x as isize, y as isize);
return;
}
}
}
panic!("didn't find the start square!");
}
fn reachable_after(&self, from: &Position, n: usize) -> u64 {
let bar = ProgressBar::new(n as u64).with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
);
let mut visited_after: Vec<HashSet<Position>> = Vec::new();
visited_after.push(HashSet::from([*from]));
for i in 1..n+1 {
visited_after.push(
visited_after[i - 1]
.iter()
.flat_map(|last| self.adjacent_to(last))
.collect(),
);
bar.inc(1);
// if primes::is_prime(i as u64) {
// println!("count after {} steps: {}", i, visited_after[i].len());
// }
}
visited_after[n].len() as u64
}
fn reachable_count_after(&self, from: &Position, n: usize) -> u64 {
let dim = self.width() as f64;
let target_mod = (n % self.width()) as f64;
let x_values:Vec<f64> = vec![target_mod, target_mod + dim, target_mod + 2.*dim];
let y_values:Vec<f64> = x_values.iter().map(|n| self.reachable_after(from, *n as usize) as f64).collect();
let coeffs = polyfit_rs::polyfit_rs::polyfit(
&x_values,
&y_values,
2,
).unwrap();
println!("values: x: {:?} y: {:?}", x_values, y_values);
println!("coefficients: {:?}", coeffs);
let f_n= n as f64;
let result = coeffs[0] + coeffs[1] * f_n + coeffs[2] * f_n.powf(2.0);
result.round() as u64
}
fn draw_with_bounds(&self, from: &Position, to: &Position) {
for row in from.1..to.1 + 1 {
for col in from.0..to.0 + 1 {
print!("{}", self.at(&(col, row)).c);
}
println!();
}
}
}
// fn print_visited(map: &GardenMap, visited: &Vec<Vec<bool>>) {
// for (y, row) in visited.iter().enumerate() {
// for (x, cell) in row.iter().enumerate() {
// print!("{}", if *cell { 'O' } else { map.at(&(x, y)).c });
// }
// println!();
// }
// }
// PROBLEM 1 solution
fn problem1_impl<T: BufRead>(input: Lines<T>, n: usize) -> u64 {
let map = GardenMap::from(input);
// println!("map: {:?} start: {:?}", map.map, &map.start);
// map.draw_with_bounds(
// &(-(map.width() as isize), -(map.height() as isize)),
// &(map.width() as isize * 2 + 1, map.height() as isize * 2 + 1),
// );
map.reachable_count_after(&map.start, n)
}
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, 64)
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, 26501365)
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
use test_case::test_case;
const EXAMPLE: &str = &"...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), 6), 16);
}
#[test_case(6, 16)]
#[test_case(10, 50)]
#[test_case(50, 1594)]
#[test_case(100, 6536)]
#[test_case(500, 167004)]
fn problem2_example(n: usize, expect: u64) {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), n), expect);
}
}

128
22/Cargo.lock generated Normal file
View File

@ -0,0 +1,128 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day22"
version = "0.1.0"
dependencies = [
"itertools",
"ndarray",
"petgraph",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"

11
22/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "day22"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
ndarray = "0.15.6"
petgraph = "0.6.4"

385
22/src/main.rs Normal file
View File

@ -0,0 +1,385 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::prelude::*;
use petgraph::visit::{IntoNodeReferences, Walker};
use std::collections::BinaryHeap;
use std::fmt::{Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::str::FromStr;
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Hash, PartialEq, Eq, Clone, Debug)]
struct Coord {
x: usize,
y: usize,
z: usize,
}
impl FromStr for Coord {
type Err = Box<dyn std::error::Error>;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let (x, y, z) = value.split(',').next_tuple().unwrap();
Ok(Self {
x: x.parse()?,
y: y.parse()?,
z: z.parse()?,
})
}
}
#[derive(Clone, Hash, PartialEq, Eq)]
struct BrickBlock {
c1: Coord,
c2: Coord,
}
impl std::fmt::Debug for BrickBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"[{},{},{} - {},{},{}]",
self.c1.x, self.c1.y, self.c1.z, self.c2.x, self.c2.y, self.c2.z
)
}
}
impl BrickBlock {
fn bottom_z_plane(&self) -> usize {
std::cmp::min(self.c1.z, self.c2.z)
}
fn top_z_plane(&self) -> usize {
std::cmp::max(self.c1.z, self.c2.z)
}
fn bottom_x_plane(&self) -> usize {
std::cmp::min(self.c1.x, self.c2.x)
}
fn top_x_plane(&self) -> usize {
std::cmp::max(self.c1.x, self.c2.x)
}
fn bottom_y_plane(&self) -> usize {
std::cmp::min(self.c1.y, self.c2.y)
}
fn top_y_plane(&self) -> usize {
std::cmp::max(self.c1.y, self.c2.y)
}
}
impl From<&str> for BrickBlock {
fn from(value: &str) -> Self {
let (c1, c2) = value.split_once('~').unwrap();
Self {
c1: c1.parse().unwrap(),
c2: c2.parse().unwrap(),
}
}
}
impl PartialOrd for BrickBlock {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(other.bottom_z_plane().cmp(&self.bottom_z_plane()))
}
}
// Note this is a reversed ordering
impl Ord for BrickBlock {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bottom_z_plane().cmp(&other.bottom_z_plane())
}
}
type BlockMap = Array3<MapTile>;
type MapTile = Option<BrickBlock>;
struct BlockPile {
blocks: Vec<BrickBlock>,
block_map: Array3<MapTile>,
bounds: (usize, usize, usize),
graph: Graph<BrickBlock, (), Directed, usize>,
}
impl BlockPile {
fn remove_block(&mut self, block: &BrickBlock) {
// loop over the (inclusive) bounding coordinates and remove them all from the map
self.block_map
.slice_mut(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.bottom_z_plane()..block.top_z_plane() + 1,
])
.fill(None);
self.blocks.remove(self.blocks.iter().position(|b| b == block).unwrap());
}
fn add_block(&mut self, block: &BrickBlock) {
// loop over the (inclusive) bounding coordinates and remove them all from the map
self.block_map
.slice_mut(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.bottom_z_plane()..block.top_z_plane() + 1,
])
.fill(Some(block.to_owned()));
self.blocks.push(block.clone());
}
fn blocks_directly_above(&self, block: &BrickBlock) -> Vec<BrickBlock> {
// find the top plane of the block
// get the array range of all squares at z_plane + 1 within the bounds of x & y
// i think there is a built in way in ndarray to do this...
let directly_above = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
block.top_z_plane() + 1..std::cmp::min(block.top_z_plane() + 2, self.bounds.2)
]);
directly_above.iter().filter_map(|v| v.clone()).unique().collect()
}
fn supported_by(&self, block: &BrickBlock) -> usize {
let z_plane = std::cmp::min(block.c1.z, block.c2.z);
// get the slice of tiles below us
let directly_below = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
z_plane - 1..z_plane // the layer below
]);
directly_below.iter().filter_map(|v| v.clone()).unique().count()
}
fn blocks_above_will_move_if_we_are_gone(&mut self, block: &BrickBlock) -> bool {
self.blocks_directly_above(&block)
.iter()
.map(|b| self.supported_by(b))
.any(|b| b == 1) // block we support will move if we are their only support
}
fn blocks_supported_by_at_all(&self, block: &BrickBlock) -> Vec<BrickBlock> {
self.blocks_directly_above(&block).iter().map(|b| b.clone()).collect()
}
/// Find the plane of the first block directly below us
fn supporting_plane(&self, block: &BrickBlock) -> Option<usize> {
// find the bottom plane of ourselves
let z_plane = std::cmp::min(block.c1.z, block.c2.z);
// get the slice of tiles below us
let directly_below = self.block_map.slice(s![
block.bottom_x_plane()..block.top_x_plane() + 1,
block.bottom_y_plane()..block.top_y_plane() + 1,
1..z_plane // don't include our own plane
]);
// find the highest top z value of those
let block_below = directly_below
.indexed_iter()
.filter_map(|(idx, v)| if let Some(val) = v { Some((idx, val)) } else { None })
.max_by_key(|(_idx, v)| v.top_z_plane());
if let Some(block) = block_below {
Some(block.1.top_z_plane())
} else {
None
}
}
fn drop_blocks(&mut self) {
// VecDeque doesn't sort and Vec isn't convenient for pushback and popfront, so eh... use a heap.
let mut blocks_to_move = BinaryHeap::from(self.blocks.clone());
while let Some(mut block) = blocks_to_move.pop() {
let z_move = match self.supporting_plane(&block) {
Some(z) if z + 1 != block.bottom_z_plane() => block.bottom_z_plane() - (z + 1),
None if block.bottom_z_plane() != 1 => block.bottom_z_plane() - 1,
_ => {
continue;
} // we are in position already with nothing below us
};
self.remove_block(&block);
block.c1.z -= z_move;
block.c2.z -= z_move;
self.add_block(&block);
blocks_to_move.push(block);
}
}
fn build_graph(&mut self) {
self.blocks.sort_by_key(|b| b.bottom_z_plane());
for b in 0..self.blocks.len() {
self.graph.add_node(self.blocks[b].clone());
}
for b in 0..self.blocks.len() {
let block = &self.blocks[b];
let depends_on_us = self.blocks_supported_by_at_all(block);
for dependent in depends_on_us {
self.graph.add_edge(
b.into(),
self.blocks.iter().position(|b| b == &dependent).unwrap().into(),
(),
);
}
}
}
}
impl<T: BufRead> From<Lines<T>> for BlockPile {
fn from(lines: Lines<T>) -> Self {
let blocks = lines.map(|line| BrickBlock::from(line.unwrap().as_str())).collect_vec();
let mut bounds = (0, 0, 0);
for block in &blocks {
bounds.0 = std::cmp::max(block.top_x_plane() + 1, bounds.0);
bounds.1 = std::cmp::max(block.top_y_plane() + 1, bounds.1);
bounds.2 = std::cmp::max(block.top_z_plane() + 1, bounds.2);
}
let mut new = BlockPile {
blocks: Vec::new(),
bounds,
block_map: BlockMap::from_elem(bounds, None),
graph: Graph::default(),
};
for block in blocks {
new.add_block(&block);
}
new
}
}
impl Display for BlockPile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for z in (0..self.bounds.2).rev() {
let z_plane = self.block_map.slice(s![.., .., z..z+1]);
if z_plane.iter().all(|b| b.is_none()) { continue };
for i in 0..self.bounds.0 {
// XZ view
let y = z_plane.slice(s![i..i + 1, .., ..]);
f.write_char(
match y.iter().enumerate().find_map(|(idx, val)| val.is_some().then(|| idx)) {
None => ' ',
Some(0) | Some(1) => '█',
Some(2) | Some(3) => '▓',
Some(4) | Some(5) => '▒',
Some(6) | Some(7) => '░',
_ => '░',
},
)?;
}
write!(f, " {} ", z)?;
for y in 0..self.bounds.1 {
let x = z_plane.slice(s![.., y..y + 1, ..]);
f.write_char(
match x.iter().enumerate().find_map(|(idx, val)| val.is_some().then(|| idx)) {
None => ' ',
Some(0) | Some(1) => '█',
Some(2) | Some(3) => '▓',
Some(4) | Some(5) => '▒',
Some(6) | Some(7) => '░',
_ => '░',
},
)?;
}
writeln!(f, " {}", z)?;
}
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut pile = BlockPile::from(input);
println!("{}", pile);
println!("dropping blocks!");
pile.drop_blocks();
println!("{}", pile);
let blocks = pile.blocks.clone();
let removable: Vec<_> = blocks
.iter()
.filter(|b| !pile.blocks_above_will_move_if_we_are_gone(*b))
.collect();
removable.len() as u64
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut pile = BlockPile::from(input);
pile.drop_blocks();
pile.build_graph();
println!("{}", pile);
let mut accum = 0;
let fixed_nodes = pile
.graph
.node_references()
.filter(|(_idx, b)| b.bottom_z_plane() == 1)
.map(|(idx, _b)| idx)
.collect_vec();
for node in pile.graph.node_indices() {
// remove links to node's neighbors
let dependents = pile.graph.neighbors(node).collect_vec();
let edges = pile.graph.edges(node).map(|v| v.id()).collect_vec();
for edge in edges {
pile.graph.remove_edge(edge);
}
// find how many nodes are reachable from z = 1 - these won't move
let safe_blocks = fixed_nodes
.iter()
.flat_map(|origin| {
Bfs::new(&pile.graph, *origin)
.iter(&pile.graph)
.map(|n| pile.graph[n].clone())
})
.unique()
.count();
// we are looking for the nodes that *will* disintegrate
accum += pile.graph.node_count() - safe_blocks;
// put the graph back how it was
for neigh in dependents {
pile.graph.add_edge(node, neigh, ());
}
}
accum as u64
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"1,0,1~1,2,1
0,0,2~2,0,2
0,2,3~2,2,3
0,0,4~0,2,4
2,0,5~2,2,5
0,1,6~2,1,6
1,1,8~1,1,9";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 5);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 7);
}
}

128
23/Cargo.lock generated Normal file
View File

@ -0,0 +1,128 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day23"
version = "0.1.0"
dependencies = [
"itertools",
"ndarray",
"petgraph",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "matrixmultiply"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"

11
23/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "day23"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
ndarray = "0.15.6"
petgraph = "0.6.4"

326
23/src/main.rs Normal file
View File

@ -0,0 +1,326 @@
use itertools::Itertools;
use ndarray::prelude::*;
use petgraph::algo::all_simple_paths;
use petgraph::prelude::*;
use std::collections::{HashMap, HashSet};
use std::fmt::{Debug, Display, Write};
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
#[derive(Clone)]
struct Node {
c: char,
pos: Position,
}
impl Display for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.0, self.pos.1)
}
}
impl Debug for Node {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({},{})", self.pos.0, self.pos.1)
}
}
type Position = (usize, usize);
#[derive(Clone)]
struct ForestMap {
map: Array2<char>,
indexes: HashMap<Position, NodeIndex>,
graph: StableDiGraph<Node, u64>,
start: Position,
end: Position,
}
const ADJACENCIES: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, -1), (0, 1)];
fn offset_pos(map: &Array2<char>, pos: Position, ofs: (isize, isize)) -> Option<Position> {
let new_pos = (pos.0 as isize + ofs.0, pos.1 as isize + ofs.1);
if new_pos.0 >= 0
&& new_pos.0 < map.len_of(Axis(0)) as isize
&& new_pos.1 >= 0
&& new_pos.1 < map.len_of(Axis(1)) as isize
{
Some((new_pos.0 as usize, new_pos.1 as usize))
} else {
None
}
}
fn adjacent_to(map: &Array2<char>, pos: Position) -> Vec<Position> {
ADJACENCIES
.iter()
.filter_map(|ofs| offset_pos(map, pos, *ofs))
.collect()
}
impl<T: BufRead> From<Lines<T>> for ForestMap {
fn from(lines: Lines<T>) -> Self {
let rows = lines.map(|line| line.unwrap().chars().collect_vec()).collect_vec();
let map = Array::from_shape_vec([rows[0].len(), rows.len()], rows.into_iter().flatten().collect_vec())
.unwrap()
.reversed_axes();
let start = (map.slice(s![.., 0]).iter().position(|c| *c == '.').unwrap(), 0);
let end = (
map.slice(s![.., map.len_of(Axis(1)) - 1])
.iter()
.position(|c| *c == '.')
.unwrap(),
map.len_of(Axis(1)) - 1,
);
let mut graph = StableGraph::default();
let mut indexes = HashMap::new();
for (pos, c) in map.indexed_iter() {
if *c != '#' {
indexes.insert(pos, graph.add_node(Node { c: *c, pos }));
}
}
Self {
map,
start,
end,
graph,
indexes,
}
}
}
impl ForestMap {
fn build_graph(&mut self) {
for (pos, c) in self.map.indexed_iter() {
match c {
'#' => continue,
'.' => {
adjacent_to(&self.map, pos).iter().for_each(|adj| {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[adj], 1);
}
});
}
'^' => {
if let Some(adj) = offset_pos(&self.map, pos, (0, -1)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'>' => {
if let Some(adj) = offset_pos(&self.map, pos, (1, 0)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'v' => {
if let Some(adj) = offset_pos(&self.map, pos, (0, 1)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
'<' => {
if let Some(adj) = offset_pos(&self.map, pos, (-1, 0)) {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[&adj], 1);
}
}
}
c => panic!("invalid map character {}", c),
}
}
}
fn build_graph2(&mut self) {
for (pos, c) in self.map.indexed_iter() {
match c {
'#' => continue,
'.' | '^' | '>' | 'v' | '<' => {
adjacent_to(&self.map, pos).iter().for_each(|adj| {
if self.indexes.contains_key(&adj) {
self.graph.add_edge(self.indexes[&pos], self.indexes[adj], 1);
}
});
}
c => panic!("invalid map character {}", c),
}
}
}
// Cull nodes that don't change the topology of the graph and combine their cost
fn simplify_graph(&mut self) {
let mut idxs: Vec<_> = self
.graph
.neighbors(self.indexes[&self.start])
.map(|idx| (self.indexes[&self.start], idx))
.collect();
let mut visited = HashSet::from([self.indexes[&self.start]]);
while let Some((last_idx, cur_idx)) = idxs.pop() {
if !visited.insert(cur_idx) {
continue;
}
let our_neighbors = self.graph.neighbors(cur_idx).collect_vec();
// if we have exactly 2 neighbours, then one is where we came from, and we can shortcut this node with a
// pair of new edges A <-> C and break the existing 4 edges between them
if our_neighbors.len() == 2 {
let next_idx = our_neighbors.iter().find(|n| **n != last_idx).unwrap();
// remove the 4 existing edges
// careful of order of operations, as removing edges invalidates edge indexes
let forward_cost = self
.graph
.remove_edge(self.graph.find_edge(cur_idx, *next_idx).unwrap())
.unwrap();
let last_forward_cost = self
.graph
.remove_edge(self.graph.find_edge(last_idx, cur_idx).unwrap())
.unwrap();
let backward_cost = self
.graph
.remove_edge(self.graph.find_edge(cur_idx, last_idx).unwrap())
.unwrap();
let next_backward_cost = self
.graph
.remove_edge(self.graph.find_edge(*next_idx, cur_idx).unwrap())
.unwrap();
let new_forward_cost = forward_cost + last_forward_cost;
let new_backward_cost = backward_cost + next_backward_cost;
// add edge from last to next
self.graph.add_edge(last_idx, *next_idx, new_forward_cost);
self.graph.add_edge(*next_idx, last_idx, new_backward_cost);
self.graph.remove_node(cur_idx);
// push the next node
idxs.push((last_idx, *next_idx));
} else {
// don't do anything about nodes with > 2 edges, just push them onto the stack, if there are some
idxs.append(
&mut self
.graph
.neighbors(cur_idx)
.into_iter()
.map(|next_idx| (cur_idx, next_idx))
.collect(),
);
}
}
}
}
impl Debug for ForestMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for y in 0..self.map.len_of(Axis(1)) {
for c in self.map.index_axis(Axis(1), y) {
f.write_char(*c)?;
}
writeln!(f)?;
}
Ok(())
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let mut map = ForestMap::from(input);
map.build_graph();
// println!("{:?}", map);
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| path.len()).unwrap();
longest.len() as u64 - 1
}
fn calc_path_length(map: &ForestMap, path: &Vec<NodeIndex>) -> u64 {
path.iter().tuple_windows().fold(0, |accum, (prev, next)| {
accum + map.graph[map.graph.find_edge(*prev, *next).unwrap()]
})
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
let mut map = ForestMap::from(input);
map.build_graph2();
map.simplify_graph();
let paths = all_simple_paths::<Vec<_>, _>(&map.graph, map.indexes[&map.start], map.indexes[&map.end], 0, None)
.collect_vec();
let longest = paths.iter().max_by_key(|path| calc_path_length(&map, &path)).unwrap();
longest.iter().tuple_windows().fold(0, |accum, (prev, next)| {
accum + map.graph[map.graph.find_edge(*prev, *next).unwrap()]
})
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 94);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 154);
}
}

134
24/Cargo.lock generated Normal file
View File

@ -0,0 +1,134 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "day24"
version = "0.1.0"
dependencies = [
"itertools",
"lazy-regex",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "lazy-regex"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "proc-macro2"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "syn"
version = "2.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

10
24/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "day24"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
itertools = "0.12.0"
lazy-regex = "3.1.0"

24
24/cheat.py Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python3
from z3 import *
rx, ry, rz = Ints('rx ry rz')
rvx, rvy, rvz = Ints('rvx rvy rvz')
t0, t1, t2 = Ints('t0 t1 t2')
answer = Int('answer')
solve(
rx + t0 * rvx == 225415405941969 + t0 * 23,
ry + t0 * rvy == 400648127977931 + t0 * -204,
rz + t0 * rvz == 79201130433258 + t0 * 617,
rx + t1 * rvx == 353783687623292 + t1 * -80,
ry + t1 * rvy == 138575899489956 + t1 * 156,
rz + t1 * rvz == 318416438572569 + t1 * 21,
rx + t2 * rvx == 215751176267772 + t2 * -120,
ry + t2 * rvy == 376619563956940 + t2 * 126,
rz + t2 * rvz == 230133299986253 + t2 * -352,
answer == rx + ry + rz,
)

205
24/src/main.rs Normal file
View File

@ -0,0 +1,205 @@
use itertools::Itertools;
use std::fmt::Debug;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::ops::Index;
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// Parse
#[derive(Clone, Copy)]
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl Debug for Vec3 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}, {}", self.x, self.y, self.z)
}
}
#[derive(Clone, Copy)]
struct Vec2 {
x: f64,
y: f64,
}
impl Debug for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}", self.x, self.y)
}
}
#[derive(Clone)]
struct Hailstone {
pos: Vec3,
vel: Vec3,
}
impl Hailstone {
fn xy_vel_angle(&self) -> f64 {
(self.vel.y / self.vel.x).atan()
}
fn xy_vel_abs(&self) -> f64 {
(self.vel.x.powi(2) + self.vel.y.powi(2)).sqrt()
}
fn xy_slope(&self) -> f64 {
self.vel.y / self.vel.x
}
fn y_crossing(&self) -> f64 {
self.pos.y - self.xy_slope() * self.pos.x
}
fn xy_poi(&self, other: &Hailstone) -> Option<Vec2> {
let our_slope = self.xy_slope();
let other_slope = other.xy_slope();
if our_slope == other_slope || our_slope * other_slope == -1. {
None
} else {
let our_yint = self.y_crossing();
let other_yint = other.y_crossing();
let ratio = (other_yint - our_yint) / (our_slope - other_slope);
Some(Vec2 {
x: ratio,
y: our_slope * ratio + our_yint,
})
}
}
fn xy_point_future(&self, point: &Vec2) -> bool {
// a point will be in the past if the difference between its 'new' position and its 'start' position has a
// different sign than the velocity for any component
let diffs = [point.x - self.pos.x, point.y - self.pos.y];
// for (diff, vel) in diffs.iter().zip([self.vel.x, self.vel.y].iter()) {
// println!(" diff: {:?} vel: {:?} mul: {:?}", diff, vel, diff * vel > 0.);
// }
diffs.iter().zip([self.vel.x, self.vel.y].iter()).any(|(diff, vel)| diff * vel > 0.)
}
}
impl From<&str> for Hailstone {
fn from(value: &str) -> Self {
let (pos, vel) = value.split_once("@").unwrap();
let [px, py, pz] = pos.split(",").map(|s| s.trim().parse::<f64>().unwrap()).collect_vec()[..] else {
panic!()
};
let [vx, vy, vz] = vel.split(",").map(|s| s.trim().parse::<f64>().unwrap()).collect_vec()[..] else {
panic!()
};
Self {
pos: Vec3 { x: px, y: py, z: pz },
vel: Vec3 { x: vx, y: vy, z: vz },
}
}
}
impl Debug for Hailstone {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?} @ {:?}", self.pos, self.vel)
}
}
#[derive(Debug)]
struct Hailstones {
stones: Vec<Hailstone>,
}
impl Hailstones {
fn count_xy_pois(&self, bounds: (f64, f64)) -> u64 {
assert!(bounds.0 < bounds.1);
let mut stones = self.stones.clone();
let mut count = 0;
while let Some(stone) = &stones.pop() {
for other in &stones {
let Some(poi) = stone.xy_poi(other) else { continue };
// println!("intersection: {:?} / {:?} @ {:?}", stone, other, poi);
if poi.x >= bounds.0 && poi.x <= bounds.1 && poi.y >= bounds.0 && poi.y <= bounds.1 && stone.xy_point_future(&poi) && other.xy_point_future(&poi) {
count += 1;
}
}
}
count
}
}
impl Index<usize> for Hailstones {
type Output = Hailstone;
fn index(&self, index: usize) -> &Self::Output {
&self.stones[index]
}
}
impl<T: BufRead> From<Lines<T>> for Hailstones {
fn from(value: Lines<T>) -> Self {
let mut stones = Vec::new();
for line in value {
stones.push(Hailstone::from(line.unwrap().as_str()));
}
Hailstones { stones }
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
problem1_impl(input, (200000000000000., 400000000000000.))
}
fn problem1_impl<T: BufRead>(input: Lines<T>, bounds: (f64, f64)) -> u64 {
let stones = Hailstones::from(input);
stones.count_xy_pois(bounds)
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"19, 13, 30 @ -2, 1, -2
18, 19, 22 @ -1, -1, -2
20, 25, 34 @ -2, -2, -4
12, 31, 28 @ -1, -2, -1
20, 19, 15 @ 1, -5, -3";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1_impl(c.lines(), (7., 27.)), 2);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}

266
25/Cargo.lock generated Normal file
View File

@ -0,0 +1,266 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
dependencies = [
"cfg-if",
]
[[package]]
name = "day25"
version = "0.1.0"
dependencies = [
"indicatif",
"itertools",
"petgraph",
"rayon",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fixedbitset"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "indexmap"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "indicatif"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
dependencies = [
"console",
"instant",
"number_prefix",
"portable-atomic",
"unicode-width",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
[[package]]
name = "number_prefix"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]]
name = "petgraph"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9"
dependencies = [
"fixedbitset",
"indexmap",
]
[[package]]
name = "portable-atomic"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

12
25/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "day25"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
indicatif = "0.17.7"
itertools = "0.12.0"
petgraph = "0.6.4"
rayon = "1.8.0"

171
25/src/main.rs Normal file
View File

@ -0,0 +1,171 @@
use indicatif::{ProgressBar, ProgressIterator, ProgressStyle};
use itertools::Itertools;
use petgraph::algo::connected_components;
use petgraph::dot::Dot;
use petgraph::prelude::*;
use petgraph::visit::{depth_first_search, DfsEvent, IntoEdgeReferences, IntoNodeIdentifiers, IntoNodeReferences};
use rayon::prelude::*;
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines};
use std::time::Instant;
// BOILERPLATE
type InputIter = Lines<BufReader<File>>;
fn get_input() -> InputIter {
let f = File::open("input").unwrap();
let br = BufReader::new(f);
br.lines()
}
fn main() {
let start = Instant::now();
let ans1 = problem1(get_input());
let duration = start.elapsed();
println!("Problem 1 solution: {} [{}s]", ans1, duration.as_secs_f64());
let start = Instant::now();
let ans2 = problem2(get_input());
let duration = start.elapsed();
println!("Problem 2 solution: {} [{}s]", ans2, duration.as_secs_f64());
}
// PARSE
struct Circuit {
graph: UnGraph<String, String>,
nodes: HashMap<String, Node>,
}
#[derive(Debug, Clone)]
struct Node {
targets: Vec<String>,
index: Option<NodeIndex>,
}
impl Node {
fn with_index(mut self, index: NodeIndex) -> Self {
self.index = Some(index);
self
}
}
impl<T: BufRead> From<Lines<T>> for Circuit {
fn from(lines: Lines<T>) -> Self {
let mut graph = UnGraph::default();
let mut nodes = HashMap::new();
for line in lines.map(|v| v.unwrap()) {
let (source, targets_s) = line.split_once(": ").unwrap();
let targets = targets_s.split_whitespace().map(|s| s.to_string()).collect_vec();
nodes.insert(
source.to_string(),
Node {
targets,
index: Some(graph.add_node(source.to_string())),
},
);
}
for (name, node) in nodes.iter_mut() {
for target in &node.targets {
let target_idx = match graph.node_references().find(|n| n.1 == target).map(|n| n.0) {
Some(target) => target,
None => graph.add_node(target.to_string()),
};
graph.add_edge(node.index.unwrap(), target_idx, format!("{}-{}", name, target));
}
}
Self { graph, nodes }
}
}
impl Circuit {
// returns the set of nodes on each side
fn find_critical_edges(&self, removals: usize) -> (Vec<String>, Vec<String>) {
let bar = ProgressBar::new(self.graph.edge_references().combinations(removals).try_len().unwrap() as u64)
.with_style(
ProgressStyle::with_template(
"[{elapsed_precise}/{eta_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {per_sec}",
)
.unwrap(),
);
// for set in self.graph.edge_references().combinations(removals).progress_with(bar) {
let set = [("zcp", "zjm"), ("rsg", "nsk"), ("jks", "rfg")]
.iter()
.flat_map(|(src, dst)| {
self.graph.edge_references().filter(|e| self.graph[e.source()] == *src && self.graph[e.target()] == *dst || self.graph[e.target()] == *src && self.graph[e.source()] == *dst)
}).collect_vec();
let mut local_graph: StableUnGraph<_, _> = self.graph.clone().into();
for wire in &set {
local_graph.remove_edge(wire.id());
}
let local_graph = UnGraph::from(local_graph);
if connected_components(&local_graph) == 2 {
// each edge will have one leg in each component, so return the set of connected nodes
let mut left = Vec::new();
depth_first_search(&local_graph, Some(set[0].source()), |event| {
if let DfsEvent::Discover(n, t) = event {
left.push(local_graph[n].to_string());
}
});
let mut right = Vec::new();
depth_first_search(&local_graph, Some(set[0].target()), |event| {
if let DfsEvent::Discover(n, t) = event {
right.push(local_graph[n].to_string());
}
});
return (left, right);
}
// }
panic!("no cutline found");
}
}
// PROBLEM 1 solution
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
let circuit = Circuit::from(input);
println!("{:?}", Dot::new(&circuit.graph));
let (left, right) = circuit.find_critical_edges(3);
(left.len() * right.len()) as u64
}
// PROBLEM 2 solution
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
0
}
#[cfg(test)]
mod tests {
use crate::*;
use std::io::Cursor;
const EXAMPLE: &str = &"jqt: rhn xhk nvd
rsh: frs pzl lsr
xhk: hfx
cmg: qnr nvd lhk bvb
rhn: xhk bvb hfx
bvb: xhk hfx
pzl: lsr hfx nvd
qnr: nvd
ntq: jqt hfx bvb xhk
nvd: lhk
lsr: lhk
rzs: qnr cmg lsr rsh
frs: qnr lhk lsr";
#[test]
fn problem1_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem1(c.lines()), 54);
}
#[test]
fn problem2_example() {
let c = Cursor::new(EXAMPLE);
assert_eq!(problem2(c.lines()), 0);
}
}