day20
This commit is contained in:
192
day20/src/main_sane.rs
Normal file
192
day20/src/main_sane.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
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);
|
||||
}
|
Reference in New Issue
Block a user