From 59e06d73eb4c487ca26955fadf1cbfcb0d84e98c Mon Sep 17 00:00:00 2001 From: Andrew Coleman Date: Thu, 11 Jan 2024 15:07:13 -0500 Subject: [PATCH] day20 solution --- 2023/src/day20.rs | 225 ++++++++++++++++++++++++++++++++++++++++++++++ 2023/src/lib.rs | 1 + 2 files changed, 226 insertions(+) create mode 100644 2023/src/day20.rs diff --git a/2023/src/day20.rs b/2023/src/day20.rs new file mode 100644 index 0000000..a8511e1 --- /dev/null +++ b/2023/src/day20.rs @@ -0,0 +1,225 @@ +use aoc_runner_derive::aoc; +use num::integer::lcm; +use std::collections::{HashMap, VecDeque}; + +#[derive(Eq, PartialEq)] +enum MachineType { + Broadcaster, + FlipFlop, + Conjuction, +} + +struct Machine { + name: String, + mt: MachineType, + neighbors: Vec, + states: HashMap, +} + +fn pulse( + machine: &mut Machine, + signal: bool, + source: String, + queue: &mut VecDeque<(String, String, bool)>, +) { + match machine.mt { + MachineType::Broadcaster => { + for n in machine.neighbors.iter() { + queue.push_back((n.to_string(), machine.name.clone(), signal)); + } + } + MachineType::FlipFlop => { + if signal { + return; + } + machine + .states + .entry(machine.name.clone()) + .and_modify(|s| *s = !*s); + let &signal = machine.states.get(&machine.name).unwrap(); + for n in machine.neighbors.iter() { + queue.push_back((n.to_string(), machine.name.clone(), signal)); + } + } + MachineType::Conjuction => { + machine + .states + .entry(source) + .and_modify(|s| *s = signal) + .or_insert(signal); + let mut next = false; + for (_name, &t) in machine.states.iter() { + if !t { + next = true; + break; + } + } + for n in machine.neighbors.iter() { + queue.push_back((n.to_string(), machine.name.clone(), next)); + } + } + } +} + +fn parse<'a>(input: &str) -> HashMap { + let mut r: HashMap = HashMap::new(); + let mut c: Vec = vec![]; + for line in input.lines() { + if line.is_empty() { + continue; + } + + let lparts = line.split(" -> ").collect::>(); + + let name = if lparts[0].starts_with("&") { + lparts[0].strip_prefix("&").unwrap() + } else if lparts[0].starts_with("%") { + lparts[0].strip_prefix("%").unwrap() + } else { + lparts[0] + }; + + let neighbors = lparts[1] + .split(", ") + .map(|s| s.to_string()) + .collect::>(); + + if name == "broadcaster" { + r.insert( + name.to_string(), + Machine { + name: name.to_string(), + mt: MachineType::Broadcaster, + neighbors, + states: HashMap::with_capacity(0), + }, + ); + } else if line.starts_with("&") { + c.push(name.to_string()); + r.insert( + name.to_string(), + Machine { + name: name.to_string(), + mt: MachineType::Conjuction, + neighbors, + states: HashMap::new(), + }, + ); + } else if line.starts_with("%") { + r.insert( + name.to_string(), + Machine { + name: name.to_string(), + mt: MachineType::FlipFlop, + neighbors, + states: HashMap::from([(name.to_string(), false)]), + }, + ); + } + } + + let mut c2: HashMap> = HashMap::new(); + for (name, node) in r.iter() { + for n in node.neighbors.iter() { + if c.contains(n) { + c2.entry(n.clone()) + .and_modify(|t| t.push(name.clone())) + .or_insert(vec![name.clone()]); + } + } + } + + for (name, inputs) in c2.iter() { + let node = r.get_mut(name).unwrap(); + for i in inputs.iter() { + node.states.entry(i.clone()).or_insert(false); + } + } + + r +} + +#[aoc(day20, part1)] +fn part1(input: &str) -> u64 { + let mut parts = parse(input); + let mut queue: VecDeque<(String, String, bool)> = VecDeque::new(); + let mut high = 0; + let mut low = 0; + for _ in 0..1000 { + queue.push_back((String::from("broadcaster"), String::from("button"), false)); + while let Some((dest, source, signal)) = queue.pop_front() { + if signal { + high += 1; + } else { + low += 1; + } + if let Some(mut node) = parts.get_mut(&dest) { + pulse(&mut node, signal, source, &mut queue); + } + } + } + + high * low +} + +#[aoc(day20, part2)] +fn part2(input: &str) -> u64 { + let mut parts = parse(input); + let mut queue: VecDeque<(String, String, bool)> = VecDeque::new(); + let mut last_cons = vec![ + String::from("zl"), + String::from("xn"), + String::from("qn"), + String::from("xf"), + ]; + let mut con_indexes: Vec = vec![]; + let mut c = 1; + for i in 0..10000 { + queue.push_back((String::from("broadcaster"), String::from("button"), false)); + while let Some((dest, source, signal)) = queue.pop_front() { + if signal { + if let Some(index) = last_cons.iter().position(|s| *s == source) { + last_cons.remove(index); + con_indexes.push(i + 1); + c = lcm(i + 1, c); + if last_cons.is_empty() { + return c; + } + } + } + if let Some(mut node) = parts.get_mut(&dest) { + pulse(&mut node, signal, source, &mut queue); + } + } + } + 0 +} + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE_DATA: &'static str = r#"broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a +"#; + + const SAMPLE_DATA2: &'static str = r#"broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output"#; + + #[test] + fn sample_data() { + assert_eq!(part1(&SAMPLE_DATA), 32000000); + // assert_eq!(part2(&SAMPLE_DATA), 71503); + } + + #[test] + fn sample_data2() { + assert_eq!(part1(&SAMPLE_DATA2), 11687500); + } +} diff --git a/2023/src/lib.rs b/2023/src/lib.rs index 596c2be..1add4d4 100644 --- a/2023/src/lib.rs +++ b/2023/src/lib.rs @@ -19,5 +19,6 @@ mod day16; mod day17; mod day18; mod day19; +mod day20; aoc_lib! { year = 2023 }