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