From 7c03b536ddacb0b01d5c6e6ad2f52a9260d8a33d Mon Sep 17 00:00:00 2001 From: Leandro Lisboa Penz Date: Wed, 20 Dec 2023 16:01:03 +0000 Subject: [PATCH] Day 20a --- Cargo.lock | 16 +++++ Cargo.toml | 1 + day20/Cargo.toml | 10 +++ day20/input.txt | 58 ++++++++++++++++ day20/src/bin/day20a.rs | 116 +++++++++++++++++++++++++++++++ day20/src/lib.rs | 150 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 351 insertions(+) create mode 100644 day20/Cargo.toml create mode 100644 day20/input.txt create mode 100644 day20/src/bin/day20a.rs create mode 100644 day20/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 79c020d..4d79e4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,6 +104,12 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "copstr" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "746fca5d567fdeb2dba431861b02fb33c714ce0188472317e2363ab8da92d336" + [[package]] name = "crossbeam-deque" version = "0.8.3" @@ -324,6 +330,16 @@ dependencies = [ "sqrid 0.0.23", ] +[[package]] +name = "day20" +version = "0.1.0" +dependencies = [ + "aoc", + "color-eyre", + "copstr", + "nom", +] + [[package]] name = "either" version = "1.9.0" diff --git a/Cargo.toml b/Cargo.toml index 0f6fe5c..849594f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,6 @@ members = [ "day16", "day17", "day18", + "day20", ] diff --git a/day20/Cargo.toml b/day20/Cargo.toml new file mode 100644 index 0000000..c326009 --- /dev/null +++ b/day20/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day20" +version = "0.1.0" +edition = "2021" + +[dependencies] +aoc = { path = "../aoc" } +color-eyre = "0.6.2" +copstr = "0.1.2" +nom = "7.1.3" diff --git a/day20/input.txt b/day20/input.txt new file mode 100644 index 0000000..f537a2c --- /dev/null +++ b/day20/input.txt @@ -0,0 +1,58 @@ +%dt -> fm, hd +%tl -> jk, hd +%vx -> kc, sz +%sz -> kc +%kj -> tl, hd +%pm -> tb +%fc -> rt +&tb -> sx, qn, vj, qq, sk, pv +%df -> bb +%qq -> jm +%sl -> vz +broadcaster -> hp, zb, xv, qn +&pv -> kh +%gf -> pm, tb +%pb -> hd, kj +%gr -> hd, pb +%gs -> kc, pd +%tx -> df +%jm -> tb, db +%bh -> fl, gz +%rt -> kc, xp +&qh -> kh +%lb -> zm, fl +%pd -> lq +%qn -> sk, tb +%gb -> qq, tb +&xm -> kh +%mv -> hd, gr +%gz -> fl +%js -> mv +%hp -> dt, hd +%nk -> kc, vx +&kh -> rx +%zc -> tx +%mp -> js, hd +%zm -> mb +%xh -> cd, tb +%db -> xh, tb +%sx -> vj +&hz -> kh +%vj -> gb +%zq -> hd +%lj -> fc, kc +%lg -> kc, nk +&fl -> xv, tx, sl, df, qh, zc, zm +&kc -> zb, xp, pd, fc, xm +%lq -> kc, lj +&hd -> hp, js, hz +%mb -> fl, sl +%vz -> fl, bh +%fm -> mp, hd +%bb -> fl, lb +%zb -> gs, kc +%xp -> lg +%jk -> zq, hd +%xv -> zc, fl +%sk -> sx +%cd -> gf, tb diff --git a/day20/src/bin/day20a.rs b/day20/src/bin/day20a.rs new file mode 100644 index 0000000..187bd04 --- /dev/null +++ b/day20/src/bin/day20a.rs @@ -0,0 +1,116 @@ +// Copyright (C) 2023 Leandro Lisboa Penz +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. + +use day20::*; + +use std::collections::BTreeMap; + +#[derive(Default, Debug)] +pub struct ModState { + state: bool, + // Conjunction memory: + memory: BTreeMap, +} + +fn eval<'a>( + module: &'a Module, + msts: &'a mut ModState, + src_mname: Mname, + pulse: bool, +) -> Box + 'a> { + match module.mtype { + Mtype::Broadcast => Box::new( + module + .dsts + .iter() + .copied() + .map(move |d| (module.mname, pulse, d)), + ), + Mtype::FlipFlop => { + if !pulse { + msts.state = !msts.state; + Box::new( + module + .dsts + .iter() + .copied() + .map(|d| (module.mname, msts.state, d)), + ) + } else { + Box::new(std::iter::empty()) + } + } + Mtype::Conjunct => { + msts.memory.insert(src_mname, pulse); + // eprintln!("{} memory {:?}", module.mname.0, msts.memory); + let pulse = !msts.memory.values().all(|v| *v); + Box::new( + module + .dsts + .iter() + .copied() + .map(move |d| (module.mname, pulse, d)), + ) + } + Mtype::None => Box::new(std::iter::empty()), + } +} + +fn process(bufin: impl BufRead) -> Result { + let modules = parser::parse(bufin)?; + let mut sts = BTreeMap::new(); + for mname in modules.keys() { + sts.insert(mname, ModState::default()); + } + for src_mname in modules.keys() { + let src_module = &modules[src_mname]; + for dst_mname in &src_module.dsts { + let dst_module = &modules[&dst_mname]; + if dst_module.mtype == Mtype::Conjunct { + let conj = sts.get_mut(dst_mname).unwrap(); + conj.memory.insert(*src_mname, false); + } + } + } + let broadcast_mname: Mname = "0".into(); + sts.insert(&broadcast_mname, ModState::default()); + let mut total_low = 0_u64; + let mut total_high = 0_u64; + for _i in 0..1000 { + let mut pulses = vec![(broadcast_mname, false, broadcast_mname)]; + while !pulses.is_empty() { + // eprintln!("i {} pulses {:?}", i, pulses); + let (low, high) = pulses.iter().fold((0, 0), |(low, high), (_, p, _)| { + (low + if !p { 1 } else { 0 }, high + if *p { 1 } else { 0 }) + }); + total_low += low; + total_high += high; + let mut next_pulses = Vec::<(Mname, bool, Mname)>::new(); + for (src_mname, pulse, dst_mname) in pulses.into_iter() { + let module = &modules[&dst_mname]; + let module_pulses = + eval(module, sts.get_mut(&dst_mname).unwrap(), src_mname, pulse); + next_pulses.extend(module_pulses.into_iter()); + } + pulses = next_pulses; + } + } + Ok(total_low * total_high) +} + +#[test] +fn test1() -> Result<()> { + assert_eq!(process(EXAMPLE1.as_bytes())?, 32000000); + Ok(()) +} + +#[test] +fn test2() -> Result<()> { + assert_eq!(process(EXAMPLE2.as_bytes())?, 11687500); + Ok(()) +} + +fn main() -> Result<()> { + do_main(|| process(stdin().lock())) +} diff --git a/day20/src/lib.rs b/day20/src/lib.rs new file mode 100644 index 0000000..cd46da1 --- /dev/null +++ b/day20/src/lib.rs @@ -0,0 +1,150 @@ +// Copyright (C) 2023 Leandro Lisboa Penz +// This file is subject to the terms and conditions defined in +// file 'LICENSE', which is part of this source code package. + +pub use aoc::*; + +use std::collections::BTreeMap; +use std::str::FromStr; + +pub const EXAMPLE1: &str = "broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a +"; + +pub const EXAMPLE2: &str = "broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output +"; + +#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] +pub struct Mname(pub copstr::Str<6>); + +impl From<&str> for Mname { + fn from(s: &str) -> Self { + Mname(s.try_into().unwrap()) + } +} + +impl FromStr for Mname { + type Err = Report; + fn from_str(s: &str) -> Result { + Ok(Mname(copstr::Str::<6>::try_from(s)?)) + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Default)] +pub enum Mtype { + #[default] + None, + FlipFlop, + Conjunct, + Broadcast, +} + +impl TryFrom for Mtype { + type Error = Report; + fn try_from(c: char) -> Result { + match c { + '%' => Ok(Mtype::FlipFlop), + '&' => Ok(Mtype::Conjunct), + other => Err(eyre!("invalid module type {}", other)), + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq, Default)] +pub struct Module { + pub mname: Mname, + pub mtype: Mtype, + pub dsts: Vec, +} + +pub mod parser { + use aoc::parser::*; + + use super::*; + + fn mtype(input: &str) -> IResult<&str, Mtype> { + let (input, mtype_char) = character::one_of("&%")(input)?; + Ok((input, Mtype::try_from(mtype_char).unwrap())) + } + + fn mname(input: &str) -> IResult<&str, Mname> { + let (input, mname) = character::alpha1(input)?; + Ok((input, mname.into())) + } + + fn module_line(input: &str) -> IResult<&str, Module> { + let (input, mtype) = mtype(input)?; + let (input, name) = mname(input)?; + let (input, _) = tag(" -> ")(input)?; + let (input, dsts) = multi::separated_list1(tag(", "), mname)(input)?; + let (input, _) = newline(input)?; + Ok(( + input, + Module { + mname: name, + mtype, + dsts, + }, + )) + } + + fn broadcast_line(input: &str) -> IResult<&str, Module> { + let (input, _) = tag("broadcaster -> ")(input)?; + let (input, dsts) = multi::separated_list1(tag(", "), mname)(input)?; + let (input, _) = newline(input)?; + Ok(( + input, + Module { + mname: Mname("0".try_into().unwrap()), + mtype: Mtype::Broadcast, + dsts, + }, + )) + } + + fn line(input: &str) -> IResult<&str, Module> { + branch::alt((module_line, broadcast_line))(input) + } + + fn parse_helper(mut bufin: impl BufRead) -> Result> { + aoc::parse_with!(multi::many1(line), bufin) + } + + pub fn parse(bufin: impl BufRead) -> Result> { + let modules = parse_helper(bufin)?; + let dsts = modules + .iter() + .flat_map(|m| m.dsts.iter()) + .copied() + .collect::>(); + let mut modules = modules + .into_iter() + .map(|m| (m.mname, m)) + .collect::>(); + for dst_mname in dsts { + modules.entry(dst_mname).or_default(); + } + Ok(modules) + } +} + +#[test] +fn test1() -> Result<()> { + let input = parser::parse(EXAMPLE1.as_bytes())?; + assert_eq!(input.len(), 5); + Ok(()) +} + +#[test] +fn test2() -> Result<()> { + let input = parser::parse(EXAMPLE2.as_bytes())?; + assert_eq!(input.len(), 6); + Ok(()) +}