annotate enigma/machine.h @ 15:9e02d8696e67

Added enigma_machine::step().
author Brian Neal <bgneal@gmail.com>
date Wed, 04 Jul 2012 19:52:41 -0500
parents 919b7a0d1802
children 280facb82b80
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@4 9 #include <memory>
bgneal@4 10 #include <string>
bgneal@4 11 #include <vector>
bgneal@4 12 #include <cassert>
bgneal@4 13 #include "enigma_types.h"
bgneal@4 14 #include "rotor.h"
bgneal@4 15 #include "plugboard.h"
bgneal@4 16
bgneal@4 17 namespace enigma
bgneal@4 18 {
bgneal@14 19 typedef std::vector<rotor*> rotor_vector;
bgneal@4 20
bgneal@4 21 class enigma_machine_error : public enigma_error
bgneal@4 22 {
bgneal@4 23 public:
bgneal@4 24 explicit enigma_machine_error(const std::string& what_arg)
bgneal@4 25 : enigma_error(what_arg)
bgneal@4 26 {}
bgneal@4 27 };
bgneal@4 28
bgneal@4 29 class enigma_machine
bgneal@4 30 {
bgneal@4 31 public:
bgneal@14 32 // Construct an Enigma machine from component parts.
bgneal@14 33 // Note that the enigma_machine makes copies of the rotors and will not
bgneal@14 34 // delete the rotor pointers:
bgneal@11 35 enigma_machine(const rotor_vector& rv,
bgneal@14 36 const rotor& reflector,
bgneal@4 37 const plugboard& pb);
bgneal@4 38
bgneal@4 39 // construct an Enigma machine with a default plugboard (no cables connected):
bgneal@11 40 enigma_machine(const rotor_vector& rv,
bgneal@14 41 const rotor& reflector);
bgneal@4 42
bgneal@4 43 // key-sheet style constructors:
bgneal@4 44 enigma_machine(const std::vector<std::string>& rotor_types,
bgneal@4 45 const std::vector<int>& ring_settings,
bgneal@4 46 const std::string& reflector_name = "B",
bgneal@4 47 const std::string& plugboard_settings = "");
bgneal@4 48
bgneal@4 49 // set the rotor display (starting position) - 3 rotor version
bgneal@4 50 void set_display(char left, char mid, char right)
bgneal@4 51 {
bgneal@13 52 assert(rotors.size() == 4);
bgneal@4 53
bgneal@13 54 rotors[1].set_display(left);
bgneal@13 55 rotors[2].set_display(mid);
bgneal@13 56 rotors[3].set_display(right);
bgneal@4 57 }
bgneal@4 58
bgneal@4 59 // set the rotor display (starting position) - 4 rotor version
bgneal@4 60 void set_display(char c0, char c1, char c2, char c3)
bgneal@4 61 {
bgneal@13 62 assert(rotors.size() == 5);
bgneal@4 63
bgneal@13 64 rotors[1].set_display(c0);
bgneal@13 65 rotors[2].set_display(c1);
bgneal@13 66 rotors[3].set_display(c2);
bgneal@13 67 rotors[4].set_display(c3);
bgneal@4 68 }
bgneal@4 69
bgneal@7 70 // Set the rotor display (starting position) using a string; the
bgneal@7 71 // string length must match the number of rotors in use or a
bgneal@7 72 // enigma_machine_error exception will be thrown:
bgneal@7 73 void set_display(const std::string& val)
bgneal@7 74 {
bgneal@13 75 if (val.size() == 3 && rotors.size() == 4)
bgneal@7 76 {
bgneal@7 77 set_display(val[0], val[1], val[2]);
bgneal@7 78 }
bgneal@13 79 else if (val.size() == 4 && rotors.size() == 5)
bgneal@7 80 {
bgneal@7 81 set_display(val[0], val[1], val[2], val[3]);
bgneal@7 82 }
bgneal@7 83 else
bgneal@7 84 {
bgneal@7 85 throw enigma_machine_error("set_display invalid size");
bgneal@7 86 }
bgneal@7 87 }
bgneal@7 88
bgneal@4 89 // return the rotor display (starting position) as a string
bgneal@4 90 std::string get_display() const
bgneal@4 91 {
bgneal@4 92 std::string result;
bgneal@13 93 for (std::size_t i = 1; i < rotors.size(); ++i)
bgneal@4 94 {
bgneal@13 95 result += rotors[i].get_display();
bgneal@4 96 }
bgneal@4 97 return result;
bgneal@4 98 }
bgneal@4 99
bgneal@4 100 // simulate front panel key press; returns the lamp character that is lit
bgneal@4 101 char key_press(char c)
bgneal@4 102 {
bgneal@4 103 step_rotors();
bgneal@4 104 return electric_signal(c - 'A') + 'A';
bgneal@4 105 }
bgneal@4 106
bgneal@15 107 // this is like key_press(), but it works in signal numbers (0-25) instead of chars:
bgneal@15 108 int step(int n)
bgneal@15 109 {
bgneal@15 110 step_rotors();
bgneal@15 111 return electric_signal(n);
bgneal@15 112 }
bgneal@15 113
bgneal@4 114 // Process a buffer of text of length n, placing the result in an output buffer.
bgneal@4 115 void process_text(const char* input, char* output, std::size_t n)
bgneal@4 116 {
bgneal@4 117 for (std::size_t i = 0; i < n; ++i)
bgneal@4 118 {
bgneal@4 119 *output++ = key_press(*input++);
bgneal@4 120 }
bgneal@4 121 }
bgneal@4 122
bgneal@4 123 std::string process_text(const std::string& input)
bgneal@4 124 {
bgneal@4 125 std::string result;
bgneal@4 126 result.reserve(input.size());
bgneal@4 127
bgneal@4 128 for (const auto& c : input)
bgneal@4 129 {
bgneal@4 130 result += key_press(c);
bgneal@4 131 }
bgneal@4 132 return result;
bgneal@4 133 }
bgneal@4 134
bgneal@12 135 // Process a buffer of pre-processed text of length n, placing the result in an output buffer.
bgneal@12 136 void process_data(const char* input, char* output, std::size_t n)
bgneal@12 137 {
bgneal@12 138 for (std::size_t i = 0; i < n; ++i)
bgneal@12 139 {
bgneal@12 140 step_rotors();
bgneal@12 141 *output++ = electric_signal(*input++) + 'A';
bgneal@12 142 }
bgneal@12 143 }
bgneal@12 144
bgneal@4 145 // for access to the plugboard for hill-climbing, etc
bgneal@4 146 plugboard& get_plugboard() { return pb; }
bgneal@4 147
bgneal@8 148 // Returns a string representation of the enigma machine's state. Useful
bgneal@8 149 // for logging, etc:
bgneal@8 150 //
bgneal@8 151 std::string army_str() const { return str(true); }
bgneal@8 152 std::string navy_str() const { return str(false); }
bgneal@8 153
bgneal@4 154 private:
bgneal@13 155 // Note that to improve cache performance, the rotors and reflectors are stored
bgneal@13 156 // in a contiguous vector.
bgneal@13 157 std::vector<rotor> rotors; // rotor & reflector array
bgneal@4 158 plugboard pb;
bgneal@4 159 rotor* r_rotor; // rightmost rotor
bgneal@4 160 rotor* m_rotor; // 2nd to right rotor
bgneal@4 161 rotor* l_rotor; // 3rd to right rotor
bgneal@4 162
bgneal@4 163 void rotor_count_check();
bgneal@4 164
bgneal@4 165 void step_rotors()
bgneal@4 166 {
bgneal@4 167 // The right-most rotor's right-side ratchet is always over a pawl, and
bgneal@4 168 // it has no neighbor to the right, so it always rotates.
bgneal@4 169 //
bgneal@4 170 // The middle rotor will rotate if either:
bgneal@4 171 // 1) The right-most rotor's left side notch is over the 2nd pawl
bgneal@4 172 // or
bgneal@4 173 // 2) It has a left-side notch over the 3rd pawl
bgneal@4 174 //
bgneal@4 175 // The third rotor (from the right) will rotate only if the middle rotor
bgneal@4 176 // has a left-side notch over the 3rd pawl.
bgneal@4 177 //
bgneal@4 178 // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost)
bgneal@4 179 // does not rotate (they did not add a 4th pawl to the mechanism).
bgneal@4 180
bgneal@4 181 const bool l_rotate = m_rotor->notch_over_pawl();
bgneal@4 182 const bool m_rotate = l_rotate || r_rotor->notch_over_pawl();
bgneal@4 183
bgneal@4 184 r_rotor->rotate();
bgneal@4 185 if (m_rotate)
bgneal@4 186 {
bgneal@4 187 m_rotor->rotate();
bgneal@4 188 }
bgneal@4 189 if (l_rotate)
bgneal@4 190 {
bgneal@4 191 l_rotor->rotate();
bgneal@4 192 }
bgneal@4 193 }
bgneal@4 194
bgneal@4 195 // Simulate running an electric signal through the machine in order to
bgneal@4 196 // perform an encrypt or decrypt operation
bgneal@4 197 // signal_num - the wire (0-25) that the simulated current occurs on
bgneal@4 198 // Returns a lamp number to light (an integer 0-25).
bgneal@4 199 int electric_signal(int signal_num)
bgneal@4 200 {
bgneal@13 201 int n = pb.signal(signal_num);
bgneal@4 202
bgneal@13 203 if (rotors.size() == 4) // 3 rotors + reflector
bgneal@4 204 {
bgneal@13 205 n = rotors[3].signal_in(n);
bgneal@13 206 n = rotors[2].signal_in(n);
bgneal@13 207 n = rotors[1].signal_in(n);
bgneal@13 208 n = rotors[0].signal_in(n); // reflector
bgneal@13 209 n = rotors[1].signal_out(n);
bgneal@13 210 n = rotors[2].signal_out(n);
bgneal@13 211 n = rotors[3].signal_out(n);
bgneal@4 212 }
bgneal@13 213 else // Kriegsmarine 4 rotor + reflector
bgneal@4 214 {
bgneal@13 215 n = rotors[4].signal_in(n);
bgneal@13 216 n = rotors[3].signal_in(n);
bgneal@13 217 n = rotors[2].signal_in(n);
bgneal@13 218 n = rotors[1].signal_in(n);
bgneal@13 219 n = rotors[0].signal_in(n); // reflector
bgneal@13 220 n = rotors[1].signal_out(n);
bgneal@13 221 n = rotors[2].signal_out(n);
bgneal@13 222 n = rotors[3].signal_out(n);
bgneal@13 223 n = rotors[4].signal_out(n);
bgneal@4 224 }
bgneal@13 225 return pb.signal(n);
bgneal@4 226 }
bgneal@8 227
bgneal@8 228 std::string str(bool army) const;
bgneal@4 229 };
bgneal@4 230 }
bgneal@4 231
bgneal@4 232 #endif