aoc/year2016/
day21.rs

1//! # Scrambled Letters and Hash
2//!
3//! The forward transformations are a straightforward. The trickiest reverse
4//! implementation is the rotation based on the index of the letter.
5use crate::util::parse::*;
6
7#[derive(Clone, Copy)]
8pub enum Op {
9    SwapPosition(usize, usize),
10    SwapLetter(u8, u8),
11    RotateLeft(usize),
12    RotateRight(usize),
13    RotateLetterLeft(u8),
14    RotateLetterRight(u8),
15    Reverse(usize, usize),
16    Move(usize, usize),
17}
18
19impl Op {
20    fn from(line: &str) -> Op {
21        let tokens: Vec<_> = line.split_ascii_whitespace().collect();
22        let digit = |i: usize| tokens[i].unsigned();
23        let letter = |i: usize| tokens[i].as_bytes()[0];
24
25        match tokens[0] {
26            "reverse" => Op::Reverse(digit(2), digit(4)),
27            "move" => Op::Move(digit(2), digit(5)),
28            _ => match tokens[1] {
29                "position" => Op::SwapPosition(digit(2), digit(5)),
30                "letter" => Op::SwapLetter(letter(2), letter(5)),
31                "left" => Op::RotateLeft(digit(2)),
32                "right" => Op::RotateRight(digit(2)),
33                "based" => Op::RotateLetterRight(letter(6)),
34                _ => unreachable!(),
35            },
36        }
37    }
38
39    fn transform(self, password: &mut Vec<u8>) {
40        let position = |a: u8| password.iter().position(|&b| a == b).unwrap();
41
42        match self {
43            Op::SwapPosition(first, second) => password.swap(first, second),
44            Op::SwapLetter(first, second) => {
45                let first = position(first);
46                let second = position(second);
47                password.swap(first, second);
48            }
49            Op::RotateLeft(first) => password.rotate_left(first),
50            Op::RotateRight(first) => password.rotate_right(first),
51            // This is the trickiest transformation to invert.
52            // Tests each possible starting index to check if it matches the current index.
53            Op::RotateLetterLeft(first) => {
54                let first = position(first);
55                for i in 0..password.len() {
56                    let second = if i >= 4 { 2 } else { 1 };
57                    let third = (2 * i + second) % password.len();
58                    if first == third {
59                        if i < first {
60                            password.rotate_left(first - i);
61                        } else {
62                            password.rotate_right(i - first);
63                        }
64                    }
65                }
66            }
67            Op::RotateLetterRight(first) => {
68                let first = position(first);
69                let second = if first >= 4 { 2 } else { 1 };
70                let third = (first + second) % password.len();
71                password.rotate_right(third);
72            }
73            Op::Reverse(first, second) => password[first..=second].reverse(),
74            Op::Move(first, second) => {
75                let letter = password.remove(first);
76                password.insert(second, letter);
77            }
78        }
79    }
80
81    fn inverse(self) -> Op {
82        match self {
83            Op::RotateLeft(first) => Op::RotateRight(first),
84            Op::RotateRight(first) => Op::RotateLeft(first),
85            Op::RotateLetterLeft(first) => Op::RotateLetterRight(first),
86            Op::RotateLetterRight(first) => Op::RotateLetterLeft(first),
87            Op::Move(first, second) => Op::Move(second, first),
88            // Other operations are their own inverse.
89            other => other,
90        }
91    }
92}
93
94pub fn parse(input: &str) -> Vec<Op> {
95    input.lines().map(Op::from).collect()
96}
97
98pub fn part1(input: &[Op]) -> String {
99    scramble(input, b"abcdefgh")
100}
101
102pub fn part2(input: &[Op]) -> String {
103    unscramble(input, b"fbgdceah")
104}
105
106pub fn scramble(input: &[Op], slice: &[u8]) -> String {
107    let mut password = slice.to_vec();
108
109    for op in input {
110        op.transform(&mut password);
111    }
112
113    String::from_utf8(password).unwrap()
114}
115
116pub fn unscramble(input: &[Op], slice: &[u8]) -> String {
117    let mut password = slice.to_vec();
118
119    for op in input.iter().rev() {
120        op.inverse().transform(&mut password);
121    }
122
123    String::from_utf8(password).unwrap()
124}