193 lines
6.0 KiB
Rust
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(¤t_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);
|
|
}
|