day 20
This commit is contained in:
parent
ff6ef36588
commit
082eb03bc1
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
Loading…
Reference in New Issue