This commit is contained in:
Moritz Hölting 2023-12-20 16:22:42 +01:00
parent ff6ef36588
commit 082eb03bc1
5 changed files with 480 additions and 0 deletions

9
Cargo.lock generated
View File

@ -197,6 +197,15 @@ dependencies = [
"nom",
]
[[package]]
name = "day-20"
version = "0.0.0"
dependencies = [
"indoc",
"nom",
"num",
]
[[package]]
name = "deprecate-until"
version = "0.1.1"

13
day-20/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "day-20"
version = "0.0.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom.workspace = true
num = "0.4.1"
[dev-dependencies]
indoc.workspace = true

58
day-20/src/bin/input.txt Normal file
View File

@ -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

221
day-20/src/bin/part1.rs Normal file
View File

@ -0,0 +1,221 @@
use std::{
collections::{BTreeMap, HashMap, VecDeque},
sync::Mutex,
};
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{self, alpha1, line_ending},
combinator::{map, value},
multi::separated_list1,
sequence::{pair, separated_pair},
IResult,
};
fn main() {
println!("{}", part1(include_str!("./input.txt")));
}
fn part1(input: &str) -> usize {
let modules = input_parser(input).unwrap().1;
let conjunction_targets = modules
.iter()
.filter(|(_, mod_type, _)| mod_type == &Module::Conjunction)
.map(|(tag, _, _)| {
(
*tag,
modules
.iter()
.filter(|(_, _, targets)| targets.contains(tag))
.map(|(tag, _, _)| *tag)
.collect::<Vec<_>>(),
)
})
.collect::<HashMap<_, _>>();
let modules = modules
.into_iter()
.map(|(tag, mod_type, targets)| {
let module = match mod_type {
Module::Broadcaster => StatefulModule::Broadcaster { targets },
Module::FlipFlop => StatefulModule::FlipFlop {
targets,
state: Mutex::new(false),
},
Module::Conjunction => StatefulModule::Conjunction {
targets,
state: conjunction_targets[&tag]
.iter()
.map(|tag| (*tag, Mutex::new(false)))
.collect(),
},
};
(tag, module)
})
.collect::<HashMap<_, _>>();
let counter = PulseCounter::new();
for _ in 0..1000 {
let mut next_state = VecDeque::from(vec![("button", false, "broadcaster")]);
while let Some((prev_tag, high, tag)) = next_state.pop_front() {
counter.count(high);
if let Some(module) = modules.get(tag) {
next_state.extend(
module
.send(high, prev_tag)
.into_iter()
.map(|(t, h)| (tag, h, t)),
);
}
}
}
counter.get_product()
}
type ModuleVec<'a> = Vec<(&'a str, Module, Vec<&'a str>)>;
fn input_parser(i: &str) -> IResult<&str, ModuleVec> {
map(separated_list1(line_ending, module_parser), |modules| {
modules
.into_iter()
.map(|((mod_type, tag), targets)| (tag, mod_type, targets))
.collect::<Vec<_>>()
})(i)
}
fn module_parser(i: &str) -> IResult<&str, ((Module, &str), Vec<&str>)> {
separated_pair(
alt((
value((Module::Broadcaster, "broadcaster"), tag("broadcaster")),
pair(value(Module::FlipFlop, complete::char('%')), alpha1),
pair(value(Module::Conjunction, complete::char('&')), alpha1),
)),
tag(" -> "),
separated_list1(tag(", "), alpha1),
)(i)
}
#[derive(Debug)]
enum StatefulModule<'a, 'b, 'c> {
Broadcaster {
targets: Vec<&'a str>,
},
FlipFlop {
targets: Vec<&'b str>,
state: Mutex<bool>,
},
Conjunction {
targets: Vec<&'c str>,
state: BTreeMap<&'c str, Mutex<bool>>,
},
}
impl StatefulModule<'_, '_, '_> {
fn send(&self, high: bool, prev_tag: &str) -> Vec<(&str, bool)> {
match self {
Self::Broadcaster { targets } => {
targets.iter().map(|tag| (*tag, high)).collect::<Vec<_>>()
}
Self::FlipFlop { targets, state } => {
if high {
Vec::new()
} else {
let mut state = state.lock().unwrap();
*state = !*state;
let high = *state;
drop(state);
targets.iter().map(|tag| (*tag, high)).collect::<Vec<_>>()
}
}
Self::Conjunction { targets, state } => {
let mut sender_state = state[prev_tag].lock().unwrap();
*sender_state = high;
drop(sender_state);
let all_high = state.values().all(|state| *state.lock().unwrap());
targets
.iter()
.map(|tag| (*tag, !all_high))
.collect::<Vec<_>>()
}
}
}
}
#[derive(Debug)]
struct PulseCounter {
count_low: Mutex<usize>,
count_high: Mutex<usize>,
}
impl PulseCounter {
fn new() -> Self {
Self {
count_low: Mutex::new(0),
count_high: Mutex::new(0),
}
}
fn count(&self, high: bool) {
if high {
*self.count_high.lock().unwrap() += 1;
} else {
*self.count_low.lock().unwrap() += 1;
}
}
fn get(&self, high: bool) -> usize {
if high {
*self.count_high.lock().unwrap()
} else {
*self.count_low.lock().unwrap()
}
}
fn get_product(&self) -> usize {
self.get(true) * self.get(false)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Module {
Broadcaster,
FlipFlop,
Conjunction,
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
#[test]
fn test_part1_example1() {
assert_eq!(
part1(indoc!(
"
broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a
"
)),
32_000_000
);
}
#[test]
fn test_part1_example2() {
assert_eq!(
part1(indoc!(
"
broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output
"
)),
11_687_500
);
}
}

179
day-20/src/bin/part2.rs Normal file
View File

@ -0,0 +1,179 @@
use std::{
collections::{BTreeMap, HashMap, VecDeque},
sync::Mutex,
};
use nom::{
branch::alt,
bytes::complete::tag,
character::complete::{self, alpha1, line_ending},
combinator::{map, value},
multi::separated_list1,
sequence::{pair, separated_pair},
IResult,
};
fn main() {
println!("{}", part2(include_str!("./input.txt")));
}
fn part2(input: &str) -> usize {
let raw_modules = input_parser(input).unwrap().1;
let conjunction_targets = raw_modules
.iter()
.filter(|(_, mod_type, _)| mod_type == &Module::Conjunction)
.map(|(tag, _, _)| {
(
*tag,
raw_modules
.iter()
.filter(|(_, _, targets)| targets.contains(tag))
.map(|(tag, _, _)| *tag)
.collect::<Vec<_>>(),
)
})
.collect::<HashMap<_, _>>();
let modules = to_stateful_modules(&raw_modules, &conjunction_targets);
let starts = follow_signal(("button", false, "broadcaster"), &modules);
println!("{:?}", starts);
starts
.iter()
.map(|(_, i)| *i)
.reduce(num::integer::lcm)
.expect("empty list")
}
fn to_stateful_modules<'a>(
modules: &'a [(&'a str, Module, Vec<&'a str>)],
conjunction_targets: &HashMap<&str, Vec<&'a str>>,
) -> HashMap<&'a str, StatefulModule<'a, 'a, 'a>> {
modules
.iter()
.map(|(tag, mod_type, targets)| {
let module = match mod_type {
Module::Broadcaster => StatefulModule::Broadcaster {
targets: targets.clone(),
},
Module::FlipFlop => StatefulModule::FlipFlop {
targets: targets.clone(),
state: Mutex::new(false),
},
Module::Conjunction => StatefulModule::Conjunction {
targets: targets.clone(),
state: conjunction_targets[tag]
.iter()
.map(|tag| (*tag, Mutex::new(false)))
.collect(),
},
};
(*tag, module)
})
.collect()
}
fn follow_signal<'a>(
start: (&'a str, bool, &'a str),
modules: &'a HashMap<&'a str, StatefulModule<'a, 'a, 'a>>,
) -> Vec<(&'a str, usize)> {
let mut res = Vec::new();
let mut i = 0;
'button: loop {
i += 1;
let mut next_state = VecDeque::from(vec![start]);
while let Some((prev_tag, high, tag)) = next_state.pop_front() {
if !high && ["pv", "qh", "xm", "hz"].contains(&tag) {
res.push((tag, i));
}
if res.len() >= 4 {
break 'button;
}
if let Some(module) = modules.get(tag) {
next_state.extend(
module
.send(high, prev_tag)
.into_iter()
.map(|(t, h)| (tag, h, t)),
);
}
}
}
res
}
type ModuleVec<'a> = Vec<(&'a str, Module, Vec<&'a str>)>;
fn input_parser(i: &str) -> IResult<&str, ModuleVec> {
map(separated_list1(line_ending, module_parser), |modules| {
modules
.into_iter()
.map(|((mod_type, tag), targets)| (tag, mod_type, targets))
.collect::<Vec<_>>()
})(i)
}
fn module_parser(i: &str) -> IResult<&str, ((Module, &str), Vec<&str>)> {
separated_pair(
alt((
value((Module::Broadcaster, "broadcaster"), tag("broadcaster")),
pair(value(Module::FlipFlop, complete::char('%')), alpha1),
pair(value(Module::Conjunction, complete::char('&')), alpha1),
)),
tag(" -> "),
separated_list1(tag(", "), alpha1),
)(i)
}
#[derive(Debug)]
enum StatefulModule<'a, 'b, 'c> {
Broadcaster {
targets: Vec<&'a str>,
},
FlipFlop {
targets: Vec<&'b str>,
state: Mutex<bool>,
},
Conjunction {
targets: Vec<&'c str>,
state: BTreeMap<&'c str, Mutex<bool>>,
},
}
impl StatefulModule<'_, '_, '_> {
fn send(&self, high: bool, prev_tag: &str) -> Vec<(&str, bool)> {
match self {
Self::Broadcaster { targets } => {
targets.iter().map(|tag| (*tag, high)).collect::<Vec<_>>()
}
Self::FlipFlop { targets, state } => {
if high {
Vec::new()
} else {
let mut state = state.lock().unwrap();
*state = !*state;
let high = *state;
drop(state);
targets.iter().map(|tag| (*tag, high)).collect::<Vec<_>>()
}
}
Self::Conjunction { targets, state } => {
let mut sender_state = state[prev_tag].lock().unwrap();
*sender_state = high;
drop(sender_state);
let all_high = state.values().all(|state| *state.lock().unwrap());
targets
.iter()
.map(|tag| (*tag, !all_high))
.collect::<Vec<_>>()
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Module {
Broadcaster,
FlipFlop,
Conjunction,
}