diff --git a/input/day20.txt b/input/day20.txt new file mode 100644 index 0000000..551873f --- /dev/null +++ b/input/day20.txt @@ -0,0 +1,58 @@ +%bc -> ph +%hr -> zq +%sn -> sj +%df -> dn, hf +%lp -> dm +%lf -> sn, vk +&fv -> sq +%gd -> vm, gr +%jt -> vm, lz +%xf -> vk +%nf -> lp, kb +%dl -> nn +&sq -> rx +%vb -> vm, lg +%zq -> kb, fl +%fk -> fz +%gj -> pq +%jx -> jh +%pq -> kb, nf +&dn -> kg, vt, tg, fs, pn, jx +%ln -> dn, fs +%fz -> vm, jt +%fs -> df +%dm -> kb, hr +%ds -> kb +&kk -> sq +%tg -> tv +&vt -> sq +%fl -> zl, kb +&vk -> bc, sj, jd, lf, xr, sn +%jd -> pp +%tv -> dn, pn +%sb -> gf, vk +&kb -> kk, gj, gt, hr, lp +%pp -> vk, bc +%pn -> pf +%zc -> vm +&vm -> dl, fk, nn, fv, gd, lg +%rn -> dn +%gr -> vb, vm +%sj -> kq +%zl -> kb, ds +%lz -> vm, zc +%jh -> dn, ln +%pf -> dn, jx +%kq -> sb, vk +%ph -> md, vk +%nc -> gj, kb +%kg -> tg, dn +%hf -> dn, rn +%nn -> fk +%gf -> jd, vk +%lg -> pv +broadcaster -> gd, kg, gt, lf +%gt -> nc, kb +%pv -> dl, vm +&xr -> sq +%md -> vk, xf diff --git a/src/day08.rs b/src/day08.rs index bdc2592..a7e6788 100644 --- a/src/day08.rs +++ b/src/day08.rs @@ -128,7 +128,7 @@ fn count_steps_until_end( steps } -fn lcm(xs: impl Iterator) -> usize { +pub fn lcm(xs: impl Iterator) -> usize { let mut a = 1; for x in xs { a = a * x / gcd(a, x); diff --git a/src/day20.rs b/src/day20.rs new file mode 100644 index 0000000..6eb2ec5 --- /dev/null +++ b/src/day20.rs @@ -0,0 +1,324 @@ +use std::{ + collections::{HashMap, HashSet, VecDeque}, + fmt::Display, +}; + +use itertools::Itertools; + +pub fn run() { + let input = include_str!("../input/day20.txt"); + // let input = r#" + // broadcaster -> a, b, c + // %a -> b + // %b -> c + // %c -> inv + // &inv -> a + // "#; + + // let input = r#" + // broadcaster -> a + // %a -> inv, con + // &inv -> b + // %b -> con + // &con -> output + // "#; + + let modules = parse_modules(input); + dbg!(simulate_one_thousand(modules.clone())); + + let rx_input = find_inputs(&modules, "rx"); + assert_eq!(rx_input.len(), 1); + let rx_input = rx_input[0]; + assert!(modules[rx_input].as_any().is::()); + + let rx_input_inputs = find_inputs(&modules, rx_input); + let cycles = rx_input_inputs + .into_iter() + .map(|s| find_output_cycle(modules.clone(), s)) + .collect::>(); + + dbg!(crate::day08::lcm(cycles.into_iter())); +} + +fn parse_modules(s: &'static str) -> HashMap<&'static str, Box> { + let mut m = HashMap::<&'static str, Box>::new(); + m.insert("output", Box::new(Output)); + let mut conjunctions = vec![]; + for l in s.trim().lines() { + let l = l.trim(); + let (name, receivers) = l.split_once(" -> ").unwrap(); + let receivers = receivers.split(", ").collect::>(); + let trimmed_name: &'static str; + let module: Box; + if name == "broadcaster" { + trimmed_name = "broadcaster"; + module = Box::new(Broadcaster { receivers }); + } else if name.starts_with("%") { + trimmed_name = &name[1..]; + module = Box::new(FlipFlop { + receivers, + state: false, + }); + } else if name.starts_with("&") { + trimmed_name = &name[1..]; + conjunctions.push(trimmed_name); + module = Box::new(Conjunction { + receivers, + inputs: HashMap::new(), + }); + } else { + panic!("invalid name {}", name); + } + m.insert(trimmed_name, module); + } + for name in conjunctions { + let inputs = m + .iter() + .filter(|(_, v)| v.get_destinations().contains(&name)) + .map(|(k, _)| (*k, false)) + .collect::>(); + let any = m.get_mut(name).unwrap().as_any_mut(); + let conjunction = any.downcast_mut::().unwrap(); + conjunction.inputs = inputs; + } + m +} + +#[derive(Debug, Clone, Copy)] +struct Pulse { + destination: &'static str, + value: bool, +} + +trait Module: std::fmt::Debug { + fn get_destinations(&self) -> &[&'static str]; + fn handle_pulse(&mut self, source: &'static str, value: bool) -> Vec; + fn as_any_mut(&mut self) -> &mut dyn std::any::Any; + fn as_any(&self) -> &dyn std::any::Any; + fn clone_module(&self) -> Box; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Broadcaster { + receivers: Vec<&'static str>, +} + +impl Module for Broadcaster { + fn handle_pulse(&mut self, _source: &'static str, value: bool) -> Vec { + self.receivers + .iter() + .map(|r| Pulse { + destination: r, + value, + }) + .collect() + } + + fn get_destinations(&self) -> &[&'static str] { + &self.receivers + } + + fn clone_module(&self) -> Box { + Box::new(self.clone()) + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct FlipFlop { + receivers: Vec<&'static str>, + state: bool, +} + +impl Module for FlipFlop { + fn handle_pulse(&mut self, _source: &'static str, value: bool) -> Vec { + if value { + return vec![]; + } + self.state = !self.state; + self.receivers + .iter() + .map(|r| Pulse { + destination: r, + value: self.state, + }) + .collect() + } + + fn get_destinations(&self) -> &[&'static str] { + &self.receivers + } + + fn clone_module(&self) -> Box { + Box::new(self.clone()) + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Conjunction { + receivers: Vec<&'static str>, + inputs: HashMap<&'static str, bool>, +} + +impl Module for Conjunction { + fn handle_pulse(&mut self, source: &'static str, value: bool) -> Vec { + self.inputs.insert(source, value); + let output = !self.inputs.values().all(|b| *b); + self.receivers + .iter() + .map(|r| Pulse { + destination: r, + value: output, + }) + .collect() + } + + fn get_destinations(&self) -> &[&'static str] { + &self.receivers + } + + fn clone_module(&self) -> Box { + Box::new(self.clone()) + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Output; + +impl Module for Output { + fn get_destinations(&self) -> &[&'static str] { + &[] + } + + fn handle_pulse(&mut self, _source: &'static str, _value: bool) -> Vec { + vec![] + } + + fn clone_module(&self) -> Box { + Box::new(Output) + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +fn simulate(modules: &mut HashMap<&'static str, Box>) -> (usize, usize) { + let mut pulses_to_send = VecDeque::from([( + "button", + Pulse { + destination: "broadcaster", + value: false, + }, + )]); + let mut high = 0; + let mut low = 0; + // println!("button -low-> broadcaster"); + while let Some((source, p)) = pulses_to_send.pop_front() { + if p.value { + high += 1; + } else { + low += 1; + } + let handler = p.destination; + let handling_module = modules.get_mut(handler); + if handling_module.is_none() { + // println!("no module {}", handler); + continue; + } + let new = handling_module.unwrap().handle_pulse(source, p.value); + for p in new { + // let value = if p.value { "high" } else { "low" }; + // println!("{} -{}-> {}", handler, value, p.destination); + pulses_to_send.push_back((handler, p)); + } + } + (high, low) +} + +impl Clone for Box { + fn clone(&self) -> Self { + self.clone_module() + } +} + +fn simulate_one_thousand(mut modules: HashMap<&'static str, Box>) -> usize { + let mut high = 0; + let mut low = 0; + for _ in 0..1000 { + let r = simulate(&mut modules); + high += r.0; + low += r.1; + } + high * low +} + +fn find_inputs( + modules: &HashMap<&'static str, Box>, + dest: &'static str, +) -> Vec<&'static str> { + let mut inputs = vec![]; + for (name, module) in modules.iter() { + if module.get_destinations().contains(&dest) { + inputs.push(*name); + } + } + inputs +} + +fn find_output_cycle( + mut modules: HashMap<&'static str, Box>, + name: &'static str, +) -> usize { + let mut iterations = 0; + loop { + iterations += 1; + + let mut pulses_to_send = VecDeque::from([( + "button", + Pulse { + destination: "broadcaster", + value: false, + }, + )]); + while let Some((source, p)) = pulses_to_send.pop_front() { + if !p.value && p.destination == name { + return iterations; + } + let handler = p.destination; + if let Some(handling_module) = modules.get_mut(handler) { + let new = handling_module.handle_pulse(source, p.value); + for p in new { + pulses_to_send.push_back((handler, p)); + } + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 7d7a681..8601e3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,8 @@ mod day16; mod day17; mod day18; mod day19; +mod day20; fn main() { - day19::run(); + day20::run(); }