day20: problem 2 solution - needed a hint
This commit is contained in:
parent
877101f9a2
commit
512b05f624
83
20/Cargo.lock
generated
83
20/Cargo.lock
generated
@ -11,11 +11,18 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "day20"
|
name = "day20"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy-regex",
|
"lazy-regex",
|
||||||
|
"num",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -47,6 +54,82 @@ version = "2.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[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",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy-regex = "3.1.0"
|
lazy-regex = "3.1.0"
|
||||||
|
num = "0.4.1"
|
||||||
|
116
20/src/main.rs
116
20/src/main.rs
@ -1,10 +1,12 @@
|
|||||||
use std::collections::{BinaryHeap, HashMap, VecDeque};
|
use std::collections::{BinaryHeap, HashMap, VecDeque};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::io::{BufRead, BufReader, Lines};
|
use std::io::{BufRead, BufReader, Lines};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use lazy_regex::lazy_regex;
|
use lazy_regex::lazy_regex;
|
||||||
|
use num::integer::lcm;
|
||||||
|
|
||||||
// BOILERPLATE
|
// BOILERPLATE
|
||||||
type InputIter = Lines<BufReader<File>>;
|
type InputIter = Lines<BufReader<File>>;
|
||||||
@ -31,7 +33,7 @@ fn main() {
|
|||||||
|
|
||||||
const MODULE_PATTERN: lazy_regex::Lazy<lazy_regex::Regex> = lazy_regex!("^([%&]?)([a-z]+) -> ([a-z, ]+)$");
|
const MODULE_PATTERN: lazy_regex::Lazy<lazy_regex::Regex> = lazy_regex!("^([%&]?)([a-z]+) -> ([a-z, ]+)$");
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
||||||
enum PulseType {
|
enum PulseType {
|
||||||
Low,
|
Low,
|
||||||
High,
|
High,
|
||||||
@ -79,7 +81,15 @@ struct Module {
|
|||||||
inputs: Vec<String>,
|
inputs: Vec<String>,
|
||||||
outputs: Vec<String>,
|
outputs: Vec<String>,
|
||||||
on: bool,
|
on: bool,
|
||||||
last_input_states: HashMap<String, PulseType>,
|
last_input_states: Vec<PulseType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Module {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
// the only states that change are 'on' and 'last_input_states', the rest are constant once created
|
||||||
|
self.on.hash(state);
|
||||||
|
self.last_input_states.hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
@ -90,18 +100,19 @@ impl Module {
|
|||||||
inputs: Vec::new(),
|
inputs: Vec::new(),
|
||||||
outputs: Vec::new(),
|
outputs: Vec::new(),
|
||||||
on: false,
|
on: false,
|
||||||
last_input_states: HashMap::new(),
|
last_input_states: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn source_idx(&self, name: &str) -> usize {
|
||||||
|
self.inputs.iter().position(|in_name| in_name == name).unwrap()
|
||||||
|
}
|
||||||
fn with_kind(name: String, kind: ModuleKind) -> Self {
|
fn with_kind(name: String, kind: ModuleKind) -> Self {
|
||||||
let mut m = Self::new(name);
|
let mut m = Self::new(name);
|
||||||
m.kind = Some(kind);
|
m.kind = Some(kind);
|
||||||
m
|
m
|
||||||
}
|
}
|
||||||
fn and_destinations(mut self, destinations: &str) -> Self {
|
fn and_destinations(mut self, destinations: &str) -> Self {
|
||||||
println!("{} destinations: {}", self.name, destinations);
|
|
||||||
for dest in destinations.split(", ") {
|
for dest in destinations.split(", ") {
|
||||||
println!(" {}", dest);
|
|
||||||
self.outputs.push(dest.into());
|
self.outputs.push(dest.into());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
@ -111,14 +122,28 @@ impl Module {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn add_source(&mut self, source: String) {
|
fn add_source(&mut self, source: String) {
|
||||||
self.inputs.push(source.clone());
|
self.inputs.push(source);
|
||||||
self.last_input_states.insert(source, PulseType::Low);
|
self.last_input_states.push(PulseType::Low);
|
||||||
|
}
|
||||||
|
fn reset(&mut self) {
|
||||||
|
// relevant state is on and last_input_states
|
||||||
|
self.on = false;
|
||||||
|
for input in &mut self.last_input_states {
|
||||||
|
*input = PulseType::Low;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn state_space(&self) -> u128 {
|
||||||
|
match self.kind {
|
||||||
|
Some(ModuleKind::Broadcast) | Some(ModuleKind::Button) => 1,
|
||||||
|
Some(ModuleKind::FlipFlop) => 2,
|
||||||
|
Some(ModuleKind::Conjunction) => 1 << self.last_input_states.len(), // 2^n
|
||||||
|
None => 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Module {
|
impl From<&str> for Module {
|
||||||
fn from(s: &str) -> Self {
|
fn from(s: &str) -> Self {
|
||||||
println!("s: {}", s);
|
|
||||||
let Some(re_result) = MODULE_PATTERN.captures(s) else {
|
let Some(re_result) = MODULE_PATTERN.captures(s) else {
|
||||||
panic!("unparseable module: {}", s);
|
panic!("unparseable module: {}", s);
|
||||||
};
|
};
|
||||||
@ -166,7 +191,7 @@ struct Machine {
|
|||||||
modules: HashMap<String, Module>,
|
modules: HashMap<String, Module>,
|
||||||
work_queue: BinaryHeap<Job>,
|
work_queue: BinaryHeap<Job>,
|
||||||
count_low: u64,
|
count_low: u64,
|
||||||
count_high: u64
|
count_high: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: BufRead> From<Lines<T>> for Machine {
|
impl<T: BufRead> From<Lines<T>> for Machine {
|
||||||
@ -199,7 +224,8 @@ impl<T: BufRead> From<Lines<T>> for Machine {
|
|||||||
Machine {
|
Machine {
|
||||||
modules,
|
modules,
|
||||||
work_queue: BinaryHeap::new(),
|
work_queue: BinaryHeap::new(),
|
||||||
count_low: 0, count_high: 0
|
count_low: 0,
|
||||||
|
count_high: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,16 +253,10 @@ impl Machine {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(ModuleKind::Conjunction) => {
|
Some(ModuleKind::Conjunction) => {
|
||||||
self.modules
|
let target_m = self.modules.get_mut(target).unwrap();
|
||||||
.get_mut(target)
|
let source_idx = target_m.source_idx(from);
|
||||||
.unwrap()
|
target_m.last_input_states[source_idx] = signal;
|
||||||
.last_input_states
|
if target_m.last_input_states.iter().all(|state| *state == PulseType::High) {
|
||||||
.insert(from.to_owned(), signal);
|
|
||||||
if self.modules[target]
|
|
||||||
.last_input_states
|
|
||||||
.values()
|
|
||||||
.all(|state| *state == PulseType::High)
|
|
||||||
{
|
|
||||||
self.send_all_outputs(PulseType::Low, target, priority)
|
self.send_all_outputs(PulseType::Low, target, priority)
|
||||||
} else {
|
} else {
|
||||||
self.send_all_outputs(PulseType::High, target, priority)
|
self.send_all_outputs(PulseType::High, target, priority)
|
||||||
@ -260,13 +280,34 @@ impl Machine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// the number of cycles it took until the target sends a low pulse, or None if the target node sent no low pulses but the job finished
|
||||||
|
fn time_to_state(&mut self, goal:&str, state: PulseType) -> Option<u64> {
|
||||||
|
while let Some(job) = self.work_queue.pop() {
|
||||||
|
if job.from == goal && job.signal == state {
|
||||||
|
return Some(self.count_high + self.count_low);
|
||||||
|
}
|
||||||
|
for target in job.targets {
|
||||||
|
self.send_pulse(job.signal, &target, &job.from, job.priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn reset(&mut self) {
|
||||||
|
for (_name, m) in &mut self.modules {
|
||||||
|
m.reset();
|
||||||
|
}
|
||||||
|
self.count_low = 0;
|
||||||
|
self.count_high = 0;
|
||||||
|
}
|
||||||
|
fn state_space(&self) -> u128 {
|
||||||
|
self.modules.values().map(|m| m.state_space()).product()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 1 solution
|
// PROBLEM 1 solution
|
||||||
const PROBLEM1_ITERATIONS: usize = 1000;
|
const PROBLEM1_ITERATIONS: usize = 1000;
|
||||||
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
||||||
let mut machine = Machine::from(input);
|
let mut machine = Machine::from(input);
|
||||||
println!("{:?}", machine);
|
|
||||||
for _ in 0..PROBLEM1_ITERATIONS {
|
for _ in 0..PROBLEM1_ITERATIONS {
|
||||||
machine.press_button();
|
machine.press_button();
|
||||||
machine.run();
|
machine.run();
|
||||||
@ -275,8 +316,31 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PROBLEM 2 solution
|
// PROBLEM 2 solution
|
||||||
fn problem2<T: BufRead>(input: Lines<T>) -> u64 {
|
fn problem2<T: BufRead>(input: Lines<T>) -> u128 {
|
||||||
0
|
let mut machine = Machine::from(input);
|
||||||
|
println!("STATES: {}", machine.state_space());
|
||||||
|
// Find the rx module and look at its parent(s)
|
||||||
|
let rx = &machine.modules["rx"];
|
||||||
|
// my input has a single conjunction as parent, todo implement other possibilities
|
||||||
|
let con = &machine.modules[&rx.inputs[0]];
|
||||||
|
|
||||||
|
// for each input, find how long it takes for it to be High, so the conjunction sends a low to rx
|
||||||
|
let mut cycles: Vec<_> = Vec::new();
|
||||||
|
for input in con.inputs.clone() {
|
||||||
|
print!("searching distance to {}...", input);
|
||||||
|
machine.reset();
|
||||||
|
let mut button_count = 0;
|
||||||
|
loop {
|
||||||
|
machine.press_button();
|
||||||
|
button_count += 1;
|
||||||
|
if let Some(distance) = machine.time_to_state(&input, PulseType::High) {
|
||||||
|
println!("got {} pulses, {} button presses", distance, button_count);
|
||||||
|
cycles.push(button_count as u128);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cycles.iter().fold(1, |accum, cycle| lcm(accum, *cycle))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -295,10 +359,4 @@ mod tests {
|
|||||||
let c = Cursor::new(EXAMPLE);
|
let c = Cursor::new(EXAMPLE);
|
||||||
assert_eq!(problem1(c.lines()), 11687500);
|
assert_eq!(problem1(c.lines()), 11687500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn problem2_example() {
|
|
||||||
let c = Cursor::new(EXAMPLE);
|
|
||||||
assert_eq!(problem2(c.lines()), 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user