diff options
author | Prefetch | 2023-02-25 11:41:27 +0100 |
---|---|---|
committer | Prefetch | 2023-02-25 11:41:27 +0100 |
commit | 3b877bf4cc667eb8bcc787d145203600a4dba2d2 (patch) | |
tree | c1d247def29fcb58ae28e4ae4e4d73d1b4e1b27f /d17/src |
Initial commit
Diffstat (limited to 'd17/src')
-rw-r--r-- | d17/src/main.rs | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/d17/src/main.rs b/d17/src/main.rs new file mode 100644 index 0000000..bacc9f4 --- /dev/null +++ b/d17/src/main.rs @@ -0,0 +1,109 @@ +use std::collections::VecDeque; + +use md5; + +fn solve_puzzle(passwd: &str) -> Vec<String> { + let mut paths = Vec::new(); + + // Breadth-first search of all possible paths + let start = (String::new(), (0, 0)); + let mut queue = VecDeque::from([start]); + loop { + let pp = queue.pop_front(); + // All paths have finished or got stuck at locked doors + if pp.is_none() { + break; + } + // Did we successfully reach the bottom-right corner? + let (path, pos) = pp.unwrap(); + if pos == (3, 3) { + paths.push(path.clone()); + continue; + } + + // Doors open based on MD5 hash of `passw' + `path' + let data = format!("{}{}", passwd, path); + let hash: Vec<char> = format!("{:x}", md5::compute(data)).chars().collect(); + + // Which doors are open, i.e. what are the valid next moves? + let mut options = Vec::new(); + let unlock = ['b', 'c', 'd', 'e', 'f']; + if unlock.contains(&hash[0]) && pos.1 > 0 { + options.push((format!("{}U", path), (pos.0, pos.1 - 1))); + } + if unlock.contains(&hash[1]) && pos.1 < 3 { + options.push((format!("{}D", path), (pos.0, pos.1 + 1))); + } + if unlock.contains(&hash[2]) && pos.0 > 0 { + options.push((format!("{}L", path), (pos.0 - 1, pos.1))); + } + if unlock.contains(&hash[3]) && pos.0 < 3 { + options.push((format!("{}R", path), (pos.0 + 1, pos.1))); + } + + // This is a breadth-first search, but because `path' is encoded + // into the state, we don't need to check that nodes are unexplored + for (npath, npos) in options { + queue.push_back((npath, npos)); + } + } + + paths +} + +fn main() { + // My personal puzzle input + let input = "edjrjqaa"; + + let paths = solve_puzzle(input); + + // Part 1 gives "DUDRDLRRRD" for me + println!("Part 1 solution: {}", paths.first().unwrap()); + + // Part 2 gives 502 for me + println!("Part 2 solution: {}", paths.last().unwrap().len()); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn part1_example1() { + let passwd = "ihgpwlah"; + assert_eq!(solve_puzzle(passwd).first().unwrap(), "DDRRRD"); + } + + #[test] + fn part2_example1() { + let passwd = "ihgpwlah"; + assert_eq!(solve_puzzle(passwd).last().unwrap().len(), 370); + } + + #[test] + fn part1_example2() { + let passwd = "kglvqrro"; + assert_eq!(solve_puzzle(passwd).first().unwrap(), "DDUDRLRRUDRD"); + } + + #[test] + fn part2_example2() { + let passwd = "kglvqrro"; + assert_eq!(solve_puzzle(passwd).last().unwrap().len(), 492); + } + + #[test] + fn part1_example3() { + let passwd = "ulqzkmiv"; + assert_eq!( + solve_puzzle(passwd).first().unwrap(), + "DRURDRUDDLLDLUURRDULRLDUUDDDRR" + ); + } + + #[test] + fn part2_example3() { + let passwd = "ulqzkmiv"; + assert_eq!(solve_puzzle(passwd).last().unwrap().len(), 830); + } +} |