#![allow(non_upper_case_globals)] #![allow(unused_imports)] use md5::Digest; use crate::prelude::*; pub type I = &'static str; pub type O = String; pub type O2 = usize; const _cand: [(isize, isize, char); 4] = [(-1, 0, 'U'), (1, 0, 'D'), (0, -1, 'L'), (0, 1, 'R')]; fn _open_doors ((y, x): (usize, usize), digest: Digest) -> Vec<(usize, usize, char)> { let repr = format!("{:x}", digest); let access: [char; 4] = repr.chars().take(4).collect_vec().try_into().unwrap(); _cand.iter() .enumerate() .filter_map(|(i, &(dy, dx, n))| match (y.checked_add_signed(dy), x.checked_add_signed(dx)) { (Some(y), Some(x)) => if y <= 3 && x <= 3 { Some((y, x, n, i)) } else { None }, _ => None }) .filter(|(_, _, _, idx)| ('b'..='f').contains(&access[*idx])) .map(|(a, b, c, _)| (a, b, c)) .collect() } fn _digest (pass: &'static str, i: &str) -> Digest { let full = format!("{pass}{i}"); md5::compute(full) } fn _dfs (cur: (usize, usize), path: &mut String, pass: &'static str) -> bool { if cur == (3, 3) { return true; } else { let digest = _digest(pass, &path); let doors = _open_doors(cur, digest); for (ny, nx, p) in doors { path.push(p); if _dfs((ny, nx), path, pass) { return true; } else { path.pop(); } } return false; } } fn _dfs_max (cur: (usize, usize), path: &mut String, pass: &'static str, max: &mut usize) { if cur == (3, 3) { *max = (*max).max(path.len()); } else { let digest = _digest(pass, &path); let doors = _open_doors(cur, digest); for (ny, nx, p) in doors { path.push(p); _dfs_max((ny, nx), path, pass, max); path.pop(); } } } fn _silver (data: I) -> O { let mut path = String::new(); _dfs((0, 0), &mut path, data); path } fn _gold (data: I) -> O2 { let mut path = String::new(); let mut max = 0; _dfs_max((0, 0), &mut path, data, &mut max); max } #[cfg(test)] mod test { use super::*; const pass: &'static str = "edjrjqaa"; #[test] fn silver () { let ans = _silver(pass); assert_eq!(ans, s!("DUDRDLRRRD")) } #[test] fn gold () { let ans = _gold(pass); assert_eq!(ans, 502) } }