From b7db7574faf02505ff151d72606a513d574aebfd Mon Sep 17 00:00:00 2001 From: kageru Date: Wed, 20 Dec 2023 11:27:02 +0100 Subject: [PATCH] add 2023/20/1 --- 2023/inputs/day20 | 58 ++++++++++++++++ 2023/src/bin/day20.rs | 151 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 2023/inputs/day20 create mode 100644 2023/src/bin/day20.rs diff --git a/2023/inputs/day20 b/2023/inputs/day20 new file mode 100644 index 0000000..53bea22 --- /dev/null +++ b/2023/inputs/day20 @@ -0,0 +1,58 @@ +%xf -> qr +%qr -> bt, zt +%xm -> gp, cq +%zs -> ct +&vg -> lg +%dx -> bt, tz +%tq -> jm +%pr -> gp, pf +&nb -> lg +%tz -> bt +%kj -> fk +%hx -> rb +%xh -> zs, rb +&vc -> lg +%tl -> bn +%bb -> kf, rb +%nn -> xf, bt +%nk -> nn, bt +%kp -> vk +&bt -> tl, nk, pb, xf, vg +%sr -> vs, ml +%sh -> zk +%jm -> ml, kp +%kq -> tl, bt +%vs -> tq, ml +%sv -> dx, bt +%gs -> gp +%kf -> rb, ph +%ct -> rt, rb +%sj -> kj, rb +%kh -> ml +%nt -> gs, gp +%bn -> sv, bt +%lx -> ff, gp +%rt -> hq +%ph -> rb, hx +&ls -> lg +%nv -> xm +%df -> nv +%vk -> tk +%cq -> gp, mq +%hq -> bb +&lg -> rx +%zk -> ml, ps +&ml -> kp, sr, tq, nb, tk, sh, vk +%pf -> gp, nt +%ff -> gp, df +%zt -> pb, bt +broadcaster -> sj, sr, tp, nk +%mq -> pr +&rb -> vc, zs, fk, hq, rt, sj, kj +%pb -> kq +%qz -> ml, kh +%tp -> gp, lx +%tk -> sh +&gp -> df, ls, mq, tp, nv +%fk -> xh +%ps -> qz, ml diff --git a/2023/src/bin/day20.rs b/2023/src/bin/day20.rs new file mode 100644 index 0000000..caaeb47 --- /dev/null +++ b/2023/src/bin/day20.rs @@ -0,0 +1,151 @@ +#![feature(test)] +extern crate test; +use std::collections::VecDeque; + +use self::{Node::*, Pulse::*}; +use aoc2023::{boilerplate, common::*}; +use fnv::FnvHashMap as Map; + +const DAY: usize = 20; +type Parsed<'a> = Map<&'a str, (Node<'a>, Vec<&'a str>)>; + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum Pulse { + High, + Low, +} + +#[derive(Debug, Clone)] +enum Node<'a> { + Broadcaster, + FlipFlop(bool), + Conjunction(Map<&'a str, Pulse>), +} + +fn parse_input(raw: &str) -> Parsed { + let mut parsed: Parsed = raw + .lines() + .map(|line| line.split_once(" -> ").unwrap()) + .map(|(node, outputs)| { + let (node, name) = match node.as_bytes()[0] { + b'%' => (FlipFlop(false), &node[1..]), + b'&' => (Conjunction(Map::default()), &node[1..]), + _ => (Broadcaster, node), + }; + let outputs = outputs.split(", ").collect(); + (name, (node, outputs)) + }) + .collect(); + // Now the annoying part: set all inputs of all conjunctions to Low + let mut con_inputs = Map::<&str, Vec<&str>>::default(); + for (input, (_, outputs)) in parsed.iter() { + for output in outputs { + if matches!(parsed.get(output), Some((Conjunction(_), _))) { + match con_inputs.get_mut(output) { + Some(v) => v.push(input), + None => { + con_inputs.insert(output, vec![input]); + } + }; + } + } + } + for (con, inputs) in con_inputs { + let Some((Conjunction(map), _)) = parsed.get_mut(con) else { unreachable!() }; + for i in inputs { + map.insert(i, Low); + } + } + parsed +} + +const EMPTY: &Vec<&str> = &Vec::new(); + +fn process<'a, 'b>(map: &'a mut Parsed<'b>, name: &'b str, pulse: Pulse, src: &'b str) -> (&'b str, Pulse, &'a Vec<&'b str>) { + // println!("{src} ({pulse:?}) -> {name}"); + // First modify as needed… + match map.get_mut(name) { + // nodes that don’t lead anywhere, e.g. `output` from the second example + None => return ("", Low, EMPTY), + Some((FlipFlop(_), _)) if pulse == High => return ("", Low, EMPTY), + Some((FlipFlop(b), _)) => *b = !*b, + Some((Conjunction(inputs), _)) => { + inputs.insert(src, pulse); + } + Some(_) => (), + }; + // …then match again on an immutable borrow from the map so we can return the output vector. + let (pulse, out) = match (map.get(name).unwrap(), pulse) { + ((Broadcaster, out), p) => (p, out), + // Careful: we already flipped the flipflops above, so off gives low and on gives high. + ((FlipFlop(false), out), Low) => (Low, out), + ((FlipFlop(true), out), Low) => (High, out), + ((FlipFlop(_), _), High) => (High, EMPTY), + ((Conjunction(inputs), out), _) => { + if inputs.values().all(|&v| v == High) { + (Low, out) + } else { + (High, out) + } + } + }; + (name, pulse, out) +} + +fn push_button<'a, 'b>(map: &'a mut Parsed<'b>) -> (usize, usize) { + let mut low_count = 1; // initial button + let mut high_count = 0; + let mut queue = VecDeque::new(); + queue.push_back(("button", Low, "broadcaster")); + while let Some((src, pulse, output)) = queue.pop_front() { + let (src, pulse, outputs) = process(map, output, pulse, src); + for output in outputs { + queue.push_back((src, pulse, output)); + match pulse { + Low => low_count += 1, + High => high_count += 1, + } + } + } + (low_count, high_count) +} + +fn part1<'a>(map: &Parsed) -> usize { + let mut map = map.to_owned(); + let (mut low, mut high) = (0, 0); + for _ in 0..1000 { + let (new_low, new_high) = push_button(&mut map); + low += new_low; + high += new_high; + } + low * high +} + +fn part2(parsed: &Parsed) -> usize { + unimplemented!() +} + +boilerplate! { + TEST_INPUT == "\ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a", + TEST_INPUT_2 == "\ +broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output" + for tests: { + part1: { + TEST_INPUT => 32000000, + TEST_INPUT_2 => 11687500, + }, + part2: { TEST_INPUT => 0 }, + }, + bench1 == 929810733, + bench2 == 0, + bench_parse: Map::len => 58, +}