mini_enigma/components/
rotor.rs1use crate::utils::UNIT_LETTER_MAP;
2
3use super::utils::{string_to_inv_letter_map, string_to_letter_map, Letter, LetterMapping};
4
5const MAX_NOTCHES: usize = 2;
6
7#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
8pub struct Rotor {
10 ringstellung: u8,
11 step: Letter,
12 wiring: LetterMapping,
13 reverse_wiring: LetterMapping,
14 notches: [Letter; MAX_NOTCHES],
15 fixed: bool,
16}
17
18impl Default for Rotor {
19 fn default() -> Self {
20 Rotor {
21 ringstellung: u8::default(),
22 step: Letter::default(),
23 wiring: UNIT_LETTER_MAP,
24 reverse_wiring: UNIT_LETTER_MAP,
25 notches: [Letter::default(); MAX_NOTCHES],
26 fixed: false,
27 }
28 }
29}
30
31impl Rotor {
32 #[must_use]
36 pub const fn from_static_str(
37 string: &'static str,
38 notches: [Letter; MAX_NOTCHES],
39 fixed: bool,
40 ) -> Rotor {
41 Rotor {
42 wiring: string_to_letter_map(string),
43 reverse_wiring: string_to_inv_letter_map(string),
44 step: Letter::A,
45 ringstellung: 0,
46 notches,
47 fixed,
48 }
49 }
50
51 pub fn set_grundstellung(&mut self, letter: Letter) {
53 self.step = letter;
54 }
55
56 pub fn set_ringstellung(&mut self, ringstellung: u8) {
62 assert!(ringstellung > 0);
63 self.ringstellung = ringstellung - 1;
64 }
65
66 #[must_use]
69 pub fn wiring_map(&self, index: Letter) -> Letter {
70 self.wiring[index - self.ringstellung + self.step] + self.ringstellung - self.step
71 }
72
73 #[must_use]
76 pub fn rev_wiring_map(&self, index: Letter) -> Letter {
77 self.reverse_wiring[index - self.ringstellung + self.step] + self.ringstellung - self.step
78 }
79
80 pub fn step(&mut self) -> bool {
82 if self.fixed {
83 false
84 } else {
85 self.step += 1;
86 self.notches.iter().any(|&x| x == self.step)
87 }
88 }
89
90 #[must_use]
92 pub fn get_grundstellung(&self) -> Letter {
93 self.step
94 }
95
96 #[must_use]
100 pub fn will_turn(&self) -> bool {
101 self.notches.iter().any(|&x| x == self.step + 1)
102 }
103}
104
105#[allow(missing_docs)]
106pub const ROTOR_I: Rotor =
107 Rotor::from_static_str("EKMFLGDQVZNTOWYHXUSPAIBRCJ", [Letter::R, Letter::R], false);
108
109#[allow(missing_docs)]
110pub const ROTOR_II: Rotor =
111 Rotor::from_static_str("AJDKSIRUXBLHWTMCQGZNPYFVOE", [Letter::F, Letter::F], false);
112
113#[allow(missing_docs)]
114pub const ROTOR_III: Rotor =
115 Rotor::from_static_str("BDFHJLCPRTXVZNYEIWGAKMUSQO", [Letter::W, Letter::W], false);
116
117#[allow(missing_docs)]
118pub const ROTOR_IV: Rotor =
119 Rotor::from_static_str("ESOVPZJAYQUIRHXLNFTGKDCMWB", [Letter::K, Letter::K], false);
120
121#[allow(missing_docs)]
122pub const ROTOR_V: Rotor =
123 Rotor::from_static_str("VZBRGITYUPSDNHLXAWMJQOFECK", [Letter::A, Letter::A], false);
124
125#[allow(missing_docs)]
126pub const ROTOR_VI: Rotor =
127 Rotor::from_static_str("JPGVOUMFYQBENHZRDKASXLICTW", [Letter::A, Letter::N], false);
128
129#[allow(missing_docs)]
130pub const ROTOR_VII: Rotor =
131 Rotor::from_static_str("NZJHGRCXMYSWBOUFAIVLPEKQDT", [Letter::A, Letter::N], false);
132
133#[allow(missing_docs)]
134pub const ROTOR_VIII: Rotor =
135 Rotor::from_static_str("FKQHTLXOCBJSPDZRAMEWNIUYGV", [Letter::A, Letter::N], false);
136
137#[allow(missing_docs)]
138pub const ROTOR_BETA: Rotor =
139 Rotor::from_static_str("LEYJVCNIXWPBQMDRTAKZGFUHOS", [Letter::A, Letter::A], true);
140
141#[allow(missing_docs)]
142pub const ROTOR_GAMMA: Rotor =
143 Rotor::from_static_str("FSOKANUERHMBTIYCWLQPZXVGJD", [Letter::A, Letter::A], true);
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_get_at() {
151 let rotor =
152 Rotor::from_static_str("ZYXWVUTSRQPONMLKJIHGFEDCBA", [Letter::A, Letter::A], false);
153 assert_eq!(rotor.wiring_map(Letter::A), Letter::Z);
154 assert_eq!(rotor.wiring_map(Letter::C), Letter::X);
155 assert_eq!(rotor.wiring_map(Letter::Z), Letter::A);
156 }
157
158 #[test]
159 fn test_offset() {
160 let mut rotor = ROTOR_I;
161 rotor.set_grundstellung(Letter::B);
162 assert_eq!(rotor.wiring_map(Letter::A), Letter::J)
163 }
164
165 #[test]
166 fn test_ringstellung() {
167 let mut rotor = ROTOR_I;
168 rotor.set_grundstellung(Letter::A);
169 rotor.set_ringstellung(2);
170 assert_eq!(rotor.wiring_map(Letter::A), Letter::K)
171 }
172
173 #[test]
174 fn test_reverse() {
175 let rotor =
176 Rotor::from_static_str("NMLKJIHGFEDCBAZYXWVUTSRQPO", [Letter::A, Letter::A], false);
177 let forward = rotor.wiring_map(Letter::C);
178 assert_eq!(rotor.rev_wiring_map(forward), Letter::C);
179 }
180
181 #[test]
182 fn test_step() {
183 let mut rotor =
184 Rotor::from_static_str("NMLKJIHGFEDCBAZYXWVUTSRQPO", [Letter::C, Letter::C], false);
185 assert!(!rotor.step());
186 assert!(rotor.step());
187 assert!(!rotor.step());
188 }
189
190 #[test]
191 fn test_get_pos() {
192 let mut rotor =
193 Rotor::from_static_str("NMLKJIHGFEDCBAZYXWVUTSRQPO", [Letter::C, Letter::C], false);
194 rotor.set_grundstellung(Letter::G);
195 assert_eq!(Letter::G, rotor.get_grundstellung());
196 }
197}