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