summaryrefslogtreecommitdiff
path: root/2023/src/bin/day_20.rs
diff options
context:
space:
mode:
Diffstat (limited to '2023/src/bin/day_20.rs')
-rw-r--r--2023/src/bin/day_20.rs334
1 files changed, 334 insertions, 0 deletions
diff --git a/2023/src/bin/day_20.rs b/2023/src/bin/day_20.rs
new file mode 100644
index 0000000..f2e955f
--- /dev/null
+++ b/2023/src/bin/day_20.rs
@@ -0,0 +1,334 @@
+use nom::{
+ branch::alt,
+ bytes::complete::tag,
+ character::complete::{alpha1, line_ending},
+ combinator::{map, map_res, value},
+ multi::separated_list1,
+ sequence::{delimited, pair, separated_pair},
+ IResult,
+};
+use std::{collections::VecDeque, fs, num::ParseIntError};
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let input = fs::read_to_string("inputs/day_20.txt")?;
+ let circuit = Circuit::parser(&input).unwrap().1;
+
+ {
+ let mut circuit = circuit.clone();
+ let mut pulse_tracker = PulseCounter::default();
+
+ for _ in 0..1000 {
+ circuit.push_the_button(&mut pulse_tracker);
+ }
+ dbg!(pulse_tracker.low_pulse_count * pulse_tracker.high_pulse_count);
+ }
+
+ {
+ let mut circuit = circuit.clone();
+ let mut pulse_tracker = RxWatcher::new(
+ circuit
+ .modules
+ .iter()
+ .find(|m| m.id == ModuleId::rx())
+ .unwrap()
+ .index,
+ circuit
+ .modules
+ .iter()
+ .find(|m| m.id == ModuleId::from_short_alphanumeric("th").unwrap())
+ .unwrap()
+ .index,
+ );
+
+ while !pulse_tracker.rx_got_a_low_pulse {
+ pulse_tracker.i += 1;
+ circuit.push_the_button(&mut pulse_tracker);
+ }
+ dbg!(pulse_tracker.i, pulse_tracker);
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+struct ModuleId(u32);
+
+#[derive(Debug, Clone)]
+struct Circuit {
+ modules: Vec<Module>,
+}
+
+#[derive(Debug, Clone)]
+struct ParsedModule {
+ id: ModuleId,
+ outs: Vec<ModuleId>,
+ state: ModuleState,
+}
+
+#[derive(Debug, Clone)]
+struct Module {
+ id: ModuleId,
+ index: usize,
+ outs: Vec<usize>,
+ state: ModuleState,
+}
+
+#[derive(Debug, Clone)]
+enum ModuleState {
+ FlipFlop(bool),
+ Conjunction(u64),
+ Sink,
+}
+
+#[derive(Debug, Default)]
+struct PulseCounter {
+ pulses: VecDeque<Pulse>,
+ low_pulse_count: u64,
+ high_pulse_count: u64,
+}
+
+#[derive(Debug)]
+struct RxWatcher {
+ pulses: VecDeque<Pulse>,
+ rx_module_index: usize,
+ rx_got_a_low_pulse: bool,
+ logging_module_index: usize,
+ i: usize,
+}
+
+impl RxWatcher {
+ fn new(rx_module_index: usize, logging_module_index: usize) -> Self {
+ RxWatcher {
+ pulses: VecDeque::default(),
+ rx_module_index,
+ rx_got_a_low_pulse: false,
+ logging_module_index,
+ i: 0,
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Pulse {
+ state: bool,
+ input: usize,
+ output: usize,
+}
+
+impl Circuit {
+ fn parser(input: &str) -> IResult<&str, Self> {
+ map(
+ pair(
+ delimited(
+ tag("broadcaster -> "),
+ ParsedModule::outs_parser,
+ line_ending,
+ ),
+ separated_list1(line_ending, ParsedModule::parser),
+ ),
+ |(broadcaster, parsed_modules)| {
+ let sink_index = parsed_modules.len() + 1;
+ let rx_sink_index = parsed_modules.len() + 2;
+ let mut modules: Vec<Module> = parsed_modules
+ .iter()
+ .enumerate()
+ .map(|(index, module)| Module {
+ id: module.id,
+ index: index + 1,
+ outs: module
+ .outs
+ .iter()
+ .map(|out_id| {
+ parsed_modules
+ .iter()
+ .enumerate()
+ .find(|(_, out)| &out.id == out_id)
+ .map_or_else(
+ || {
+ if out_id == &ModuleId::rx() {
+ rx_sink_index
+ } else {
+ sink_index
+ }
+ },
+ |(index, _)| index + 1,
+ )
+ })
+ .collect(),
+ state: module.state.clone(),
+ })
+ .collect();
+ modules.push(Module {
+ id: ModuleId::sink(),
+ index: sink_index,
+ outs: Vec::new(),
+ state: ModuleState::Sink,
+ });
+ modules.push(Module {
+ id: ModuleId::rx(),
+ index: rx_sink_index,
+ outs: Vec::new(),
+ state: ModuleState::Sink,
+ });
+ modules.insert(
+ 0,
+ Module {
+ id: ModuleId::broadcaster(),
+ index: 0,
+ outs: broadcaster
+ .iter()
+ .map(|mid| modules.iter().find(|m| &m.id == mid).unwrap().index)
+ .collect(),
+ state: ModuleState::Sink,
+ },
+ );
+
+ let modules_snapshot = modules.clone();
+
+ for module in &mut modules {
+ if let ModuleState::Conjunction(ref mut conjunction_state) = module.state {
+ for input in &modules_snapshot {
+ if input.outs.contains(&module.index) {
+ let mask = !(1 << input.index);
+ *conjunction_state &= mask;
+ }
+ }
+ }
+ }
+
+ Circuit { modules }
+ },
+ )(input)
+ }
+
+ fn push_the_button(&mut self, pulse_tracker: &mut impl PulseTracker) {
+ pulse_tracker.button_pulse();
+ for b in &self.modules[0].outs {
+ pulse_tracker.push(Pulse {
+ state: false,
+ input: 0,
+ output: *b,
+ });
+ }
+
+ while let Some(pulse) = pulse_tracker.pop() {
+ let module = &mut self.modules[pulse.output];
+ let new_pulse_state: Option<bool> = match module.state {
+ ModuleState::FlipFlop(ref mut current) => {
+ if !pulse.state {
+ *current = !*current;
+ Some(*current)
+ } else {
+ None
+ }
+ }
+ ModuleState::Conjunction(ref mut current) => {
+ let mask = 1 << pulse.input;
+ if pulse.state {
+ *current |= mask;
+ } else {
+ *current &= !mask;
+ }
+ Some(*current != u64::MAX)
+ }
+ ModuleState::Sink => None,
+ };
+ if let Some(new_pulse_state) = new_pulse_state {
+ for out in &module.outs {
+ pulse_tracker.push(Pulse {
+ state: new_pulse_state,
+ input: module.index,
+ output: *out,
+ });
+ }
+ }
+ }
+ }
+}
+
+trait PulseTracker {
+ fn button_pulse(&mut self);
+ fn push(&mut self, pulse: Pulse);
+ fn pop(&mut self) -> Option<Pulse>;
+}
+
+impl PulseTracker for PulseCounter {
+ fn button_pulse(&mut self) {
+ self.low_pulse_count += 1;
+ }
+
+ fn push(&mut self, pulse: Pulse) {
+ if pulse.state {
+ self.high_pulse_count += 1;
+ } else {
+ self.low_pulse_count += 1;
+ }
+ self.pulses.push_back(pulse);
+ }
+
+ fn pop(&mut self) -> Option<Pulse> {
+ self.pulses.pop_front()
+ }
+}
+
+impl PulseTracker for RxWatcher {
+ fn button_pulse(&mut self) {}
+
+ fn push(&mut self, pulse: Pulse) {
+ if pulse.state && pulse.output == self.logging_module_index {
+ println!("{}: {} into {}", self.i, pulse.input, pulse.output);
+ }
+ if !pulse.state && pulse.output == self.rx_module_index {
+ self.rx_got_a_low_pulse = true;
+ }
+ self.pulses.push_back(pulse);
+ }
+
+ fn pop(&mut self) -> Option<Pulse> {
+ self.pulses.pop_front()
+ }
+}
+
+impl ParsedModule {
+ fn parser(input: &str) -> IResult<&str, Self> {
+ map(
+ separated_pair(
+ pair(
+ alt((
+ value(ModuleState::FlipFlop(false), tag("%")),
+ value(ModuleState::Conjunction(u64::MAX), tag("&")),
+ )),
+ map_res(alpha1, |id: &str| ModuleId::from_short_alphanumeric(id)),
+ ),
+ tag(" -> "),
+ ParsedModule::outs_parser,
+ ),
+ |((state, id), outs)| ParsedModule { id, state, outs },
+ )(input)
+ }
+
+ fn outs_parser(input: &str) -> IResult<&str, Vec<ModuleId>> {
+ separated_list1(
+ tag(", "),
+ map_res(alpha1, |s: &str| ModuleId::from_short_alphanumeric(s)),
+ )(input)
+ }
+}
+
+impl ModuleId {
+ fn broadcaster() -> ModuleId {
+ ModuleId(0)
+ }
+
+ fn sink() -> ModuleId {
+ ModuleId(u32::MAX)
+ }
+
+ fn rx() -> ModuleId {
+ ModuleId::from_short_alphanumeric("rx").unwrap()
+ }
+
+ fn from_short_alphanumeric(s: &str) -> Result<ModuleId, ParseIntError> {
+ u32::from_str_radix(s, 36).map(ModuleId)
+ }
+}