day20 solution
parent
05861a06b0
commit
59e06d73eb
|
@ -0,0 +1,225 @@
|
|||
use aoc_runner_derive::aoc;
|
||||
use num::integer::lcm;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum MachineType {
|
||||
Broadcaster,
|
||||
FlipFlop,
|
||||
Conjuction,
|
||||
}
|
||||
|
||||
struct Machine {
|
||||
name: String,
|
||||
mt: MachineType,
|
||||
neighbors: Vec<String>,
|
||||
states: HashMap<String, bool>,
|
||||
}
|
||||
|
||||
fn pulse(
|
||||
machine: &mut Machine,
|
||||
signal: bool,
|
||||
source: String,
|
||||
queue: &mut VecDeque<(String, String, bool)>,
|
||||
) {
|
||||
match machine.mt {
|
||||
MachineType::Broadcaster => {
|
||||
for n in machine.neighbors.iter() {
|
||||
queue.push_back((n.to_string(), machine.name.clone(), signal));
|
||||
}
|
||||
}
|
||||
MachineType::FlipFlop => {
|
||||
if signal {
|
||||
return;
|
||||
}
|
||||
machine
|
||||
.states
|
||||
.entry(machine.name.clone())
|
||||
.and_modify(|s| *s = !*s);
|
||||
let &signal = machine.states.get(&machine.name).unwrap();
|
||||
for n in machine.neighbors.iter() {
|
||||
queue.push_back((n.to_string(), machine.name.clone(), signal));
|
||||
}
|
||||
}
|
||||
MachineType::Conjuction => {
|
||||
machine
|
||||
.states
|
||||
.entry(source)
|
||||
.and_modify(|s| *s = signal)
|
||||
.or_insert(signal);
|
||||
let mut next = false;
|
||||
for (_name, &t) in machine.states.iter() {
|
||||
if !t {
|
||||
next = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for n in machine.neighbors.iter() {
|
||||
queue.push_back((n.to_string(), machine.name.clone(), next));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse<'a>(input: &str) -> HashMap<String, Machine> {
|
||||
let mut r: HashMap<String, Machine> = HashMap::new();
|
||||
let mut c: Vec<String> = vec![];
|
||||
for line in input.lines() {
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let lparts = line.split(" -> ").collect::<Vec<_>>();
|
||||
|
||||
let name = if lparts[0].starts_with("&") {
|
||||
lparts[0].strip_prefix("&").unwrap()
|
||||
} else if lparts[0].starts_with("%") {
|
||||
lparts[0].strip_prefix("%").unwrap()
|
||||
} else {
|
||||
lparts[0]
|
||||
};
|
||||
|
||||
let neighbors = lparts[1]
|
||||
.split(", ")
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if name == "broadcaster" {
|
||||
r.insert(
|
||||
name.to_string(),
|
||||
Machine {
|
||||
name: name.to_string(),
|
||||
mt: MachineType::Broadcaster,
|
||||
neighbors,
|
||||
states: HashMap::with_capacity(0),
|
||||
},
|
||||
);
|
||||
} else if line.starts_with("&") {
|
||||
c.push(name.to_string());
|
||||
r.insert(
|
||||
name.to_string(),
|
||||
Machine {
|
||||
name: name.to_string(),
|
||||
mt: MachineType::Conjuction,
|
||||
neighbors,
|
||||
states: HashMap::new(),
|
||||
},
|
||||
);
|
||||
} else if line.starts_with("%") {
|
||||
r.insert(
|
||||
name.to_string(),
|
||||
Machine {
|
||||
name: name.to_string(),
|
||||
mt: MachineType::FlipFlop,
|
||||
neighbors,
|
||||
states: HashMap::from([(name.to_string(), false)]),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut c2: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for (name, node) in r.iter() {
|
||||
for n in node.neighbors.iter() {
|
||||
if c.contains(n) {
|
||||
c2.entry(n.clone())
|
||||
.and_modify(|t| t.push(name.clone()))
|
||||
.or_insert(vec![name.clone()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (name, inputs) in c2.iter() {
|
||||
let node = r.get_mut(name).unwrap();
|
||||
for i in inputs.iter() {
|
||||
node.states.entry(i.clone()).or_insert(false);
|
||||
}
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
#[aoc(day20, part1)]
|
||||
fn part1(input: &str) -> u64 {
|
||||
let mut parts = parse(input);
|
||||
let mut queue: VecDeque<(String, String, bool)> = VecDeque::new();
|
||||
let mut high = 0;
|
||||
let mut low = 0;
|
||||
for _ in 0..1000 {
|
||||
queue.push_back((String::from("broadcaster"), String::from("button"), false));
|
||||
while let Some((dest, source, signal)) = queue.pop_front() {
|
||||
if signal {
|
||||
high += 1;
|
||||
} else {
|
||||
low += 1;
|
||||
}
|
||||
if let Some(mut node) = parts.get_mut(&dest) {
|
||||
pulse(&mut node, signal, source, &mut queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
high * low
|
||||
}
|
||||
|
||||
#[aoc(day20, part2)]
|
||||
fn part2(input: &str) -> u64 {
|
||||
let mut parts = parse(input);
|
||||
let mut queue: VecDeque<(String, String, bool)> = VecDeque::new();
|
||||
let mut last_cons = vec![
|
||||
String::from("zl"),
|
||||
String::from("xn"),
|
||||
String::from("qn"),
|
||||
String::from("xf"),
|
||||
];
|
||||
let mut con_indexes: Vec<u64> = vec![];
|
||||
let mut c = 1;
|
||||
for i in 0..10000 {
|
||||
queue.push_back((String::from("broadcaster"), String::from("button"), false));
|
||||
while let Some((dest, source, signal)) = queue.pop_front() {
|
||||
if signal {
|
||||
if let Some(index) = last_cons.iter().position(|s| *s == source) {
|
||||
last_cons.remove(index);
|
||||
con_indexes.push(i + 1);
|
||||
c = lcm(i + 1, c);
|
||||
if last_cons.is_empty() {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mut node) = parts.get_mut(&dest) {
|
||||
pulse(&mut node, signal, source, &mut queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const SAMPLE_DATA: &'static str = r#"broadcaster -> a, b, c
|
||||
%a -> b
|
||||
%b -> c
|
||||
%c -> inv
|
||||
&inv -> a
|
||||
"#;
|
||||
|
||||
const SAMPLE_DATA2: &'static str = r#"broadcaster -> a
|
||||
%a -> inv, con
|
||||
&inv -> b
|
||||
%b -> con
|
||||
&con -> output"#;
|
||||
|
||||
#[test]
|
||||
fn sample_data() {
|
||||
assert_eq!(part1(&SAMPLE_DATA), 32000000);
|
||||
// assert_eq!(part2(&SAMPLE_DATA), 71503);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_data2() {
|
||||
assert_eq!(part1(&SAMPLE_DATA2), 11687500);
|
||||
}
|
||||
}
|
|
@ -19,5 +19,6 @@ mod day16;
|
|||
mod day17;
|
||||
mod day18;
|
||||
mod day19;
|
||||
mod day20;
|
||||
|
||||
aoc_lib! { year = 2023 }
|
||||
|
|
Loading…
Reference in New Issue