Skip to content

Commit

Permalink
Day 20a
Browse files Browse the repository at this point in the history
  • Loading branch information
lpenz committed Dec 20, 2023
1 parent c2b4f33 commit 7c03b53
Show file tree
Hide file tree
Showing 6 changed files with 351 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ members = [
"day16",
"day17",
"day18",
"day20",
]

10 changes: 10 additions & 0 deletions day20/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
58 changes: 58 additions & 0 deletions day20/input.txt
Original file line number Diff line number Diff line change
@@ -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
116 changes: 116 additions & 0 deletions day20/src/bin/day20a.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (C) 2023 Leandro Lisboa Penz <[email protected]>
// 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<Mname, bool>,
}

fn eval<'a>(
module: &'a Module,
msts: &'a mut ModState,
src_mname: Mname,
pulse: bool,
) -> Box<dyn Iterator<Item = (Mname, bool, Mname)> + '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<u64> {
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()))
}
150 changes: 150 additions & 0 deletions day20/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright (C) 2023 Leandro Lisboa Penz <[email protected]>
// 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<Self, Self::Err> {
Ok(Mname(copstr::Str::<6>::try_from(s)?))
}
}

#[derive(Debug, Hash, PartialEq, Eq, Default)]
pub enum Mtype {
#[default]
None,
FlipFlop,
Conjunct,
Broadcast,
}

impl TryFrom<char> for Mtype {
type Error = Report;
fn try_from(c: char) -> Result<Self, Self::Error> {
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<Mname>,
}

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<Vec<Module>> {
aoc::parse_with!(multi::many1(line), bufin)
}

pub fn parse(bufin: impl BufRead) -> Result<BTreeMap<Mname, Module>> {
let modules = parse_helper(bufin)?;
let dsts = modules
.iter()
.flat_map(|m| m.dsts.iter())
.copied()
.collect::<Vec<Mname>>();
let mut modules = modules
.into_iter()
.map(|m| (m.mname, m))
.collect::<BTreeMap<_, _>>();
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(())
}

0 comments on commit 7c03b53

Please sign in to comment.