1
0
Fork 0

day(20): add solution

Signed-off-by: Matej Focko <me@mfocko.xyz>
This commit is contained in:
Matej Focko 2024-07-06 18:18:33 +02:00
parent 3c813855e3
commit 7bca4427f7
Signed by: mfocko
SSH key fingerprint: SHA256:5YXD7WbPuK60gxnG6DjAwJiS9+swoWj33/HFu8g8JVo
2 changed files with 232 additions and 0 deletions

5
samples/day20.txt Normal file
View file

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

227
src/bin/day20.rs Normal file
View file

@ -0,0 +1,227 @@
use std::{
collections::{HashMap, HashSet, VecDeque},
hash::{DefaultHasher, Hash, Hasher},
str::FromStr,
};
use aoc_2023::*;
type Output1 = usize;
type Output2 = Output1;
lazy_static! {
static ref GATE_REGEX: Regex = Regex::new(r"([%&]?)(.*) -> (.*)").unwrap();
}
fn gate_hash(name: &str) -> u64 {
let mut h = DefaultHasher::new();
name.hash(&mut h);
h.finish()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum GateState {
Repeater,
FlipFlop(bool),
Nand(u32),
}
impl FromStr for GateState {
type Err = &'static str;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"" => Ok(GateState::Repeater),
"%" => Ok(GateState::FlipFlop(false)),
"&" => Ok(GateState::Nand(0)),
_ => Err("Invalid kind"),
}
}
}
impl GateState {
fn exec(
&mut self,
watch: Option<u64>,
src: u64,
dest: &Gate,
p: bool,
) -> Option<(Option<usize>, bool)> {
match self {
GateState::Repeater => Some((None, p)),
GateState::FlipFlop(b) => {
if p {
None
} else {
*b = !*b;
Some((None, *b))
}
}
GateState::Nand(v) => {
let idx = dest
.inputs
.iter()
.position(|i| gate_hash(i) == src)
.unwrap();
let mut high = None;
if p {
if watch.is_some_and(|h| dest.hash == h) {
high = Some(idx);
}
*v |= 1 << idx;
} else {
*v &= !(1 << idx);
}
Some((high, *v != (1 << dest.inputs.len()) - 1))
}
}
}
}
#[derive(Debug, Clone, Default)]
struct Gate {
inputs: Vec<String>,
outputs: Vec<String>,
hash: u64,
}
impl Gate {
fn new(name: &str) -> Gate {
Gate {
inputs: vec![],
outputs: vec![],
hash: gate_hash(name),
}
}
}
struct Day20 {
gates: HashMap<String, Gate>,
initial_state: HashMap<String, GateState>,
}
impl Day20 {
fn push(
&self,
state: &mut HashMap<String, GateState>,
watch: Option<u64>,
) -> (usize, usize, Option<usize>) {
let mut q = VecDeque::new();
let mut counters = vec![0, 0];
let mut high_idx = None;
q.push_back(("button".to_owned(), "broadcaster".to_owned(), false));
while let Some((src, dest, pulse)) = q.pop_front() {
counters[pulse as usize] += 1;
let (Some(g), Some(gs)) = (self.gates.get(&dest), state.get_mut(&dest)) else {
continue;
};
if let Some((found_idx, next_pulse)) = gs.exec(watch, gate_hash(&src), g, pulse) {
if found_idx.is_some() {
high_idx = found_idx;
}
for next_gate in &g.outputs {
q.push_back((dest.clone(), next_gate.clone(), next_pulse));
}
}
}
(counters[0], counters[1], high_idx)
}
}
impl Solution<Output1, Output2> for Day20 {
fn new<P: AsRef<Path>>(pathname: P) -> Self {
let lines: Vec<String> = file_to_lines(pathname);
let mut gates: HashMap<String, Gate> = HashMap::new();
let mut initial_state: HashMap<String, GateState> = HashMap::new();
for (kind, name, outputs) in lines.into_iter().map(|g| {
let (_, [kind, name, outputs]) = GATE_REGEX.captures(&g).unwrap().extract();
(
kind.to_owned(),
name.to_owned(),
outputs.split(", ").map(str::to_owned).collect_vec(),
)
}) {
for o in &outputs {
gates
.entry(o.clone())
.or_default()
.inputs
.push(name.clone());
}
gates
.entry(name.clone())
.or_insert(Gate::new(&name))
.outputs = outputs;
initial_state.insert(name, kind.parse().unwrap());
}
assert_eq!(
gates
.keys()
.map(|k| gate_hash(k))
.collect::<HashSet<u64>>()
.len(),
gates.len(),
"no collisions in hash"
);
Self {
gates,
initial_state,
}
}
fn part_1(&mut self) -> Output1 {
let mut state = self.initial_state.clone();
let (mut l, mut h) = (0, 0);
for _ in 0..1000 {
let (l_iter, h_iter, _) = self.push(&mut state, None);
l += l_iter;
h += h_iter;
}
l * h
}
fn part_2(&mut self) -> Output2 {
let rx_parent = &self.gates["rx"].inputs[0];
let rx_parent_hash = Some(gate_hash(&rx_parent));
let mut cycles = vec![0_usize; self.gates[rx_parent].inputs.len()];
let mut found = 0;
let mut state = self.initial_state.clone();
for i in 1.. {
if found >= cycles.len() {
break;
}
if let (_, _, Some(idx)) = self.push(&mut state, rx_parent_hash) {
if cycles[idx] == 0 {
cycles[idx] = i;
found += 1;
}
}
}
cycles.into_iter().product1().unwrap()
}
}
fn main() -> Result<()> {
Day20::main()
}
test_sample!(day_20, Day20, 11687500, 0);