From fbceb131234e1e8956720238541fbe693c14f522 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Wed, 20 Dec 2023 15:26:09 +0100 Subject: [PATCH] 2023 - Day 20 [Part 2 works with manual help] --- 2023/input/20/input | 58 ++++++++++ 2023/input/20/test-1 | 5 + 2023/input/20/test-2 | 5 + 2023/src/bin/day20.rs | 252 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+) create mode 100644 2023/input/20/input create mode 100644 2023/input/20/test-1 create mode 100644 2023/input/20/test-2 create mode 100644 2023/src/bin/day20.rs diff --git a/2023/input/20/input b/2023/input/20/input new file mode 100644 index 0000000..24b129f --- /dev/null +++ b/2023/input/20/input @@ -0,0 +1,58 @@ +%rq -> ch, sj +%nf -> sm, rg +%pc -> rz, zp +%xt -> bc +%nt -> kq, sj +%hc -> kb, zp +%rd -> lk +%ml -> pp, xt +%sq -> kl, sj +%jg -> fl, rg +&xl -> df +%kl -> mb, sj +%nd -> rg, jg +&rg -> cs, zb, cp, vz, gp +%mf -> zp +%rz -> zp, fr +%kk -> rg, bj +%nb -> qj +%pr -> pp +&zp -> vl, lk, rd, kb, xl +%fl -> nf, rg +%tb -> pk, pp +%bh -> pp, pr +%nh -> sj, rq +%lk -> hc +%cp -> kk +&ln -> df +&xp -> df +%bc -> nb, pp +%lj -> rg +%vz -> nd +%vl -> lv, zp +&gp -> df +%hd -> pp, bq +%fq -> pp, bh +%pk -> fq, pp +%cs -> zb, rg +%sn -> fd +%kq -> sj, qq +%zb -> vz +%lv -> zp, rd +%qj -> pp, hd +%fd -> nt +&df -> rx +broadcaster -> vl, cs, cn, ml +%bq -> tb +%kb -> pc +%cn -> sn, sj +%qq -> sq +%mb -> sj, nh +%jd -> zp, mf +&sj -> xp, qq, cn, fd, sn +&pp -> ln, ml, xt, bq, nb +%sm -> rg, cp +%ch -> sj +%bj -> lj, rg +%fr -> zp, mr +%mr -> zp, jd diff --git a/2023/input/20/test-1 b/2023/input/20/test-1 new file mode 100644 index 0000000..2dc1bab --- /dev/null +++ b/2023/input/20/test-1 @@ -0,0 +1,5 @@ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a diff --git a/2023/input/20/test-2 b/2023/input/20/test-2 new file mode 100644 index 0000000..2738ceb --- /dev/null +++ b/2023/input/20/test-2 @@ -0,0 +1,5 @@ +broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output diff --git a/2023/src/bin/day20.rs b/2023/src/bin/day20.rs new file mode 100644 index 0000000..5f4fcfd --- /dev/null +++ b/2023/src/bin/day20.rs @@ -0,0 +1,252 @@ +#![feature(test)] +use std::collections::{HashMap, VecDeque}; + +use anyhow::Result; +use aoc::Solver; + +// -- Runners -- +fn main() -> Result<()> { + Day::solve() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_test1() -> Result<()> { + Day::test(Day::part1, "test-1", 32000000) + } + + #[test] + fn part1_test2() -> Result<()> { + Day::test(Day::part1, "test-2", 11687500) + } + + #[test] + fn part1_solution() -> Result<()> { + Day::test(Day::part1, "input", 666795063) + } + + #[test] + fn part2_solution() -> Result<()> { + Day::test(Day::part2, "input", 253302889093151) + } + + // Benchmarks + extern crate test; + #[bench] + #[ignore] + fn part1_bench(b: &mut test::Bencher) { + Day::benchmark(Day::part1, b) + } + #[bench] + #[ignore] + fn part2_bench(b: &mut test::Bencher) { + Day::benchmark(Day::part2, b) + } +} + +#[derive(Debug, Clone)] +enum ModuleType<'a> { + Broadcaster, + FlipFlop(bool), + Conjunction(HashMap<&'a str, bool>), +} + +#[derive(Debug, Clone)] +struct Module<'a> { + module_type: ModuleType<'a>, + destinations: Vec<&'a str>, +} + +impl<'a> Module<'a> { + fn process(&mut self, source: &'a str, high: bool) -> Option { + match &mut self.module_type { + ModuleType::Broadcaster => Some(high), + ModuleType::FlipFlop(current) => { + if high { + None + } else { + *current = !*current; + Some(*current) + } + } + ModuleType::Conjunction(inputs) => { + let input = inputs.get_mut(source).unwrap(); + *input = high; + + Some(!inputs.iter().all(|(_, prev)| *prev)) + } + } + } +} + +#[derive(Debug, Clone)] +struct Pulse<'a> { + source: &'a str, + destination: &'a str, + high: bool, +} + +// -- Solution -- +pub struct Day; +impl aoc::Solver for Day { + type Output1 = usize; + type Output2 = usize; + + fn day() -> u8 { + 20 + } + + fn part1(input: &str) -> Self::Output1 { + let mut modules: HashMap<_, _> = input + .lines() + .map(|line| { + let (name, destinations) = line.split_once(" -> ").unwrap(); + let destinations: Vec<_> = destinations.split(", ").collect(); + + let (name, module_type) = if name.starts_with('%') { + (name.split_at(1).1, ModuleType::FlipFlop(false)) + } else if name.starts_with('&') { + (name.split_at(1).1, ModuleType::Conjunction(HashMap::new())) + } else if name == "broadcaster" { + (name, ModuleType::Broadcaster) + } else { + unreachable!("Invalid input"); + }; + + ( + name, + Module { + module_type, + destinations, + }, + ) + }) + .collect(); + + // TODO: Because of the borrow check we have to create a clone of modules here + for (name, module) in modules.clone() { + for destination in &module.destinations { + if let Some(module) = modules.get_mut(destination) { + if let ModuleType::Conjunction(inputs) = &mut module.module_type { + inputs.insert(name, false); + } + } + } + } + + let mut count = (0, 0); + let mut pulses = VecDeque::new(); + for _ in 0..1000 { + pulses.push_back(Pulse { + source: "button", + destination: "broadcaster", + high: false, + }); + + while let Some(pulse) = pulses.pop_front() { + if pulse.high { + count.1 += 1; + } else { + count.0 += 1; + } + + if let Some(module) = modules.get_mut(pulse.destination) { + if let Some(high) = module.process(pulse.source, pulse.high) { + for destination in &module.destinations { + pulses.push_back(Pulse { + source: pulse.destination, + destination, + high, + }); + } + } + } + } + } + + count.0 * count.1 + } + + fn part2(input: &str) -> Self::Output2 { + let mut modules: HashMap<_, _> = input + .lines() + .map(|line| { + let (name, destinations) = line.split_once(" -> ").unwrap(); + let destinations: Vec<_> = destinations.split(", ").collect(); + + let (name, module_type) = if name.starts_with('%') { + (name.split_at(1).1, ModuleType::FlipFlop(false)) + } else if name.starts_with('&') { + (name.split_at(1).1, ModuleType::Conjunction(HashMap::new())) + } else if name == "broadcaster" { + (name, ModuleType::Broadcaster) + } else { + unreachable!("Invalid input"); + }; + + ( + name, + Module { + module_type, + destinations, + }, + ) + }) + .collect(); + + // TODO: Because of the borrow check we have to create a clone of modules here + for (name, module) in modules.clone() { + for destination in &module.destinations { + if let Some(module) = modules.get_mut(destination) { + if let ModuleType::Conjunction(inputs) = &mut module.module_type { + inputs.insert(name, false); + } + } + } + } + + let r#final = modules + .iter() + .find(|(_, module)| module.destinations.contains(&"rx")) + .unwrap(); + + let final_name = r#final.0.to_owned(); + let final_module = r#final.1.clone(); + + println!("{final_name}: {final_module:?}"); + + let mut pulses = VecDeque::new(); + for i in 0..10000 { + pulses.push_back(Pulse { + source: "button", + destination: "broadcaster", + high: false, + }); + + while let Some(pulse) = pulses.pop_front() { + if let Some(module) = modules.get_mut(pulse.destination) { + if pulse.destination == final_name && pulse.high { + // TODO: Instead of printing this, keep track of it + // Once all inputs have received a pulse calculate the LCM and output that + println!("{} high after {} presses", pulse.source, i + 1); + } + + if let Some(high) = module.process(pulse.source, pulse.high) { + for destination in &module.destinations { + pulses.push_back(Pulse { + source: pulse.destination, + destination, + high, + }); + } + } + } + } + } + + 0 + } +}