This commit is contained in:
Acvaxoort 2023-12-20 20:10:58 +01:00
parent 049a2181ad
commit 867a987508
6 changed files with 666 additions and 0 deletions

92
day20/Cargo.lock generated Normal file
View File

@ -0,0 +1,92 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day20"
version = "0.1.0"
dependencies = [
"num",
]
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]

18
day20/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "day20"
version = "0.1.0"
edition = "2021"
default-run = "sane"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
num = "0.4.1"
[[bin]]
name = "oop"
path= "src/main_oop.rs"
[[bin]]
name = "sane"
path= "src/main_sane.rs"

58
day20/input.txt Normal file
View File

@ -0,0 +1,58 @@
%vb -> ck
%pb -> xv
&gt -> jq
%hj -> lk, hh
%zd -> fm
%hr -> hh
%rg -> tp
%tf -> ck, tx
%pp -> vs, hh
%vx -> df
%tx -> mr, ck
%nh -> vx
%sc -> ck, vb
%cc -> ck, rj
%tn -> kz, lt
%fp -> rb
%hc -> kz
%rb -> ns, mf
%pc -> vh
broadcaster -> tf, br, zn, nc
%zn -> kz, fv
&ns -> pb, lr, br, fp, gp, gv, jl
%nc -> hh, hj
%mf -> ns, gp
%xv -> ns, kh
%rj -> ck, sc
%tg -> cc
%gp -> pb
%jz -> lj, ns
%jl -> fp
&vr -> jq
&jq -> rx
&kz -> zf, nl, df, zn, vx, nh
%gv -> jl
%gf -> zf, kz
%df -> gf
%kq -> pp
%lk -> hh, zd
%vs -> bp
%lt -> ls, kz
&nl -> jq
%mr -> rg
%zf -> qf
%br -> gv, ns
%rk -> hh, hr
%qf -> kz, tn
%rv -> rk, hh
%ls -> hc, kz
%fm -> kq
&ck -> tp, vr, pc, tg, mr, tf, rg
%fv -> nh, kz
%tp -> pc
%vh -> ck, tg
&hh -> vs, kq, gt, nc, zd, fm
&lr -> jq
%kh -> ns, jz
%bp -> hh, rv
%lj -> ns

View File

@ -0,0 +1,92 @@
The input of rx is conjunction of hh, ck, kz, ns (truth = low pulse)
&jq -> rx
&gt -> jq
&vr -> jq
&nl -> jq
&lr -> jq
Counter 1
&hh -> nc, zd, vs, kq, fm | -> gt
%nc -> hj, hh | <- hh, broadcast
%hj -> lk, hh
%lk -> zd, hh
%zd -> fm | <- hh
%fm -> kq | <- hh
%kq -> pp | <- hh
%pp -> vs, hh
%vs -> bp | <- hh
%bp -> rv, hh
%rv -> rk, hh
%rk -> hr, hh
%hr -> hh
111101000111(2) = 3911
Counter 2
&ck -> tf, mr, rg, tp, pc, tg | -> vr
%tf -> tx, ck | <- broadcast, ck
%tx -> mr, ck
%mr -> rg | <- ck
%rg -> tp | <- ck
%tp -> pc | <- ck
%pc -> vh | <- ck
%vh -> tg, ck
%tg -> cc | <- ck
%cc -> rj, ck
%rj -> sc, ck
%sc -> vb, ck
%vb -> ck
111101000011(2) = 3907
Counter 3
&kz -> zn, nh, vx, df, zf | -> nl
%zn -> fv, kz | <- broadcast, kz
%fv -> nh, kz
%nh -> vx | <- kz
%vx -> df | <- kz
%df -> gf | <- kz
%gf -> zf, kz
%zf -> qf | <- kz
%qf -> tn, kz
%tn -> lt, kz
%lt -> ls, kz
%ls -> hc, kz
%hc -> kz
111110100011(2) = 4003
Counter 4
&ns -> pb, br, fp, gp, gv, jl | -> lr
%br -> gv, ns | <- broadcast
%gv -> jl
%jl -> fp
%fp -> rb
%rb -> mf, ns
%mf -> gp, ns
%gp -> pb
%pb -> xv
%xv -> kh, ns
%kh -> jz, ns
%jz -> lj, ns
%lj -> ns
111100110001(2) = 3889
The broadcast node drives all the counters
broadcaster -> nc, tf, zn, br
Period of rx (same as first activation) is LCM of periods of all counters
LCM = 237878264003759

214
day20/src/main_oop.rs Normal file
View File

@ -0,0 +1,214 @@
use std::any::Any;
use std::cmp;
use std::collections::{HashMap, VecDeque};
use std::fs::read_to_string;
use std::time::Instant;
// Object-oriented, polymorphic way of implementing it,
// does less than main_sane.rs (only part 1) with more lines of code
struct Pulse {
src: u16,
dest: u16,
high: bool,
}
trait Module: Any {
fn handle_pulse(&mut self, pulse: Pulse, queue: &mut VecDeque<Pulse>);
fn add_input(&mut self, id: u16);
fn add_output(&mut self, id: u16);
}
struct ModuleBase {
inputs: Vec<u16>,
outputs: Vec<u16>,
id: u16,
}
impl ModuleBase {
fn new(id: u16) -> ModuleBase {
ModuleBase {
inputs: vec![],
outputs: vec![],
id: id,
}
}
fn add_input(&mut self, id: u16) {
self.inputs.push(id);
}
fn add_output(&mut self, id: u16) {
self.outputs.push(id);
}
}
struct BroadcastModule {
base: ModuleBase,
}
impl BroadcastModule {
fn new(id: u16) -> BroadcastModule {
BroadcastModule {
base: ModuleBase::new(id)
}
}
}
impl Module for BroadcastModule {
fn handle_pulse(&mut self, pulse: Pulse, queue: &mut VecDeque<Pulse>) {
queue.extend(self.base.outputs.iter().map(
|&id| Pulse { src: self.base.id, dest: id, high: pulse.high }));
}
fn add_input(&mut self, id: u16) {
self.base.add_input(id);
}
fn add_output(&mut self, id: u16) {
self.base.add_output(id);
}
}
struct FlipFlopModule {
base: ModuleBase,
on: bool,
}
impl FlipFlopModule {
fn new(id: u16) -> FlipFlopModule {
FlipFlopModule {
base: ModuleBase::new(id),
on: false,
}
}
}
impl Module for FlipFlopModule {
fn handle_pulse(&mut self, pulse: Pulse, queue: &mut VecDeque<Pulse>) {
if !pulse.high {
self.on = !self.on;
queue.extend(self.base.outputs.iter().map(
|&id| Pulse { src: self.base.id, dest: id, high: self.on }));
}
}
fn add_input(&mut self, id: u16) {
self.base.add_input(id);
}
fn add_output(&mut self, id: u16) {
self.base.add_output(id);
}
}
struct ConjunctionModule {
base: ModuleBase,
memory: Vec<bool>,
}
impl ConjunctionModule {
fn new(id: u16) -> ConjunctionModule {
ConjunctionModule {
base: ModuleBase::new(id),
memory: vec![],
}
}
}
impl Module for ConjunctionModule {
fn handle_pulse(&mut self, pulse: Pulse, queue: &mut VecDeque<Pulse>) {
if let Some(pos) = self.base.inputs.iter().position(|&id| id == pulse.src) {
self.memory[pos] = pulse.high;
let out_high = !self.memory.iter().all(|&v| v);
queue.extend(self.base.outputs.iter().map(
|&id| Pulse { src: self.base.id, dest: id, high: out_high }));
}
}
fn add_input(&mut self, id: u16) {
self.base.add_input(id);
self.memory.push(false);
}
fn add_output(&mut self, id: u16) {
self.base.add_output(id);
}
}
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, Box<dyn Module>> = HashMap::new();
// let rx_id = encode_module_name("rx");
// modules.insert(rx_id, Box::new(FlipFlopModule::new(rx_id)));
for line in input_str.lines() {
let mut split_whitespace = line.split_whitespace();
let name = split_whitespace.next().unwrap();
let new_module: Box<dyn Module>;
let id = encode_module_name(&name[1..]);
match name.as_bytes()[0] {
b'%' => new_module = Box::new(FlipFlopModule::new(id)),
b'&' => new_module = Box::new(ConjunctionModule::new(id)),
_ => new_module = Box::new(BroadcastModule::new(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);
}
}
}
}
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;
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);
}

192
day20/src/main_sane.rs Normal file
View 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(&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);
}