day20: problem 2 solution - needed a hint
This commit is contained in:
		
							
								
								
									
										83
									
								
								20/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										83
									
								
								20/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -11,11 +11,18 @@ dependencies = [ | ||||
|  "memchr", | ||||
| ] | ||||
|  | ||||
| [[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 = [ | ||||
|  "lazy-regex", | ||||
|  "num", | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -47,6 +54,82 @@ version = "2.6.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 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]] | ||||
| name = "once_cell" | ||||
| version = "1.19.0" | ||||
|   | ||||
| @@ -7,3 +7,4 @@ edition = "2021" | ||||
|  | ||||
| [dependencies] | ||||
| 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::fmt::Display; | ||||
| use std::fs::File; | ||||
| use std::hash::{Hash, Hasher}; | ||||
| use std::io::{BufRead, BufReader, Lines}; | ||||
| use std::time::Instant; | ||||
|  | ||||
| use lazy_regex::lazy_regex; | ||||
| use num::integer::lcm; | ||||
|  | ||||
| // BOILERPLATE | ||||
| 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, ]+)$"); | ||||
|  | ||||
| #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||||
| #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] | ||||
| enum PulseType { | ||||
|     Low, | ||||
|     High, | ||||
| @@ -79,7 +81,15 @@ struct Module { | ||||
|     inputs: Vec<String>, | ||||
|     outputs: Vec<String>, | ||||
|     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 { | ||||
| @@ -90,18 +100,19 @@ impl Module { | ||||
|             inputs: Vec::new(), | ||||
|             outputs: Vec::new(), | ||||
|             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 { | ||||
|         let mut m = Self::new(name); | ||||
|         m.kind = Some(kind); | ||||
|         m | ||||
|     } | ||||
|     fn and_destinations(mut self, destinations: &str) -> Self { | ||||
|         println!("{} destinations: {}", self.name, destinations); | ||||
|         for dest in destinations.split(", ") { | ||||
|             println!("  {}", dest); | ||||
|             self.outputs.push(dest.into()); | ||||
|         } | ||||
|         self | ||||
| @@ -111,14 +122,28 @@ impl Module { | ||||
|         self | ||||
|     } | ||||
|     fn add_source(&mut self, source: String) {     | ||||
|         self.inputs.push(source.clone()); | ||||
|         self.last_input_states.insert(source, PulseType::Low); | ||||
|         self.inputs.push(source); | ||||
|         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 { | ||||
|     fn from(s: &str) -> Self { | ||||
|         println!("s: {}", s); | ||||
|         let Some(re_result) = MODULE_PATTERN.captures(s) else { | ||||
|             panic!("unparseable module: {}", s); | ||||
|         }; | ||||
| @@ -166,7 +191,7 @@ struct Machine { | ||||
|     modules: HashMap<String, Module>, | ||||
|     work_queue: BinaryHeap<Job>, | ||||
|     count_low: u64, | ||||
|     count_high: u64 | ||||
|     count_high: u64, | ||||
| } | ||||
|  | ||||
| impl<T: BufRead> From<Lines<T>> for Machine { | ||||
| @@ -199,7 +224,8 @@ impl<T: BufRead> From<Lines<T>> for Machine { | ||||
|         Machine { | ||||
|             modules, | ||||
|             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) => { | ||||
|                 self.modules | ||||
|                     .get_mut(target) | ||||
|                     .unwrap() | ||||
|                     .last_input_states | ||||
|                     .insert(from.to_owned(), signal); | ||||
|                 if self.modules[target] | ||||
|                     .last_input_states | ||||
|                     .values() | ||||
|                     .all(|state| *state == PulseType::High) | ||||
|                 { | ||||
|                 let target_m = self.modules.get_mut(target).unwrap(); | ||||
|                 let source_idx = target_m.source_idx(from); | ||||
|                 target_m.last_input_states[source_idx] = signal; | ||||
|                 if target_m.last_input_states.iter().all(|state| *state == PulseType::High) { | ||||
|                     self.send_all_outputs(PulseType::Low, target, priority) | ||||
|                 } else { | ||||
|                     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 | ||||
| const PROBLEM1_ITERATIONS: usize = 1000; | ||||
| fn problem1<T: BufRead>(input: Lines<T>) -> u64 { | ||||
|     let mut machine = Machine::from(input); | ||||
|     println!("{:?}", machine); | ||||
|     for _ in 0..PROBLEM1_ITERATIONS { | ||||
|         machine.press_button(); | ||||
|         machine.run(); | ||||
| @@ -275,8 +316,31 @@ fn problem1<T: BufRead>(input: Lines<T>) -> u64 { | ||||
| } | ||||
|  | ||||
| // PROBLEM 2 solution | ||||
| fn problem2<T: BufRead>(input: Lines<T>) -> u64 { | ||||
|     0 | ||||
| fn problem2<T: BufRead>(input: Lines<T>) -> u128 { | ||||
|     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)] | ||||
| @@ -295,10 +359,4 @@ mod tests { | ||||
|         let c = Cursor::new(EXAMPLE); | ||||
|         assert_eq!(problem1(c.lines()), 11687500); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn problem2_example() { | ||||
|         let c = Cursor::new(EXAMPLE); | ||||
|         assert_eq!(problem2(c.lines()), 0); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user