2023 - Day 20 [Part 2 works with manual help]

This commit is contained in:
Dreaded_X 2023-12-20 15:26:09 +01:00
parent bd741d38d4
commit fbceb13123
Signed by: Dreaded_X
GPG Key ID: 5A0CBFE3C3377FAA
4 changed files with 320 additions and 0 deletions

58
2023/input/20/input Normal file
View File

@ -0,0 +1,58 @@
%rq -> ch, sj
%nf -> sm, rg
%pc -> rz, zp
%xt -> bc
%nt -> kq, sj
%hc -> kb, zp
%rd -> lk
%ml -> pp, xt
%sq -> kl, sj
%jg -> fl, rg
&xl -> df
%kl -> mb, sj
%nd -> rg, jg
&rg -> cs, zb, cp, vz, gp
%mf -> zp
%rz -> zp, fr
%kk -> rg, bj
%nb -> qj
%pr -> pp
&zp -> vl, lk, rd, kb, xl
%fl -> nf, rg
%tb -> pk, pp
%bh -> pp, pr
%nh -> sj, rq
%lk -> hc
%cp -> kk
&ln -> df
&xp -> df
%bc -> nb, pp
%lj -> rg
%vz -> nd
%vl -> lv, zp
&gp -> df
%hd -> pp, bq
%fq -> pp, bh
%pk -> fq, pp
%cs -> zb, rg
%sn -> fd
%kq -> sj, qq
%zb -> vz
%lv -> zp, rd
%qj -> pp, hd
%fd -> nt
&df -> rx
broadcaster -> vl, cs, cn, ml
%bq -> tb
%kb -> pc
%cn -> sn, sj
%qq -> sq
%mb -> sj, nh
%jd -> zp, mf
&sj -> xp, qq, cn, fd, sn
&pp -> ln, ml, xt, bq, nb
%sm -> rg, cp
%ch -> sj
%bj -> lj, rg
%fr -> zp, mr
%mr -> zp, jd

5
2023/input/20/test-1 Normal file
View File

@ -0,0 +1,5 @@
broadcaster -> a, b, c
%a -> b
%b -> c
%c -> inv
&inv -> a

5
2023/input/20/test-2 Normal file
View File

@ -0,0 +1,5 @@
broadcaster -> a
%a -> inv, con
&inv -> b
%b -> con
&con -> output

252
2023/src/bin/day20.rs Normal file
View File

@ -0,0 +1,252 @@
#![feature(test)]
use std::collections::{HashMap, VecDeque};
use anyhow::Result;
use aoc::Solver;
// -- Runners --
fn main() -> Result<()> {
Day::solve()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_test1() -> Result<()> {
Day::test(Day::part1, "test-1", 32000000)
}
#[test]
fn part1_test2() -> Result<()> {
Day::test(Day::part1, "test-2", 11687500)
}
#[test]
fn part1_solution() -> Result<()> {
Day::test(Day::part1, "input", 666795063)
}
#[test]
fn part2_solution() -> Result<()> {
Day::test(Day::part2, "input", 253302889093151)
}
// Benchmarks
extern crate test;
#[bench]
#[ignore]
fn part1_bench(b: &mut test::Bencher) {
Day::benchmark(Day::part1, b)
}
#[bench]
#[ignore]
fn part2_bench(b: &mut test::Bencher) {
Day::benchmark(Day::part2, b)
}
}
#[derive(Debug, Clone)]
enum ModuleType<'a> {
Broadcaster,
FlipFlop(bool),
Conjunction(HashMap<&'a str, bool>),
}
#[derive(Debug, Clone)]
struct Module<'a> {
module_type: ModuleType<'a>,
destinations: Vec<&'a str>,
}
impl<'a> Module<'a> {
fn process(&mut self, source: &'a str, high: bool) -> Option<bool> {
match &mut self.module_type {
ModuleType::Broadcaster => Some(high),
ModuleType::FlipFlop(current) => {
if high {
None
} else {
*current = !*current;
Some(*current)
}
}
ModuleType::Conjunction(inputs) => {
let input = inputs.get_mut(source).unwrap();
*input = high;
Some(!inputs.iter().all(|(_, prev)| *prev))
}
}
}
}
#[derive(Debug, Clone)]
struct Pulse<'a> {
source: &'a str,
destination: &'a str,
high: bool,
}
// -- Solution --
pub struct Day;
impl aoc::Solver for Day {
type Output1 = usize;
type Output2 = usize;
fn day() -> u8 {
20
}
fn part1(input: &str) -> Self::Output1 {
let mut modules: HashMap<_, _> = input
.lines()
.map(|line| {
let (name, destinations) = line.split_once(" -> ").unwrap();
let destinations: Vec<_> = destinations.split(", ").collect();
let (name, module_type) = if name.starts_with('%') {
(name.split_at(1).1, ModuleType::FlipFlop(false))
} else if name.starts_with('&') {
(name.split_at(1).1, ModuleType::Conjunction(HashMap::new()))
} else if name == "broadcaster" {
(name, ModuleType::Broadcaster)
} else {
unreachable!("Invalid input");
};
(
name,
Module {
module_type,
destinations,
},
)
})
.collect();
// TODO: Because of the borrow check we have to create a clone of modules here
for (name, module) in modules.clone() {
for destination in &module.destinations {
if let Some(module) = modules.get_mut(destination) {
if let ModuleType::Conjunction(inputs) = &mut module.module_type {
inputs.insert(name, false);
}
}
}
}
let mut count = (0, 0);
let mut pulses = VecDeque::new();
for _ in 0..1000 {
pulses.push_back(Pulse {
source: "button",
destination: "broadcaster",
high: false,
});
while let Some(pulse) = pulses.pop_front() {
if pulse.high {
count.1 += 1;
} else {
count.0 += 1;
}
if let Some(module) = modules.get_mut(pulse.destination) {
if let Some(high) = module.process(pulse.source, pulse.high) {
for destination in &module.destinations {
pulses.push_back(Pulse {
source: pulse.destination,
destination,
high,
});
}
}
}
}
}
count.0 * count.1
}
fn part2(input: &str) -> Self::Output2 {
let mut modules: HashMap<_, _> = input
.lines()
.map(|line| {
let (name, destinations) = line.split_once(" -> ").unwrap();
let destinations: Vec<_> = destinations.split(", ").collect();
let (name, module_type) = if name.starts_with('%') {
(name.split_at(1).1, ModuleType::FlipFlop(false))
} else if name.starts_with('&') {
(name.split_at(1).1, ModuleType::Conjunction(HashMap::new()))
} else if name == "broadcaster" {
(name, ModuleType::Broadcaster)
} else {
unreachable!("Invalid input");
};
(
name,
Module {
module_type,
destinations,
},
)
})
.collect();
// TODO: Because of the borrow check we have to create a clone of modules here
for (name, module) in modules.clone() {
for destination in &module.destinations {
if let Some(module) = modules.get_mut(destination) {
if let ModuleType::Conjunction(inputs) = &mut module.module_type {
inputs.insert(name, false);
}
}
}
}
let r#final = modules
.iter()
.find(|(_, module)| module.destinations.contains(&"rx"))
.unwrap();
let final_name = r#final.0.to_owned();
let final_module = r#final.1.clone();
println!("{final_name}: {final_module:?}");
let mut pulses = VecDeque::new();
for i in 0..10000 {
pulses.push_back(Pulse {
source: "button",
destination: "broadcaster",
high: false,
});
while let Some(pulse) = pulses.pop_front() {
if let Some(module) = modules.get_mut(pulse.destination) {
if pulse.destination == final_name && pulse.high {
// TODO: Instead of printing this, keep track of it
// Once all inputs have received a pulse calculate the LCM and output that
println!("{} high after {} presses", pulse.source, i + 1);
}
if let Some(high) = module.process(pulse.source, pulse.high) {
for destination in &module.destinations {
pulses.push_back(Pulse {
source: pulse.destination,
destination,
high,
});
}
}
}
}
}
0
}
}