Files
adventofcode2023/day20/src/main_sane.rs
Acvaxoort 867a987508 day20
2023-12-20 20:10:58 +01:00

193 lines
6.0 KiB
Rust

use std::cmp;
use std::collections::{HashMap, VecDeque};
use std::fs::read_to_string;
use std::time::Instant;
use num::integer::lcm;
struct Pulse {
src: u16,
dest: u16,
high: bool,
}
enum ModuleKind {
Broadcast,
FlipFlop(bool),
Conjunction(Vec<bool>),
}
struct Module {
inputs: Vec<u16>,
outputs: Vec<u16>,
id: u16,
kind: ModuleKind,
}
impl Module {
fn new(id: u16, kind: ModuleKind) -> Module {
Module {
inputs: vec![],
outputs: vec![],
id,
kind,
}
}
fn new_broadcast(id: u16) -> Module {
Module::new(id, ModuleKind::Broadcast)
}
fn new_flipflop(id: u16) -> Module {
Module::new(id, ModuleKind::FlipFlop(false))
}
fn new_conjunction(id: u16) -> Module {
Module::new(id, ModuleKind::Conjunction(vec![]))
}
fn add_input(&mut self, id: u16) {
self.inputs.push(id);
match self.kind {
ModuleKind::Conjunction(ref mut memory) => {
memory.push(false);
}
_ => {}
}
}
fn add_output(&mut self, id: u16) {
self.outputs.push(id);
}
fn handle_pulse(&mut self, pulse: Pulse, queue: &mut VecDeque<Pulse>) {
match self.kind {
ModuleKind::Broadcast => {
queue.extend(self.outputs.iter().map(
|&id| Pulse { src: self.id, dest: id, high: pulse.high }));
}
ModuleKind::FlipFlop(ref mut on) => {
if !pulse.high {
*on = !*on;
queue.extend(self.outputs.iter().map(
|&id| Pulse { src: self.id, dest: id, high: *on }));
}
}
ModuleKind::Conjunction(ref mut memory) => {
if let Some(pos) = self.inputs.iter().position(|&id| id == pulse.src) {
memory[pos] = pulse.high;
let out_high = !memory.iter().all(|&v| v);
queue.extend(self.outputs.iter().map(
|&id| Pulse { src: self.id, dest: id, high: out_high }));
}
}
}
}
}
fn encode_module_name(name: &str) -> u16 {
let bytes = &name.as_bytes()[0..cmp::min(name.as_bytes().len(), 3)];
let mut acc = 0u16;
for &c in bytes {
acc = acc * 26 + (c - b'a') as u16;
}
acc
}
fn main() {
let time_start = Instant::now();
let input_str = read_to_string("input.txt").unwrap();
let time_start_no_io = Instant::now();
let mut modules: HashMap<u16, Module> = HashMap::new();
// let rx_id = encode_module_name("rx");
// modules.insert(rx_id, Module::new_flipflop(rx_id));
for line in input_str.lines() {
let mut split_whitespace = line.split_whitespace();
let name = split_whitespace.next().unwrap();
let new_module: Module;
let id = encode_module_name(&name[1..]);
match name.as_bytes()[0] {
b'%' => new_module = Module::new_flipflop(id),
b'&' => new_module = Module::new_conjunction(id),
_ => new_module = Module::new_broadcast(id)
}
modules.insert(id, new_module);
}
for line in input_str.lines() {
let mut split = line.split([' ', ',']).
filter(|&v| !v.is_empty());
let name = split.next().unwrap();
let in_id = encode_module_name(&name[1..]);
split.next();
while let Some(name) = split.next() {
let id = encode_module_name(name);
modules.get_mut(&in_id).unwrap().add_output(id);
match modules.get_mut(&id) {
None => {}
Some(in_module) => {
in_module.add_input(in_id);
}
}
}
}
// Part 1
let broadcast_id = encode_module_name("roadcast");
let mut low_pulses = 0u64;
let mut high_pulses = 0u64;
let mut queue: VecDeque<Pulse> = VecDeque::new();
for _ in 0..1000 {
let mut low_pulses_step = 0;
let mut high_pulses_step = 0;
queue.push_back(Pulse { src: u16::MAX, dest: broadcast_id, high: false });
while !queue.is_empty() {
let pulse = queue.pop_front().unwrap();
match pulse.high {
true => high_pulses_step += 1,
false => low_pulses_step += 1
}
match modules.get_mut(&pulse.dest) {
None => {}
Some(module) => {
module.handle_pulse(pulse, &mut queue);
}
}
}
low_pulses += low_pulses_step;
high_pulses += high_pulses_step;
}
let result1 = low_pulses * high_pulses;
// Part 2
// Use the knowledge of the network, that it is that broadcast drives a series of counters
let mut counter_periods: Vec<u64> = vec![];
for &node in &modules.get(&broadcast_id).unwrap().outputs {
let mut period = 0u16;
let mut current_node = node;
let mut bits = 0;
loop {
bits += 1;
let mut next_id = current_node;
period >>= 1;
let outputs = &modules.get(&current_node).unwrap().outputs;
for &output_id in outputs {
match modules.get(&output_id).unwrap().kind {
ModuleKind::Conjunction(_) => period |= 0x8000,
_ => next_id = output_id
}
}
if next_id == current_node {
break;
} else {
current_node = next_id;
}
}
period >>= 16 - bits;
counter_periods.push(period as u64);
}
let result2 = counter_periods.iter().fold(1u64, |acc, &v| lcm(acc, v));
let elapsed = time_start.elapsed().as_micros();
let elapsed_no_io = time_start_no_io.elapsed().as_micros();
println!("Time: {}us", elapsed);
println!("Time without file i/o: {}us", elapsed_no_io);
println!("Result1: {}", result1);
println!("Result2: {}", result2);
}