annotate enigma/machine.h @ 17:b04dea5f71a3

process_data() uses step() now.
author Brian Neal <bgneal@gmail.com>
date Sat, 07 Jul 2012 15:03:39 -0500
parents 280facb82b80
children
rev   line source
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