From ca251d93529864c9ea890ce3dec689209f76fd53 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Wed, 20 Dec 2023 11:03:17 -0500 Subject: [PATCH] Add another part 2 solution that needs massive optimization --- 2023/day8/README.md | 46 ++++++++++++++++++++++++++++ 2023/day8/src/example-input2.txt | 10 +++++++ 2023/day8/src/main.rs | 51 +++++++++++++++++++++++++++++++- 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 2023/day8/src/example-input2.txt diff --git a/2023/day8/README.md b/2023/day8/README.md index e0879c8..579cca6 100644 --- a/2023/day8/README.md +++ b/2023/day8/README.md @@ -47,3 +47,49 @@ ZZZ = (ZZZ, ZZZ) ``` Starting at AAA, follow the left/right instructions. **How many steps are required to reach ZZZ**? + +## Part 2 + +The sandstorm is upon you and you aren't any closer to escaping the wasteland. You had the camel follow the +instructions, but you've barely left your starting position. It's going to take **significantly more steps** to escape! + +What if the map isn't for people - what if the map is for **ghosts?** Are ghosts even bound by the laws of spacetime? +Only one way to find out. + +After examining the maps a bit longer, your attention is drawn to a curious fact: the number of nodes with names +ending in A is equal to the number ending in Z! If you were a ghost, you'd probably just **start at every node that +ends with A** and follow all of the paths at the same time until they all simultaneously end up at nodes that end with +`Z`. + +For example: + +``` +LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) +``` + +Here, there are two starting nodes, 11A and 22A (because they both end with A). As you follow each left/right +instruction, use that instruction to **simultaneously** navigate away from both nodes you're currently on. Repeat this +process until **all** of the nodes you're currently on end with Z. (If only some of the nodes you're on end with Z, they +act like any other node and you continue as normal.) In this example, you would proceed as follows: + +- Step 0: You are at `11A` and `22A`.` +- Step 1: You choose all of the **left** paths, leading you to `11B` and `22B`. +- Step 2: You choose all of the **right** paths, leading you to **11Z** and `22C`. +- Step 3: You choose all of the left paths, leading you to `11B` and **22Z**. +- Step 4: You choose all of the right paths, leading you to **11Z** and `22B`. +- Step 5: You choose all of the left paths, leading you to `11B` and `22C`. +- Step 6: You choose all of the right paths, leading you to **11Z** and **22Z**. + +So, in this example, you end up entirely on nodes that end in Z after **6** steps. + +Simultaneously start on every node that ends with A. +**How many steps does it take before you're only on nodes that end with `Z`?** diff --git a/2023/day8/src/example-input2.txt b/2023/day8/src/example-input2.txt new file mode 100644 index 0000000..a8e2c98 --- /dev/null +++ b/2023/day8/src/example-input2.txt @@ -0,0 +1,10 @@ +LR + +11A = (11B, XXX) +11B = (XXX, 11Z) +11Z = (11B, XXX) +22A = (22B, XXX) +22B = (22C, 22C) +22C = (22Z, 22Z) +22Z = (22B, 22B) +XXX = (XXX, XXX) \ No newline at end of file diff --git a/2023/day8/src/main.rs b/2023/day8/src/main.rs index 39d5290..2dc2842 100644 --- a/2023/day8/src/main.rs +++ b/2023/day8/src/main.rs @@ -86,6 +86,37 @@ impl Network { } } } + + fn count_steps_part_two(&self) -> usize { + let mut count = 0usize; + let mut next_keys: Vec<_> = self.map.keys().filter(|k| k.ends_with('A')).collect(); + let mut current: Vec<_> = next_keys + .iter() + .map(|k| self.map.get(*k).unwrap()) + .collect(); + + loop { + for x in self.direction_list.iter() { + count += 1; + next_keys = current + .iter() + .map(|c| match x { + Direction::Left => &c.0, + Direction::Right => &c.1, + }) + .collect(); + + if next_keys.iter().all(|k| k.ends_with('Z')) { + return count; + } + + current = next_keys + .iter() + .map(|k| self.map.get(*k).unwrap()) + .collect(); + } + } + } } fn part_one() { @@ -95,18 +126,36 @@ fn part_one() { println!("Part 1: Step count from AAA to ZZZ {}", step_count); } +fn part_two() { + let network = Network::parse(FILE_STR); + let step_count = network.count_steps_part_two(); + + println!( + "Part 2: Step count for steps to locations ending in Z {}", + step_count + ); +} + fn main() { part_one(); + part_two(); } #[cfg(test)] mod tests { const EXAMPLE_FILE_STR: &str = include_str!("example-input.txt"); + const EXAMPLE2_FILE_STR: &str = include_str!("example-input2.txt"); use super::*; #[test] - fn first_example() { + fn part_one_example() { let network = Network::parse(EXAMPLE_FILE_STR); assert_eq!(6, network.count_steps()); } + + #[test] + fn part_two_example() { + let network = Network::parse(EXAMPLE2_FILE_STR); + assert_eq!(6, network.count_steps_part_two()); + } }