bgneal@4
|
1 #ifndef CPP_ENIGMA_MACHINE_H
|
bgneal@4
|
2 #define CPP_ENIGMA_MACHINE_H
|
bgneal@4
|
3 // Copyright (C) 2012 by Brian Neal.
|
bgneal@4
|
4 // This file is part of Cpp-Enigma, the Enigma Machine simulation.
|
bgneal@4
|
5 // Cpp-Enigma is released under the MIT License (see License.txt).
|
bgneal@4
|
6 //
|
bgneal@4
|
7 // machine.h - This file contains the main Enigma machine class.
|
bgneal@4
|
8
|
bgneal@16
|
9 #include <algorithm>
|
bgneal@4
|
10 #include <memory>
|
bgneal@4
|
11 #include <string>
|
bgneal@4
|
12 #include <vector>
|
bgneal@4
|
13 #include <cassert>
|
bgneal@16
|
14 #include <cstddef>
|
bgneal@4
|
15 #include "enigma_types.h"
|
bgneal@4
|
16 #include "rotor.h"
|
bgneal@4
|
17 #include "plugboard.h"
|
bgneal@4
|
18
|
bgneal@4
|
19 namespace enigma
|
bgneal@4
|
20 {
|
bgneal@14
|
21 typedef std::vector<rotor*> rotor_vector;
|
bgneal@4
|
22
|
bgneal@4
|
23 class enigma_machine_error : public enigma_error
|
bgneal@4
|
24 {
|
bgneal@4
|
25 public:
|
bgneal@4
|
26 explicit enigma_machine_error(const std::string& what_arg)
|
bgneal@4
|
27 : enigma_error(what_arg)
|
bgneal@4
|
28 {}
|
bgneal@4
|
29 };
|
bgneal@4
|
30
|
bgneal@4
|
31 class enigma_machine
|
bgneal@4
|
32 {
|
bgneal@4
|
33 public:
|
bgneal@14
|
34 // Construct an Enigma machine from component parts.
|
bgneal@14
|
35 // Note that the enigma_machine makes copies of the rotors and will not
|
bgneal@14
|
36 // delete the rotor pointers:
|
bgneal@11
|
37 enigma_machine(const rotor_vector& rv,
|
bgneal@14
|
38 const rotor& reflector,
|
bgneal@4
|
39 const plugboard& pb);
|
bgneal@4
|
40
|
bgneal@4
|
41 // construct an Enigma machine with a default plugboard (no cables connected):
|
bgneal@11
|
42 enigma_machine(const rotor_vector& rv,
|
bgneal@14
|
43 const rotor& reflector);
|
bgneal@4
|
44
|
bgneal@4
|
45 // key-sheet style constructors:
|
bgneal@4
|
46 enigma_machine(const std::vector<std::string>& rotor_types,
|
bgneal@4
|
47 const std::vector<int>& ring_settings,
|
bgneal@4
|
48 const std::string& reflector_name = "B",
|
bgneal@4
|
49 const std::string& plugboard_settings = "");
|
bgneal@4
|
50
|
bgneal@4
|
51 // set the rotor display (starting position) - 3 rotor version
|
bgneal@4
|
52 void set_display(char left, char mid, char right)
|
bgneal@4
|
53 {
|
bgneal@13
|
54 assert(rotors.size() == 4);
|
bgneal@4
|
55
|
bgneal@13
|
56 rotors[1].set_display(left);
|
bgneal@13
|
57 rotors[2].set_display(mid);
|
bgneal@13
|
58 rotors[3].set_display(right);
|
bgneal@4
|
59 }
|
bgneal@4
|
60
|
bgneal@4
|
61 // set the rotor display (starting position) - 4 rotor version
|
bgneal@4
|
62 void set_display(char c0, char c1, char c2, char c3)
|
bgneal@4
|
63 {
|
bgneal@13
|
64 assert(rotors.size() == 5);
|
bgneal@4
|
65
|
bgneal@13
|
66 rotors[1].set_display(c0);
|
bgneal@13
|
67 rotors[2].set_display(c1);
|
bgneal@13
|
68 rotors[3].set_display(c2);
|
bgneal@13
|
69 rotors[4].set_display(c3);
|
bgneal@4
|
70 }
|
bgneal@4
|
71
|
bgneal@7
|
72 // Set the rotor display (starting position) using a string; the
|
bgneal@7
|
73 // string length must match the number of rotors in use or a
|
bgneal@7
|
74 // enigma_machine_error exception will be thrown:
|
bgneal@7
|
75 void set_display(const std::string& val)
|
bgneal@7
|
76 {
|
bgneal@13
|
77 if (val.size() == 3 && rotors.size() == 4)
|
bgneal@7
|
78 {
|
bgneal@7
|
79 set_display(val[0], val[1], val[2]);
|
bgneal@7
|
80 }
|
bgneal@13
|
81 else if (val.size() == 4 && rotors.size() == 5)
|
bgneal@7
|
82 {
|
bgneal@7
|
83 set_display(val[0], val[1], val[2], val[3]);
|
bgneal@7
|
84 }
|
bgneal@7
|
85 else
|
bgneal@7
|
86 {
|
bgneal@7
|
87 throw enigma_machine_error("set_display invalid size");
|
bgneal@7
|
88 }
|
bgneal@7
|
89 }
|
bgneal@7
|
90
|
bgneal@4
|
91 // return the rotor display (starting position) as a string
|
bgneal@4
|
92 std::string get_display() const
|
bgneal@4
|
93 {
|
bgneal@4
|
94 std::string result;
|
bgneal@13
|
95 for (std::size_t i = 1; i < rotors.size(); ++i)
|
bgneal@4
|
96 {
|
bgneal@13
|
97 result += rotors[i].get_display();
|
bgneal@4
|
98 }
|
bgneal@4
|
99 return result;
|
bgneal@4
|
100 }
|
bgneal@4
|
101
|
bgneal@16
|
102 // Returns the number of rotors in the machine (this count does not include
|
bgneal@16
|
103 // the reflector).
|
bgneal@16
|
104 std::size_t num_rotors() const
|
bgneal@16
|
105 {
|
bgneal@16
|
106 return rotors.size() - 1;
|
bgneal@16
|
107 }
|
bgneal@16
|
108
|
bgneal@16
|
109 // For changing the ring setting on a rotor inside the machine.
|
bgneal@16
|
110 // Parameters:
|
bgneal@16
|
111 // rotor - identifies the rotor to change the ring setting; must be
|
bgneal@16
|
112 // in the range 0 - (num_rotors() - 1). 0 is the leftmost rotor.
|
bgneal@16
|
113 // ring_setting - the ring setting value, 0-25
|
bgneal@16
|
114 //
|
bgneal@16
|
115 void set_ring_setting(int rotor, int ring_setting)
|
bgneal@16
|
116 {
|
bgneal@16
|
117 rotors[rotor + 1].set_ring_setting(ring_setting);
|
bgneal@16
|
118 }
|
bgneal@16
|
119
|
bgneal@16
|
120 // For getting the ring setting on a rotor inside the machine.
|
bgneal@16
|
121 // Parameters:
|
bgneal@16
|
122 // rotor - identifies the rotor to change the ring setting; must be
|
bgneal@16
|
123 // in the range 0 - (num_rotors() - 1). 0 is the leftmost rotor.
|
bgneal@16
|
124 //
|
bgneal@16
|
125 int get_ring_setting(int rotor) const
|
bgneal@16
|
126 {
|
bgneal@16
|
127 return rotors[rotor + 1].get_ring_setting();
|
bgneal@16
|
128 }
|
bgneal@16
|
129
|
bgneal@16
|
130 // For changing the ring settings on all rotors inside the machine.
|
bgneal@16
|
131 // Parameters:
|
bgneal@16
|
132 // settings - a vector of ring settings, 0-25. The size of this
|
bgneal@16
|
133 // vector must match num_rotors().
|
bgneal@16
|
134 //
|
bgneal@16
|
135 void set_ring_settings(const std::vector<int>& settings)
|
bgneal@16
|
136 {
|
bgneal@16
|
137 if (settings.size() == num_rotors())
|
bgneal@16
|
138 {
|
bgneal@16
|
139 rotor* r = &rotors[1]; // skip the reflector;
|
bgneal@16
|
140 for (auto s : settings)
|
bgneal@16
|
141 {
|
bgneal@16
|
142 r->set_ring_setting(s);
|
bgneal@16
|
143 ++r;
|
bgneal@16
|
144 }
|
bgneal@16
|
145 }
|
bgneal@16
|
146 else
|
bgneal@16
|
147 {
|
bgneal@16
|
148 throw enigma_machine_error("set_ring_settings rotor/settings size mismatch");
|
bgneal@16
|
149 }
|
bgneal@16
|
150 }
|
bgneal@16
|
151
|
bgneal@16
|
152 // For getting the ring settings as a vector of integers. Element 0 corresponds
|
bgneal@16
|
153 // to the leftmost rotor.
|
bgneal@16
|
154 std::vector<int> get_ring_settings() const
|
bgneal@16
|
155 {
|
bgneal@16
|
156 std::vector<int> result(num_rotors());
|
bgneal@16
|
157 std::transform(rotors.begin() + 1, rotors.end(), result.begin(),
|
bgneal@16
|
158 [](const rotor& r) { return r.get_ring_setting(); });
|
bgneal@16
|
159 return result;
|
bgneal@16
|
160 }
|
bgneal@16
|
161
|
bgneal@4
|
162 // simulate front panel key press; returns the lamp character that is lit
|
bgneal@4
|
163 char key_press(char c)
|
bgneal@4
|
164 {
|
bgneal@4
|
165 step_rotors();
|
bgneal@4
|
166 return electric_signal(c - 'A') + 'A';
|
bgneal@4
|
167 }
|
bgneal@4
|
168
|
bgneal@15
|
169 // this is like key_press(), but it works in signal numbers (0-25) instead of chars:
|
bgneal@15
|
170 int step(int n)
|
bgneal@15
|
171 {
|
bgneal@15
|
172 step_rotors();
|
bgneal@15
|
173 return electric_signal(n);
|
bgneal@15
|
174 }
|
bgneal@15
|
175
|
bgneal@4
|
176 // Process a buffer of text of length n, placing the result in an output buffer.
|
bgneal@4
|
177 void process_text(const char* input, char* output, std::size_t n)
|
bgneal@4
|
178 {
|
bgneal@4
|
179 for (std::size_t i = 0; i < n; ++i)
|
bgneal@4
|
180 {
|
bgneal@4
|
181 *output++ = key_press(*input++);
|
bgneal@4
|
182 }
|
bgneal@4
|
183 }
|
bgneal@4
|
184
|
bgneal@16
|
185 // Process a buffer of text from a string, returning the result as a string.
|
bgneal@4
|
186 std::string process_text(const std::string& input)
|
bgneal@4
|
187 {
|
bgneal@4
|
188 std::string result;
|
bgneal@4
|
189 result.reserve(input.size());
|
bgneal@4
|
190
|
bgneal@4
|
191 for (const auto& c : input)
|
bgneal@4
|
192 {
|
bgneal@4
|
193 result += key_press(c);
|
bgneal@4
|
194 }
|
bgneal@4
|
195 return result;
|
bgneal@4
|
196 }
|
bgneal@4
|
197
|
bgneal@12
|
198 // Process a buffer of pre-processed text of length n, placing the result in an output buffer.
|
bgneal@12
|
199 void process_data(const char* input, char* output, std::size_t n)
|
bgneal@12
|
200 {
|
bgneal@12
|
201 for (std::size_t i = 0; i < n; ++i)
|
bgneal@12
|
202 {
|
bgneal@17
|
203 *output++ = step(*input++) + 'A';
|
bgneal@12
|
204 }
|
bgneal@12
|
205 }
|
bgneal@12
|
206
|
bgneal@4
|
207 // for access to the plugboard for hill-climbing, etc
|
bgneal@4
|
208 plugboard& get_plugboard() { return pb; }
|
bgneal@4
|
209
|
bgneal@8
|
210 // Returns a string representation of the enigma machine's state. Useful
|
bgneal@8
|
211 // for logging, etc:
|
bgneal@8
|
212 //
|
bgneal@8
|
213 std::string army_str() const { return str(true); }
|
bgneal@8
|
214 std::string navy_str() const { return str(false); }
|
bgneal@8
|
215
|
bgneal@4
|
216 private:
|
bgneal@13
|
217 // Note that to improve cache performance, the rotors and reflectors are stored
|
bgneal@13
|
218 // in a contiguous vector.
|
bgneal@13
|
219 std::vector<rotor> rotors; // rotor & reflector array
|
bgneal@4
|
220 plugboard pb;
|
bgneal@4
|
221 rotor* r_rotor; // rightmost rotor
|
bgneal@4
|
222 rotor* m_rotor; // 2nd to right rotor
|
bgneal@4
|
223 rotor* l_rotor; // 3rd to right rotor
|
bgneal@4
|
224
|
bgneal@4
|
225 void rotor_count_check();
|
bgneal@4
|
226
|
bgneal@4
|
227 void step_rotors()
|
bgneal@4
|
228 {
|
bgneal@4
|
229 // The right-most rotor's right-side ratchet is always over a pawl, and
|
bgneal@4
|
230 // it has no neighbor to the right, so it always rotates.
|
bgneal@4
|
231 //
|
bgneal@4
|
232 // The middle rotor will rotate if either:
|
bgneal@4
|
233 // 1) The right-most rotor's left side notch is over the 2nd pawl
|
bgneal@4
|
234 // or
|
bgneal@4
|
235 // 2) It has a left-side notch over the 3rd pawl
|
bgneal@4
|
236 //
|
bgneal@4
|
237 // The third rotor (from the right) will rotate only if the middle rotor
|
bgneal@4
|
238 // has a left-side notch over the 3rd pawl.
|
bgneal@4
|
239 //
|
bgneal@4
|
240 // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost)
|
bgneal@4
|
241 // does not rotate (they did not add a 4th pawl to the mechanism).
|
bgneal@4
|
242
|
bgneal@4
|
243 const bool l_rotate = m_rotor->notch_over_pawl();
|
bgneal@4
|
244 const bool m_rotate = l_rotate || r_rotor->notch_over_pawl();
|
bgneal@4
|
245
|
bgneal@4
|
246 r_rotor->rotate();
|
bgneal@4
|
247 if (m_rotate)
|
bgneal@4
|
248 {
|
bgneal@4
|
249 m_rotor->rotate();
|
bgneal@4
|
250 }
|
bgneal@4
|
251 if (l_rotate)
|
bgneal@4
|
252 {
|
bgneal@4
|
253 l_rotor->rotate();
|
bgneal@4
|
254 }
|
bgneal@4
|
255 }
|
bgneal@4
|
256
|
bgneal@4
|
257 // Simulate running an electric signal through the machine in order to
|
bgneal@4
|
258 // perform an encrypt or decrypt operation
|
bgneal@4
|
259 // signal_num - the wire (0-25) that the simulated current occurs on
|
bgneal@4
|
260 // Returns a lamp number to light (an integer 0-25).
|
bgneal@4
|
261 int electric_signal(int signal_num)
|
bgneal@4
|
262 {
|
bgneal@13
|
263 int n = pb.signal(signal_num);
|
bgneal@4
|
264
|
bgneal@13
|
265 if (rotors.size() == 4) // 3 rotors + reflector
|
bgneal@4
|
266 {
|
bgneal@13
|
267 n = rotors[3].signal_in(n);
|
bgneal@13
|
268 n = rotors[2].signal_in(n);
|
bgneal@13
|
269 n = rotors[1].signal_in(n);
|
bgneal@13
|
270 n = rotors[0].signal_in(n); // reflector
|
bgneal@13
|
271 n = rotors[1].signal_out(n);
|
bgneal@13
|
272 n = rotors[2].signal_out(n);
|
bgneal@13
|
273 n = rotors[3].signal_out(n);
|
bgneal@4
|
274 }
|
bgneal@13
|
275 else // Kriegsmarine 4 rotor + reflector
|
bgneal@4
|
276 {
|
bgneal@13
|
277 n = rotors[4].signal_in(n);
|
bgneal@13
|
278 n = rotors[3].signal_in(n);
|
bgneal@13
|
279 n = rotors[2].signal_in(n);
|
bgneal@13
|
280 n = rotors[1].signal_in(n);
|
bgneal@13
|
281 n = rotors[0].signal_in(n); // reflector
|
bgneal@13
|
282 n = rotors[1].signal_out(n);
|
bgneal@13
|
283 n = rotors[2].signal_out(n);
|
bgneal@13
|
284 n = rotors[3].signal_out(n);
|
bgneal@13
|
285 n = rotors[4].signal_out(n);
|
bgneal@4
|
286 }
|
bgneal@13
|
287 return pb.signal(n);
|
bgneal@4
|
288 }
|
bgneal@8
|
289
|
bgneal@8
|
290 std::string str(bool army) const;
|
bgneal@4
|
291 };
|
bgneal@4
|
292 }
|
bgneal@4
|
293
|
bgneal@4
|
294 #endif
|