diff --git a/src/day20/1.rs b/src/day20/1.rs index b51073e..6cf18aa 100644 --- a/src/day20/1.rs +++ b/src/day20/1.rs @@ -1 +1,172 @@ -const INPUTS: [&'static str; 2] = [include_str!("./sample.txt"), include_str!("./input.txt")]; +#![feature(slice_split_once)] +#![feature(byte_slice_trim_ascii)] +#![feature(test)] + +use std::collections::{HashMap, VecDeque}; +extern crate test; + +const INPUTS: [&str; 3] = [ + "broadcaster -> a +%a -> inv, con +&inv -> b +%b -> con +&con -> output", + include_str!("./sample.txt"), + include_str!("./input.txt"), +]; + +#[inline] +fn compress(mut c: &[u8]) -> usize { + if c.len() > 3 { + c = &c[..3]; + } + let mut out = 0; + let mut i = c.len() as i32 - 1; + + let mut pow = 1; + while i >= 0 { + out += pow * (c[i as usize] - b'a') as usize; + + pow *= 26; + + i -= 1; + } + + out +} + +fn process(data: &str) -> i64 { + let data = data.as_bytes(); + + let mut map = vec![]; + + let mut conjunction_inputs = HashMap::new(); + + for line in data.split(|&x| x == b'\n') { + if line.is_empty() { + continue; + } + let (src, remain) = line.split_once(|&x| x == b' ').unwrap(); + + let (stype, label) = match src[0] { + b'%' => (src[0], compress(&src[1..])), + b'&' => (src[0], compress(&src[1..])), + b'b' => (0, compress(src)), + + _ => unreachable!(), + }; + + let remain = &remain[3..]; + + let dst: Vec = remain + .split(|&x| x == b',') + .map(|x| x.trim_ascii()) + .map(compress) + .collect(); + + if map.len() <= label { + map.extend(std::iter::repeat((0, vec![])).take(label - map.len() + 1)) + } + + map[label] = (stype, dst); + } + + for (k, (ntype, dst)) in map.iter().enumerate() { + let ntype = *ntype; + if ntype != b'&' && ntype != b'%' { + continue; + } + for &dst in dst { + if let Some(&(dtype, _)) = map.get(dst) { + if dtype == b'&' { + conjunction_inputs + .entry(dst) + .or_insert_with(Vec::new) + .push(k); + } + } + } + } + + let mut button_state = vec![false; map.len()]; + + let mut low = 0; + let mut high = 0; + + for _ in 0..1000 { + let mut q = VecDeque::new(); // Button pressed, send signal to broadcaster + + let (_, broadcast_dst) = &map[compress("broadcaster".as_bytes())]; //map.get(&compress("broadcaster".as_bytes())).unwrap(); + // + low += 1; + + for &dst in broadcast_dst { + q.push_back((dst, 0)); + } + + while let Some((node, pulse)) = q.pop_front() { + if pulse == 0 { + low += 1; + } else { + high += 1; + } + + let (ntype, ndst) = if let Some(o) = map.get(node) { + o + } else { + continue; + }; + + match *ntype { + b'%' => { + if pulse == 1 { + continue; + } + + if let Some(state) = button_state.get_mut(node) { + *state = !*state; + for &dst in ndst { + q.push_back((dst, if *state { 1 } else { 0 })); + } + } else { + unreachable!() + } + } + b'&' => { + // Update state + + let new_state = conjunction_inputs + .get(&node) + .unwrap() + .iter() + .all(|&x| *button_state.get(x).unwrap()); + + for &dst in ndst { + q.push_back((dst, !new_state as i64)); + } + + *button_state.get_mut(node).unwrap() = !new_state; + } + + 0 => (), + _ => unreachable!(), + } + } + } + + low * high +} + +fn main() { + for input in INPUTS.iter().skip(2) { + println!("answer = {}", process(input)); + } +} + +#[bench] +fn part1(b: &mut test::Bencher) { + b.iter(|| { + let v = process(INPUTS[INPUTS.len() - 1]); + test::black_box(v); + }); +} diff --git a/src/day20/2.rs b/src/day20/2.rs index b51073e..ff5909e 100644 --- a/src/day20/2.rs +++ b/src/day20/2.rs @@ -1 +1,196 @@ -const INPUTS: [&'static str; 2] = [include_str!("./sample.txt"), include_str!("./input.txt")]; +#![feature(slice_split_once)] +#![feature(byte_slice_trim_ascii)] +#![feature(test)] + +use std::collections::{HashMap, VecDeque}; +extern crate test; + +const INPUTS: [&str; 1] = [include_str!("./input.txt")]; + +#[inline] +fn compress(mut c: &[u8]) -> usize { + if c.len() > 3 { + c = &c[..3]; + } + let mut out = 0; + let mut i = c.len() as i32 - 1; + + let mut pow = 1; + while i >= 0 { + out += pow * (c[i as usize] - b'a') as usize; + + pow *= 26; + + i -= 1; + } + + out +} + +fn process(data: &str) -> usize { + let mut map = HashMap::new(); + let mut conjunction_inputs = HashMap::new(); + + for line in data.lines() { + if line.is_empty() { + continue; + } + let (src, remain) = line.split_once(' ').unwrap(); + + let src: Vec = src.chars().collect(); + + let (stype, label) = match src[0] { + '%' => (src[0], (&src[1..])), + '&' => (src[0], &src[1..]), + 'b' => (' ', src.as_slice()), + + _ => unreachable!(), + }; + + let remain = &remain[3..]; + + let dst: Vec = remain + .split(',') + .map(|x| x.trim()) + .map(|x| x.to_string()) + .collect(); + + let label: String = label.iter().collect(); + + map.insert(label, (stype, dst)); + } + + for (k, (ntype, dst)) in map.iter() { + let ntype = *ntype; + if ntype != '&' && ntype != '%' { + continue; + } + for dst in dst { + if let Some(&(dtype, _)) = map.get(&dst.to_string()) { + if dtype == '&' { + conjunction_inputs + .entry(dst.to_string()) + .or_insert_with(Vec::new) + .push(k.to_string()); + } + } + } + } + + let mut button_state: HashMap = HashMap::new(); + + for k in map.keys() { + button_state.insert(k.clone(), false); + } + + let mut button_presses = vec![]; + let mut button_press = 0; + + loop { + button_press += 1; + cycle( + button_press, + &mut button_state, + &mut map, + &mut conjunction_inputs, + &mut button_presses, + ); + + if button_presses.len() == conjunction_inputs.get("zh").map_or(0, |x| x.len()) { + break; + } + } + + lcm(&button_presses) +} + +fn lcm(a: &[usize]) -> usize { + let mut answer = a[0]; + + for &num in a.iter().skip(1) { + answer = (num * answer) / (gcd(num, answer)); + } + + answer +} + +#[inline] +const fn gcd(a: usize, b: usize) -> usize { + if b == 0 { + return a; + } + gcd(b, a % b) +} + +fn cycle( + button_press: usize, + state: &mut HashMap, + map: &mut HashMap)>, + conjunction_inputs: &mut HashMap>, + button_presses: &mut Vec, +) { + let mut q = VecDeque::new(); + + let (_, broadcast_dst) = map.get(&"broadcaster".to_string()).unwrap(); + for dst in broadcast_dst { + q.push_back((dst, false)); + } + + while let Some((dst, pulse)) = q.pop_front() { + let (dtype, dnext) = match map.get(dst) { + Some(v) => v, + None => continue, + }; + + if pulse && dst == "zh" { + button_presses.push(button_press); + } + + match dtype { + '%' => { + if pulse { + continue; + } + + if let Some(state) = state.get_mut(dst) { + *state = !*state; + + for next in dnext.iter() { + q.push_back((next, *state)); + } + } else { + unreachable!() + } + } + + '&' => { + let new_state = conjunction_inputs + .get(dst) + .unwrap() + .iter() + .all(|x| *state.get(x).unwrap()); + + for next in dnext { + q.push_back((next, !new_state)); + } + + *state.get_mut(dst).unwrap() = !new_state; + } + _ => unreachable!(), + } + } +} + +fn main() { + for input in INPUTS.iter() { + println!("answer = {}", process(input)); + } +} + +#[bench] +fn part1(b: &mut test::Bencher) { + b.iter(|| { + let v = process(INPUTS[INPUTS.len() - 1]); + test::black_box(v); + }); +} diff --git a/src/day20/custom.svg b/src/day20/custom.svg new file mode 100644 index 0000000..8667d22 --- /dev/null +++ b/src/day20/custom.svg @@ -0,0 +1,1021 @@ + + + + + + +G + + + +zl + +zl + + + +zp + +zp + + + +zl->zp + + + + + +cl + +cl + + + +zl->cl + + + + + +zp->cl + + + + + +px + +px + + + +zp->px + + + + + +gp + +gp + + + +zp->gp + + + + + +bh + +bh + + + +zp->bh + + + + + +fn + +fn + + + +zp->fn + + + + + +ls + +ls + + + +zp->ls + + + + + +hs + +hs + + + +zp->hs + + + + + +mj + +mj + + + +cl->mj + + + + + +vp + +vp + + + +dj + +dj + + + +vp->dj + + + + + +vr + +vr + + + +vp->vr + + + + + +lq + +lq + + + +dj->lq + + + + + +mb + +mb + + + +dj->mb + + + + + +dc + +dc + + + +dj->dc + + + + + +ns + +ns + + + +dj->ns + + + + + +gz + +gz + + + +dj->gz + + + + + +vr->dj + + + + + +vr->mb + + + + + +cc + +cc + + + +xp + +xp + + + +cc->xp + + + + + +bz + +bz + + + +xp->bz + + + + + +bj + +bj + + + +xp->bj + + + + + +rb + +rb + + + +lq->rb + + + + + +xx + +xx + + + +mb->xx + + + + + +dc->vp + + + + + +dc->dj + + + + + +zh + +zh + + + +ns->zh + + + + + +df + +df + + + +gz->df + + + + + +md + +md + + + +md->zp + + + + + +ts + +ts + + + +md->ts + + + + + +ts->zp + + + + + +fc + +fc + + + +ts->fc + + + + + +fc->zp + + + + + +zx + +zx + + + +px->zx + + + + + +zx->zl + + + + + +zx->zp + + + + + +nx + +nx + + + +gl + +gl + + + +nx->gl + + + + + +br + +br + + + +nx->br + + + + + +pr + +pr + + + +nx->pr + + + + + +xf + +xf + + + +nx->xf + + + + + +vd + +vd + + + +nx->vd + + + + + +gj + +gj + + + +nx->gj + + + + + +kd + +kd + + + +nx->kd + + + + + +gl->pr + + + + + +br->nx + + + + + +br->xf + + + + + +hb + +hb + + + +pr->hb + + + + + +xf->kd + + + + + +vd->zh + + + + + +mx + +mx + + + +gj->mx + + + + + +hq + +hq + + + +kd->hq + + + + + +tf + +tf + + + +tf->dj + + + + + +lt + +lt + + + +tf->lt + + + + + +lt->dj + + + + + +fj + +fj + + + +pc + +pc + + + +fj->pc + + + + + +pc->cc + + + + + +xx->dj + + + + + +nt + +nt + + + +xx->nt + + + + + +mj->zp + + + + + +mj->gp + + + + + +pm + +pm + + + +pm->fj + + + + + +jc + +jc + + + +jc->bz + + + + + +xm + +xm + + + +jc->xm + + + + + +bz->cc + + + + + +bz->fj + + + + + +bz->pc + + + + + +bz->pm + + + + + +bv + +bv + + + +bz->bv + + + + + +dl + +dl + + + +bz->dl + + + + + +jp + +jp + + + +bz->jp + + + + + +xm->bz + + + + + +rx + +rx + + + +zh->rx + + + + + +pz + +pz + + + +pz->nx + + + + + +sr + +sr + + + +pz->sr + + + + + +sr->nx + + + + + +nt->dj + + + + + +nt->lq + + + + + +gp->md + + + + + +hb->nx + + + + + +jl + +jl + + + +hb->jl + + + + + +jl->nx + + + + + +jl->pz + + + + + +rb->dj + + + + + +rb->gz + + + + + +bh->zh + + + + + +fn->px + + + + + +ls->zp + + + + + +ls->hs + + + + + +hs->fn + + + + + +bv->bz + + + + + +nl + +nl + + + +bv->nl + + + + + +dl->zh + + + + + +xc + +xc + + + +jp->xc + + + + + +nl->pm + + + + + +nl->bz + + + + + +hq->nx + + + + + +hq->gj + + + + + +bj->bz + + + + + +bj->jp + + + + + +mx->nx + + + + + +mx->gl + + + + + +xc->jc + + + + + +xc->bz + + + + + +df->dj + + + + + +df->tf + + + + + +broadcaster + +broadcaster + + + +broadcaster->dc + + + + + +broadcaster->br + + + + + +broadcaster->ls + + + + + +broadcaster->bv + + + + + diff --git a/src/day20/input.txt b/src/day20/input.txt new file mode 100644 index 0000000..446b7a4 --- /dev/null +++ b/src/day20/input.txt @@ -0,0 +1,58 @@ +%zl -> zp, cl +%vp -> dj, vr +%cc -> xp +&dj -> lq, mb, dc, ns, gz +%md -> ts, zp +%fc -> zp +%px -> zx +&nx -> gl, br, pr, xf, vd, gj, kd +%tf -> lt, dj +%fj -> pc +%mb -> xx +%cl -> mj +%pm -> fj +%dc -> dj, vp +%jc -> bz, xm +&vd -> zh +%pz -> sr, nx +&ns -> zh +%sr -> nx +%gl -> pr +%xx -> nt, dj +%gp -> md +%hb -> jl, nx +&zh -> rx +%rb -> gz, dj +%xm -> bz +&zp -> px, gp, cl, bh, fn, ls, hs +&bz -> pm, pc, bv, dl, jp, fj, cc +%nl -> bz, pm +&bh -> zh +%hq -> gj, nx +%bv -> bz, nl +%bj -> jp, bz +%gj -> mx +%xp -> bz, bj +%vr -> dj, mb +&dl -> zh +%pr -> hb +%nt -> dj, lq +%mx -> gl, nx +%kd -> hq +%fn -> px +%jp -> xc +%zx -> zl, zp +%br -> nx, xf +%lt -> dj +%df -> dj, tf +%ts -> zp, fc +%jl -> nx, pz +%xc -> jc, bz +%xf -> kd +%lq -> rb +%gz -> df +%pc -> cc +%hs -> fn +broadcaster -> ls, bv, dc, br +%mj -> zp, gp +%ls -> hs, zp diff --git a/src/day20/sample.txt b/src/day20/sample.txt new file mode 100644 index 0000000..9ed10dd --- /dev/null +++ b/src/day20/sample.txt @@ -0,0 +1,5 @@ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a \ No newline at end of file