From a19a3258c2f1657d07b5f1a50baacb85f7ca1923 Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Thu, 21 Dec 2023 10:10:57 -0500 Subject: [PATCH] day20 --- Cargo.lock | 83 +++++++++++++++++++ Cargo.toml | 1 + src/day20.rs | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 305 insertions(+) create mode 100644 src/day20.rs diff --git a/Cargo.lock b/Cargo.lock index ec56d94..d5f15c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ "aoc-runner-derive", "array2d", "itertools", + "num", "prev-iter", "regex", "rust-crypto", @@ -61,6 +62,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b39cb2c1bf5a7c0dd097aa95ab859cf87dab5a4328900f5388942dc1889f74" +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "either" version = "1.9.0" @@ -112,6 +119,82 @@ 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 = "prev-iter" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index d1d781f..a52ad24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ strum = "0.25.0" strum_macros = "0.25" prev-iter = "0.1.2" regex = "1.10.2" +num = "0.4.1" \ No newline at end of file diff --git a/src/day20.rs b/src/day20.rs new file mode 100644 index 0000000..20dd3df --- /dev/null +++ b/src/day20.rs @@ -0,0 +1,220 @@ +use std::collections::{HashMap, VecDeque}; + +use aoc_runner_derive::{aoc, aoc_generator}; +use num::Integer; + +#[derive(Debug, Clone)] +enum Module { + FlipFlop { state: bool, dests: Vec }, + Conjunction { state: HashMap, dests: Vec }, + Broadcast { dests: Vec }, +} + +#[aoc_generator(day20)] +fn parse(input: &str) -> HashMap { + let mut input: HashMap<_, _> = input.lines().map(|line| { + let (name, dests) = line.split_once("->").unwrap(); + let dests = parse_destinations(dests); + let module = parse_module(name.trim(), dests); + (name.trim_matches(['&', '%', ' '].as_slice()).to_string(), module) + }).collect(); + collect_inputs(&mut input); + input +} + +fn parse_module(name: &str, dests: Vec) -> Module { + match name { + "broadcaster" => Module::Broadcast { dests }, + name if name.starts_with('%') => Module::FlipFlop { state: false, dests }, + name if name.starts_with('&') => Module::Conjunction { state: HashMap::new(), dests }, + _ => panic!("Invalid Module {}", name) + } + +} + +fn collect_inputs(modules: &mut HashMap) { + for (name, module) in modules.clone() { + let dests = match module { + Module::FlipFlop { state: _, dests: dest } => dest, + Module::Conjunction { state: _, dests: dest } => dest, + Module::Broadcast { dests: dest } => dest, + }; + + for dest_name in dests { + if let Some(dest_mod) = modules.get_mut(&dest_name) { + match dest_mod { + Module::Conjunction { state, dests: _ } => state.insert(name.to_string(), false), + _ => continue + }; + } + } + } +} + +fn parse_destinations(dests: &str) -> Vec { + dests.split(',') + .map(|x| x.trim().to_string()) + .collect::>() +} + +#[aoc(day20, part1)] +fn part1(modules: &HashMap) -> usize { + let mut modules = modules.clone(); + let mut high: usize = 0; + let mut low: usize = 0; + for _ in 0..1000 { + let (high_pulses, low_pulses) = push_the_button(&mut modules); + high += high_pulses; + low += low_pulses; + } + + high * low +} + +fn push_the_button(modules: &mut HashMap) -> (usize, usize) { + let mut process_queue: VecDeque<(String, bool, String)> = VecDeque::new(); + process_queue.push_back(("broadcaster".to_string(), false, "".to_string())); + let mut low_pulses: usize = 0; + let mut high_pulses: usize = 0; + + while let Some((module_name, input, sender)) = process_queue.pop_front() { + match input { + true => high_pulses += 1, + false => low_pulses += 1, + } + if let Some(module) = modules.get_mut(&module_name) { + match module { + Module::FlipFlop { state, dests } => { + if !input { + let flipped = !(*state); + *state = flipped; + for dest in dests { + process_queue.push_back((dest.clone(), state.clone(), module_name.clone())); + } + } + }, + Module::Conjunction { state, dests } => { + let stored = state.get_mut(&sender.to_string()).unwrap(); + *stored = input; + + let mut send = true; + if state.values().all(|x| *x) { + send = false; + } + for dest in dests { + process_queue.push_back((dest.clone(), send, module_name.clone())); + } + }, + Module::Broadcast { dests } => { + for dest in dests { + process_queue.push_back((dest.clone(), input, module_name.clone())); + } + }, + } + } + } + + return (high_pulses, low_pulses) +} + +#[aoc(day20, part2)] +fn part2(og_modules: &HashMap) -> usize { + let mut lcm = vec![]; + let before = ["tr", "xm", "dr", "nh"]; + + for module_name in before { + let mut modules = og_modules.clone(); + let mut count: usize = 1; + while !push_the_button_part2(&mut modules, module_name.to_string()) { + count += 1; + } + lcm.push(count); + } + + println!("{:?}", lcm); + + let lcm = lcm.iter() + .cloned() + .reduce(|a, b| a.lcm(&b)) + .unwrap(); + + lcm +} + +fn push_the_button_part2(modules: &mut HashMap, to_find: String) -> bool { + let mut process_queue: VecDeque<(String, bool, String)> = VecDeque::new(); + process_queue.push_back(("broadcaster".to_string(), false, "".to_string())); + + while let Some((module_name, input, sender)) = process_queue.pop_front() { + if module_name == to_find && input == false { + return true + } + if let Some(module) = modules.get_mut(&module_name) { + match module { + Module::FlipFlop { state, dests } => { + if !input { + let flipped = !(*state); + *state = flipped; + for dest in dests { + process_queue.push_back((dest.clone(), state.clone(), module_name.clone())); + } + } + }, + Module::Conjunction { state, dests } => { + let stored = state.get_mut(&sender.to_string()).unwrap(); + *stored = input; + + let mut send = true; + if state.values().all(|x| *x) { + send = false; + } + for dest in dests { + process_queue.push_back((dest.clone(), send, module_name.clone())); + } + }, + Module::Broadcast { dests } => { + for dest in dests { + process_queue.push_back((dest.clone(), input, module_name.clone())); + } + }, + } + } + } + + return false +} + + +#[cfg(test)] +mod tests { + use super::*; + + const EX_1: &str = r"broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a"; + +const EX_2: &str = r"broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> rx"; + + #[test] + fn part1_example1() { + assert_eq!(part1(&parse(EX_1)), 32000000); + } + + + + #[test] + fn part1_example2() { + assert_eq!(part1(&parse(EX_2)), 11687500); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX_2)), 1); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d26b20b..507f881 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day20; mod day19; mod day18; mod day17;