Skip to content

Commit

Permalink
feat(2023): add day 20 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
believer committed Dec 20, 2023
1 parent 7a87342 commit 712127a
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 5 deletions.
3 changes: 3 additions & 0 deletions rust/2023/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ You try to ask why they can't just use a weather machine ("not powerful enough")
| [Day 17: Clumsy Crucible](https://github.com/believer/advent-of-code/blob/master/rust/2023/src/day_17.rs) | 🌟 | 1013 | 🌟 | 1215 |
| [Day 18: Lavaduct Lagoon](https://github.com/believer/advent-of-code/blob/master/rust/2023/src/day_18.rs) | 🌟 | 48652 | 🌟 | 45757884535661 |
| [Day 19: Aplenty](https://github.com/believer/advent-of-code/blob/master/rust/2023/src/day_19.rs) | 🌟 | 331208 | 🌟 | 121464316215623 |
| [Day 20: Pulse Propagation](https://github.com/believer/advent-of-code/blob/master/rust/2023/src/day_19.rs) | 🌟 | 812609846 | 🌟 | 245114020323037 |
| |

## Performance

Expand All @@ -61,6 +63,7 @@ With the help of [cargo-aoc](https://github.com/gobanos/cargo-aoc) I get automat
| 17 | 64.21 ms | 173.37 ms | | 47.36 µs |
| 18 | 837.59 ns | 844.43 ns | | 41.30 µs / 73.29 µs |
| 19 | 55.27 µs | 160.90 µs | | 213.26 µs |
| 20 | 5.22 ms | 24.31 ms | | 24.13 µs |

\* compared to first solution<br/>
\*\* slow, didn't benchmark. Value comes from running the solver.
Expand Down
131 changes: 126 additions & 5 deletions rust/2023/src/day_20.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
//! Day 20
//! Day 20: Pulse Propagation
//!
//! Hard to follow and my data structures make it even harder haha. Seems
//! like a bunch of people had problems with this one. Might come back and
//! simplify it later. Again HyperNeutrino's solution made it clearer to
//! understand what should be done, but it seems like most people need
//! to make some assumptions about the input to get it to work.

use std::collections::{HashMap, VecDeque};

use crate::math;

#[derive(Debug)]
pub struct Input {
modules: HashMap<String, Module>,
Expand Down Expand Up @@ -105,7 +113,7 @@ pub fn input_generator(input: &str) -> Input {
#[doc = r#"```
use advent_of_code_2023::day_20::*;
let data = include_str!("../input/2023/day20.txt");
assert_eq!(solve_part_01(&input_generator(data)), 331208);
assert_eq!(solve_part_01(&input_generator(data)), 812609846);
```"#]
#[aoc(day20, part1)]
pub fn solve_part_01(input: &Input) -> u64 {
Expand Down Expand Up @@ -184,16 +192,129 @@ pub fn solve_part_01(input: &Input) -> u64 {

/* Part Two
*
* Some assumptions are made about the input to get this to work. We find
* that the module that feeds into "rx" is "hp" and that "hp" is fed by
* four other modules. "hp" is a conjuction module which means that it will
* only send a low pulse if all of its inputs are high. So, we can
* find the cycle length of each of the modules that feed into "hp" and
* then calculate the LCM of those cycle lengths.
*
*/
#[doc = r#"```
use advent_of_code_2023::day_20::*;
let data = include_str!("../input/2023/day20.txt");
assert_eq!(solve_part_02(&input_generator(data)), 121464316215623);
assert_eq!(solve_part_02(&input_generator(data)), 245114020323037);
```"#]
#[aoc(day20, part2)]
pub fn solve_part_02(_input: &Input) -> u64 {
todo!()
pub fn solve_part_02(input: &Input) -> u64 {
let mut modules = input.modules.clone();
let mut button_presses = 0;

// Find which module feeds into "rx". This should only be one.
// This is "hp" in my input.
let feed = input
.modules
.values()
.find(|module| module.outputs.contains(&"rx".to_string()))
.unwrap();

// Find the modules that feed into the module, "hp", that feeds into "rx".
// We'll use these to find the cycle length and then we can calculate the
// LCM of the cycle lengths.
let mut cycle_lengths: HashMap<String, u64> = HashMap::new();

let mut seen = input
.modules
.values()
.filter(|module| module.outputs.contains(&feed.name))
.map(|module| (module.name.clone(), 0))
.collect::<HashMap<String, u64>>();

let fewest_button_presses =
'outer: loop {
button_presses += 1;

let mut queue = VecDeque::new();

for target in input.broadcast_target.iter() {
queue.push_back(("broadcaster".to_string(), target.clone(), Memory::Low));
}

while let Some((from, to, pulse)) = queue.pop_front() {
// Handles unknown modules
if !modules.contains_key(&to) {
continue;
};

let module = modules.get_mut(&to).unwrap();

// We only care about the module that feeds into "hp"
// and we only care about high pulses
if module.name == feed.name && pulse == Memory::High {
seen.entry(from.clone()).and_modify(|x| *x += 1);

// Update the cycle length
if !cycle_lengths.contains_key(&from) {
cycle_lengths.insert(from.clone(), button_presses);
}

// We've seen all the modules that feed into "hp"
// Calculate the LCM of the cycle lengths and break
if seen.values().all(|x| *x == 1) {
break 'outer cycle_lengths
.values()
.fold(1, |acc, x| math::lcm(acc, *x as i64));
}
}

match module.module_type {
ModuleType::FlipFlop => {
if pulse == Memory::Low {
module.memory = if module.memory == Memory::Off {
Memory::On
} else {
Memory::Off
};
let next_pulse = if module.memory == Memory::On {
Memory::High
} else {
Memory::Low
};

for output in module.outputs.iter() {
queue.push_back((
module.name.clone(),
output.clone(),
next_pulse.clone(),
));
}
}
}

ModuleType::Conjuction => {
if let Memory::Map(ref mut map) = module.memory {
map.insert(from, pulse.clone());

let next_pulse = if map.values().all(|x| *x == Memory::High) {
Memory::Low
} else {
Memory::High
};

for output in module.outputs.iter() {
queue.push_back((
module.name.clone(),
output.clone(),
next_pulse.clone(),
));
}
}
}
}
}
};

fewest_button_presses as u64
}

#[cfg(test)]
Expand Down

0 comments on commit 712127a

Please sign in to comment.