Day 21: Keypad Conundrum

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • CameronDevOPM
    link
    fedilink
    arrow-up
    1
    ·
    10 hours ago

    Rust

    Like many it seems, this one also broke my brain. Its basically the same as Day 19, but something about it mentally broke me.

    #[cfg(test)]
    mod tests {
        use std::collections::HashMap;
    
        static NUMPAD: [[char; 3]; 4] = [
            ['7', '8', '9'],
            ['4', '5', '6'],
            ['1', '2', '3'],
            ['X', '0', 'A'],
        ];
        static DPAD: [[char; 3]; 2] = [['X', '^', 'A'], ['<', 'v', '>']];
    
        fn valid_path(pad: &[[char; 3]], start: (isize, isize), path: &str) -> bool {
            let mut pos = (start.0, start.1);
            for c in path.chars() {
                match c {
                    '^' => pos.0 -= 1,
                    'v' => pos.0 += 1,
                    '<' => pos.1 -= 1,
                    '>' => pos.1 += 1,
                    'A' => {}
                    _ => unreachable!(),
                };
    
                if pad[pos.0 as usize][pos.1 as usize] == 'X' {
                    return false;
                }
            }
            true
        }
    
        fn move_pad(pad: &[[char; 3]], start: char, end: char) -> Vec<String> {
            let mut start_coord = (0, 0);
            let mut end_coord = (0, 0);
            for i in 0..pad.len() {
                for j in 0..3 {
                    if pad[i][j] == end {
                        end_coord = (i as isize, j as isize);
                    }
                    if pad[i][j] == start {
                        start_coord = (i as isize, j as isize);
                    }
                }
            }
    
            let delta_i = end_coord.0 - start_coord.0;
            let vert = match delta_i {
                -3 => "^^^",
                -2 => "^^",
                -1 => "^",
                0 => "",
                1 => "v",
                2 => "vv",
                3 => "vvv",
                _ => unreachable!(),
            };
    
            let delta_j = end_coord.1 - start_coord.1;
            let horz = match delta_j {
                -2 => "<<",
                -1 => "<",
                0 => "",
                1 => ">",
                2 => ">>",
                _ => unreachable!(),
            };
    
            let vert_path = horz.to_string() + vert + "A";
            let horz_path = vert.to_string() + horz + "A";
    
            if !valid_path(pad, start_coord, &vert_path) {
                return vec![horz_path];
            }
            if !valid_path(pad, start_coord, &horz_path) {
                return vec![vert_path];
            }
            vec![vert_path, horz_path]
        }
    
        fn dpad_seq_len(p0: &str, depth: usize, cache: &mut HashMap<(String, usize), usize>) -> usize {
            if depth == 0 {
                return p0.len();
            }
            if let Some(cost) = cache.get(&(p0.to_string(), depth)) {
                return *cost;
            }
    
            let mut first = 'A';
            let mut length = 0;
            for second in p0.chars() {
                let moves = move_pad(&DPAD, first, second);
                let mut min = usize::MAX;
                for m in moves {
                    let l = dpad_seq_len(&m, depth - 1, cache);
                    if l < min {
                        min = l;
                    }
                }
                length += min;
                first = second;
            }
            cache.insert((p0.to_string(), depth), length);
            length
        }
    
        fn numpad_seq_len(
            p0: &str,
            depth: usize,
            cache: &mut HashMap<(String, usize), usize>,
        ) -> usize {
            let mut first = 'A';
    
            let mut length = 0;
            for second in p0.chars() {
                let moves = move_pad(&NUMPAD, first, second);
                let mut min = usize::MAX;
                for m in moves {
                    let l = dpad_seq_len(&m, depth, cache);
                    if l < min {
                        min = l;
                    }
                }
                length += min;
                first = second;
            }
    
            length
        }
    
        #[test]
        fn test_numpad2dpad() {
            let mut cache = HashMap::new();
            assert_eq!(68, numpad_seq_len("029A", 2, &mut cache));
            assert_eq!(60, numpad_seq_len("980A", 2, &mut cache));
            assert_eq!(68, numpad_seq_len("179A", 2, &mut cache));
            assert_eq!(64, numpad_seq_len("456A", 2, &mut cache));
            assert_eq!(64, numpad_seq_len("379A", 2, &mut cache));
        }
    
        #[test]
        fn day21_part1_test() {
            let mut cache = HashMap::new();
            let input = std::fs::read_to_string("src/input/day_21.txt").unwrap();
            let codes = input.split('\n').collect::<Vec<&str>>();
    
            let mut total = 0;
            for code in codes {
                let min_length = numpad_seq_len(code, 2, &mut cache);
                println!("{code}: {min_length}");
                total += min_length * code[0..3].parse::<usize>().unwrap();
            }
            println!("{}", total);
        }
    
        #[test]
        fn day21_part2_test() {
            let mut cache = HashMap::new();
            let input = std::fs::read_to_string("src/input/day_21.txt").unwrap();
            let codes = input.split('\n').collect::<Vec<&str>>();
    
            let mut total = 0;
            for code in codes {
                let min_length = numpad_seq_len(code, 25, &mut cache);
                println!("{code}: {min_length}");
                total += min_length * code[0..3].parse::<usize>().unwrap();
            }
            println!("{}", total);
        }
    }