day 20
This commit is contained in:
parent
ff6ef36588
commit
082eb03bc1
|
@ -197,6 +197,15 @@ dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day-20"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"indoc",
|
||||||
|
"nom",
|
||||||
|
"num",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deprecate-until"
|
name = "deprecate-until"
|
||||||
version = "0.1.1"
|
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