Skip to content

Commit d617f10

Browse files
committed
Day 20 puzzle 1
1 parent f85316c commit d617f10

File tree

4 files changed

+390
-0
lines changed

4 files changed

+390
-0
lines changed

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ pub fn run_day(data_dir: &str, day: &u32) -> Result<(), Error> {
8686
solutions::day19::main(data_dir);
8787
Ok(())
8888
}
89+
20 => {
90+
solutions::day20::main(data_dir);
91+
Ok(())
92+
}
8993
// <-- INSERT NEW DAY HERE -->
9094
_ => Err(Error::DayNotImplemented(*day)),
9195
}

src/solutions/day20.rs

Lines changed: 350 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,350 @@
1+
use crate::data::load;
2+
use std::collections::{HashMap, HashSet, VecDeque};
3+
use thiserror::Error;
4+
5+
#[derive(Error, Debug, PartialEq, Eq)]
6+
pub enum PuzzleErr {
7+
#[error("Input parsing error")]
8+
ParseInputError,
9+
#[error("Runtime error")]
10+
RuntimeError,
11+
}
12+
13+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14+
enum Pulse {
15+
High,
16+
Low,
17+
}
18+
19+
#[derive(Debug, Clone)]
20+
struct PulseMsg {
21+
from: String,
22+
to: String,
23+
pulse: Pulse,
24+
}
25+
26+
trait Receiver {
27+
fn receive(&mut self, in_pulse: &PulseMsg) -> Option<VecDeque<PulseMsg>>;
28+
}
29+
30+
#[derive(Debug, Clone)]
31+
struct Broadcast {
32+
name: String,
33+
receivers: Vec<String>,
34+
}
35+
36+
fn _parse_after_arrow(line: &str) -> Vec<String> {
37+
line.trim()
38+
.split("->")
39+
.nth(1)
40+
.unwrap()
41+
.trim()
42+
.split(", ")
43+
.map(|s| s.to_string())
44+
.collect::<Vec<_>>()
45+
}
46+
47+
impl Broadcast {
48+
fn new(receivers: &[String]) -> Self {
49+
Broadcast {
50+
name: "broadcaster".to_string(),
51+
receivers: receivers.to_owned(),
52+
}
53+
}
54+
55+
fn from(line: &str) -> Self {
56+
let receivers = _parse_after_arrow(line);
57+
Self::new(&receivers)
58+
}
59+
}
60+
61+
impl Receiver for Broadcast {
62+
fn receive(&mut self, in_pulse: &PulseMsg) -> Option<VecDeque<PulseMsg>> {
63+
Option::Some(
64+
self.receivers
65+
.iter()
66+
.map(|r| PulseMsg {
67+
from: self.name.clone(),
68+
to: r.clone(),
69+
pulse: in_pulse.pulse,
70+
})
71+
.collect::<VecDeque<_>>(),
72+
)
73+
}
74+
}
75+
76+
#[derive(Debug, Clone)]
77+
struct Conjunction {
78+
name: String,
79+
memory: HashMap<String, Pulse>,
80+
receivers: Vec<String>,
81+
}
82+
83+
impl Conjunction {
84+
fn new(name: &str, receivers: &[String]) -> Self {
85+
Self {
86+
name: name.to_string(),
87+
memory: HashMap::new(),
88+
receivers: receivers.to_owned(),
89+
}
90+
}
91+
92+
fn from(line: &str) -> Self {
93+
let name = line.split("->").nth(0).unwrap().replace('&', "");
94+
let receivers = _parse_after_arrow(line);
95+
Self::new(name.trim(), &receivers)
96+
}
97+
98+
fn add_input(&mut self, new_input: &str) {
99+
self.memory.insert(new_input.to_string(), Pulse::Low);
100+
}
101+
102+
fn add_inputs(&mut self, new_inputs: &HashSet<&str>) {
103+
new_inputs.iter().for_each(|i| self.add_input(i))
104+
}
105+
}
106+
107+
impl Receiver for Conjunction {
108+
fn receive(&mut self, in_pulse: &PulseMsg) -> Option<VecDeque<PulseMsg>> {
109+
self.memory.insert(in_pulse.from.clone(), in_pulse.pulse);
110+
let out_pulse = match self.memory.values().all(|p| p == &Pulse::High) {
111+
true => Pulse::Low,
112+
false => Pulse::High,
113+
};
114+
Option::Some(
115+
self.receivers
116+
.iter()
117+
.map(|r| PulseMsg {
118+
from: self.name.clone(),
119+
to: r.clone(),
120+
pulse: out_pulse,
121+
})
122+
.collect::<VecDeque<_>>(),
123+
)
124+
}
125+
}
126+
127+
#[derive(Debug, Clone)]
128+
struct FlipFlop {
129+
name: String,
130+
state: bool, // true = "on", false = "off"
131+
receivers: Vec<String>,
132+
}
133+
134+
impl FlipFlop {
135+
fn new(name: &str, receivers: &[String]) -> Self {
136+
Self {
137+
name: name.to_string(),
138+
state: false,
139+
receivers: receivers.to_owned(),
140+
}
141+
}
142+
143+
fn from(line: &str) -> Self {
144+
let name = line.split("->").nth(0).unwrap().replace('%', "");
145+
let receivers = _parse_after_arrow(line);
146+
Self::new(name.trim(), &receivers)
147+
}
148+
}
149+
150+
impl Receiver for FlipFlop {
151+
fn receive(&mut self, in_pulse: &PulseMsg) -> Option<VecDeque<PulseMsg>> {
152+
log::trace!(
153+
"FlipFlip {} received {:?} pulse.",
154+
self.name,
155+
in_pulse.pulse
156+
);
157+
if in_pulse.pulse == Pulse::High {
158+
return None;
159+
}
160+
let out_pulse = match self.state {
161+
false => Pulse::High,
162+
true => Pulse::Low,
163+
};
164+
self.state = !self.state;
165+
Option::Some(
166+
self.receivers
167+
.iter()
168+
.map(|r| PulseMsg {
169+
from: self.name.clone(),
170+
to: r.clone(),
171+
pulse: out_pulse,
172+
})
173+
.collect::<VecDeque<_>>(),
174+
)
175+
}
176+
}
177+
178+
#[derive(Debug, Clone)]
179+
struct Output {
180+
name: String,
181+
}
182+
183+
impl Output {
184+
fn new() -> Self {
185+
Self {
186+
name: "output".to_string(),
187+
}
188+
}
189+
}
190+
191+
impl Receiver for Output {
192+
fn receive(&mut self, _: &PulseMsg) -> Option<VecDeque<PulseMsg>> {
193+
Option::None
194+
}
195+
}
196+
197+
#[derive(Debug, Clone)]
198+
enum Module {
199+
B(Broadcast),
200+
C(Conjunction),
201+
F(FlipFlop),
202+
O(Output),
203+
}
204+
205+
fn _parse_input_line(line: &str) -> Result<Module, PuzzleErr> {
206+
if line.starts_with("broadcaster") {
207+
Ok(Module::B(Broadcast::from(line)))
208+
} else if line.starts_with('%') {
209+
Ok(Module::F(FlipFlop::from(line)))
210+
} else if line.starts_with('&') {
211+
Ok(Module::C(Conjunction::from(line)))
212+
} else {
213+
Err(PuzzleErr::ParseInputError)
214+
}
215+
}
216+
217+
fn parse_input(input: &str) -> Result<HashMap<String, Module>, PuzzleErr> {
218+
// Parse the individual modules defined on each line.
219+
let mut modules = input
220+
.trim()
221+
.lines()
222+
.map(_parse_input_line)
223+
.collect::<Result<Vec<_>, PuzzleErr>>()?;
224+
225+
// Manually add the `Output` module.
226+
modules.push(Module::O(Output::new()));
227+
228+
// Convert the vector into a dictionary.
229+
let mut mapping = modules
230+
.into_iter()
231+
.map(|m| {
232+
let x: (String, Module) = match m {
233+
Module::B(ref a) => (a.name.clone(), m),
234+
Module::C(ref a) => (a.name.clone(), m),
235+
Module::F(ref a) => (a.name.clone(), m),
236+
Module::O(ref a) => (a.name.clone(), m),
237+
};
238+
x
239+
})
240+
.collect::<HashMap<String, Module>>();
241+
242+
// Get inputs for Conjugation modules.
243+
let mut receiver_connections = HashMap::<String, HashSet<&str>>::new();
244+
let duplicate_mapping = mapping.clone();
245+
for (name, module) in duplicate_mapping.iter() {
246+
let recievers = match module {
247+
Module::B(b) => b.receivers.clone(),
248+
Module::F(f) => f.receivers.clone(),
249+
Module::C(c) => c.receivers.clone(),
250+
_ => Vec::new(),
251+
};
252+
recievers.iter().for_each(|r| {
253+
receiver_connections
254+
.entry(r.to_string())
255+
.and_modify(|s| {
256+
s.insert(name.as_str());
257+
})
258+
.or_insert(HashSet::from_iter([name.as_str()]));
259+
});
260+
}
261+
for (receiver, input_mods) in receiver_connections.iter() {
262+
if let Some(Module::C(c)) = mapping.get_mut(receiver) {
263+
c.add_inputs(input_mods)
264+
}
265+
}
266+
Ok(mapping)
267+
}
268+
269+
struct PulseCounter {
270+
low: u32,
271+
high: u32,
272+
}
273+
274+
impl PulseCounter {
275+
fn new() -> Self {
276+
Self { low: 0, high: 0 }
277+
}
278+
279+
fn track(&mut self, pulse_msg: &PulseMsg) {
280+
match pulse_msg.pulse {
281+
Pulse::Low => self.low += 1,
282+
Pulse::High => self.high += 1,
283+
}
284+
}
285+
}
286+
287+
pub fn puzzle_1(input: &str, n_button_presses: u32) -> Result<u32, PuzzleErr> {
288+
let mut modules = parse_input(input)?;
289+
modules
290+
.iter()
291+
.for_each(|(name, module)| log::info!("{} -> {:?}", name, module));
292+
293+
let mut pulse_counter = PulseCounter::new();
294+
for _ in 0..n_button_presses {
295+
let mut pulses = VecDeque::from_iter([PulseMsg {
296+
from: "button".to_string(),
297+
to: "broadcaster".to_string(),
298+
pulse: Pulse::Low,
299+
}]);
300+
pulse_counter.low += 1;
301+
while !pulses.is_empty() {
302+
let pulse = pulses.pop_front().unwrap();
303+
log::debug!("PULSE: {:?}", pulse);
304+
if let Some(response) = match modules.get_mut(&pulse.to) {
305+
Some(Module::B(b)) => b.receive(&pulse),
306+
Some(Module::C(c)) => c.receive(&pulse),
307+
Some(Module::F(f)) => f.receive(&pulse),
308+
Some(Module::O(o)) => o.receive(&pulse),
309+
None => None,
310+
} {
311+
log::debug!("Received {} responses.", response.len());
312+
response.into_iter().for_each(|r| {
313+
log::trace!("RESPONSE: {:?}", r);
314+
pulse_counter.track(&r);
315+
pulses.push_back(r);
316+
});
317+
} else {
318+
log::debug!("No responses.")
319+
}
320+
}
321+
}
322+
log::info!(
323+
"Final counts: {} low, {} high",
324+
pulse_counter.low,
325+
pulse_counter.high
326+
);
327+
Ok(pulse_counter.low * pulse_counter.high)
328+
}
329+
330+
pub fn main(data_dir: &str) {
331+
println!("Day 20: Pulse Propagation");
332+
let data = load(data_dir, 20, None);
333+
334+
// Puzzle 1.
335+
let _ = env_logger::try_init();
336+
let answer_1 = puzzle_1(&data, 1000);
337+
match answer_1 {
338+
Ok(x) => println!(" Puzzle 1: {}", x),
339+
Err(e) => panic!("No solution to puzzle 1: {}.", e),
340+
}
341+
assert_eq!(answer_1, Ok(944750144));
342+
343+
// Puzzle 2.
344+
// let answer_2 = puzzle_2(&data);
345+
// match answer_2 {
346+
// Ok(x) => println!(" Puzzle 2: {}", x),
347+
// Err(e) => panic!("No solution to puzzle 2: {}", e),
348+
// }
349+
// assert_eq!(answer_2, Ok(30449))
350+
}

src/solutions/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ pub mod day16;
1717
pub mod day17;
1818
pub mod day18;
1919
pub mod day19;
20+
pub mod day20;

0 commit comments

Comments
 (0)