1
0
Fork 0
advent-of-code/2023/src/bin/20.rs

221 lines
6.1 KiB
Rust

advent_of_code::solution!(20);
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
}
pub fn part_one(input: &str) -> Option<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);
}
}
}
Some(high * low)
}
pub fn part_two(input: &str) -> Option<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 Some(c);
}
}
}
if let Some(mut node) = parts.get_mut(&dest) {
pulse(&mut node, signal, source, &mut queue);
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(32000000));
}
#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, None);
}
#[test]
fn sample_data2() {
let result = part_one(&advent_of_code::template::read_file_part(
"examples", DAY, 2,
));
assert_eq!(result, Some(11687500));
}
}