Skip to main content

advent_of_code/year2016/
day21.rs

1use crate::common::permutation::all_permutations;
2use crate::input::Input;
3
4pub fn solve(input: &Input) -> Result<String, String> {
5    let mut password = [b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h'];
6    if input.is_part_one() {
7        scramble(input.text, &mut password)?;
8        Ok(password.iter().map(|&b| b as char).collect::<String>())
9    } else {
10        // fbgdceah
11        let desired = [b'f', b'b', b'g', b'd', b'c', b'e', b'a', b'h'];
12        let mut answer = None;
13        all_permutations(&mut password, &mut |permutation| {
14            let mut copy = [0, 0, 0, 0, 0, 0, 0, 0];
15            copy.copy_from_slice(permutation);
16            scramble(input.text, &mut copy)?;
17            if copy == desired {
18                answer = Some(permutation.iter().map(|&b| b as char).collect::<String>());
19            }
20            Ok(())
21        })?;
22
23        answer.ok_or_else(|| "No solution found".to_string())
24    }
25}
26
27fn scramble(input: &str, password: &mut [u8]) -> Result<(), String> {
28    let error_mapper = |_| "Invalid input";
29    for line in input.lines() {
30        let words = line.split(' ').collect::<Vec<_>>();
31        match words[0] {
32            "swap" => {
33                if words[1] == "position" {
34                    let x = words[2].parse::<usize>().map_err(error_mapper)?;
35                    let y = words[5].parse::<usize>().map_err(error_mapper)?;
36                    password.swap(x, y);
37                } else {
38                    // Swap letters
39                    let x = words[2].as_bytes()[0];
40                    let y = words[5].as_bytes()[0];
41                    for c in password.iter_mut() {
42                        let orig = *c;
43                        *c = if orig == x {
44                            y
45                        } else if orig == y {
46                            x
47                        } else {
48                            orig
49                        };
50                    }
51                }
52            }
53            "rotate" => {
54                let rotation = if words[1] == "based" {
55                    let letter = words[6].as_bytes()[0];
56                    if let Some((idx, _)) =
57                        password.iter().enumerate().find(|&(_idx, &c)| c == letter)
58                    {
59                        ((1 + idx + usize::from(idx >= 4)) % password.len()) as i32
60                    } else {
61                        return Err(format!(
62                            "Unable to find letter for rotation: '{}'",
63                            letter as char
64                        ));
65                    }
66                } else {
67                    words[2].parse::<i32>().map_err(error_mapper)?
68                        * if words[1] == "left" { -1 } else { 1 }
69                };
70
71                if rotation < 0 {
72                    password.rotate_left((-rotation) as usize);
73                } else {
74                    password.rotate_right(rotation as usize);
75                }
76            }
77            "reverse" => {
78                let x = words[2].parse::<usize>().map_err(error_mapper)?;
79                let y = words[4].parse::<usize>().map_err(error_mapper)?;
80                password[x..(y + 1)].reverse();
81            }
82            "move" => {
83                let x = words[2].parse::<usize>().map_err(error_mapper)?;
84                let y = words[5].parse::<usize>().map_err(error_mapper)?;
85                let mut buffer: Vec<u8> = password.to_vec();
86                let removed_letter = buffer.remove(x);
87                buffer.insert(y, removed_letter);
88                password.clone_from_slice(&buffer);
89            }
90            _ => {
91                return Err("Invalid input".to_string());
92            }
93        }
94    }
95    Ok(())
96}
97
98#[test]
99pub fn tests() {
100    let real_input = include_str!("day21_input.txt");
101    test_part_one!(real_input => "gcedfahb".to_string());
102    test_part_two!(real_input => "hegbdcfa".to_string());
103}