bgneal@4: #ifndef CPP_ENIGMA_MACHINE_H bgneal@4: #define CPP_ENIGMA_MACHINE_H bgneal@4: // Copyright (C) 2012 by Brian Neal. bgneal@4: // This file is part of Cpp-Enigma, the Enigma Machine simulation. bgneal@4: // Cpp-Enigma is released under the MIT License (see License.txt). bgneal@4: // bgneal@4: // machine.h - This file contains the main Enigma machine class. bgneal@4: bgneal@4: #include bgneal@4: #include bgneal@4: #include bgneal@4: #include bgneal@4: #include "enigma_types.h" bgneal@4: #include "rotor.h" bgneal@4: #include "plugboard.h" bgneal@4: bgneal@4: namespace enigma bgneal@4: { bgneal@4: typedef std::vector> rotor_vector; bgneal@4: bgneal@4: class enigma_machine_error : public enigma_error bgneal@4: { bgneal@4: public: bgneal@4: explicit enigma_machine_error(const std::string& what_arg) bgneal@4: : enigma_error(what_arg) bgneal@4: {} bgneal@4: }; bgneal@4: bgneal@4: class enigma_machine bgneal@4: { bgneal@4: public: bgneal@4: // construct an Enigma machine from component parts: bgneal@4: enigma_machine(rotor_vector rv, bgneal@4: std::unique_ptr reflector, bgneal@4: const plugboard& pb); bgneal@4: bgneal@4: // construct an Enigma machine with a default plugboard (no cables connected): bgneal@4: enigma_machine(rotor_vector rv, bgneal@4: std::unique_ptr reflector); bgneal@4: bgneal@4: // key-sheet style constructors: bgneal@4: enigma_machine(const std::vector& rotor_types, bgneal@4: const std::vector& ring_settings, bgneal@4: const std::string& reflector_name = "B", bgneal@4: const std::string& plugboard_settings = ""); bgneal@4: bgneal@4: // set the rotor display (starting position) - 3 rotor version bgneal@4: void set_display(char left, char mid, char right) bgneal@4: { bgneal@4: assert(rotors.size() == 3); bgneal@4: bgneal@4: rotors[0]->set_display(left); bgneal@4: rotors[1]->set_display(mid); bgneal@4: rotors[2]->set_display(right); bgneal@4: } bgneal@4: bgneal@4: // set the rotor display (starting position) - 4 rotor version bgneal@4: void set_display(char c0, char c1, char c2, char c3) bgneal@4: { bgneal@4: assert(rotors.size() == 4); bgneal@4: bgneal@4: rotors[0]->set_display(c0); bgneal@4: rotors[1]->set_display(c1); bgneal@4: rotors[2]->set_display(c2); bgneal@4: rotors[3]->set_display(c3); bgneal@4: } bgneal@4: bgneal@4: // return the rotor display (starting position) as a string bgneal@4: std::string get_display() const bgneal@4: { bgneal@4: std::string result; bgneal@4: for (const auto& r : rotors) bgneal@4: { bgneal@4: result += r->get_display(); bgneal@4: } bgneal@4: return result; bgneal@4: } bgneal@4: bgneal@4: // simulate front panel key press; returns the lamp character that is lit bgneal@4: char key_press(char c) bgneal@4: { bgneal@4: step_rotors(); bgneal@4: return electric_signal(c - 'A') + 'A'; bgneal@4: } bgneal@4: bgneal@4: // Process a buffer of text of length n, placing the result in an output buffer. bgneal@4: void process_text(const char* input, char* output, std::size_t n) bgneal@4: { bgneal@4: for (std::size_t i = 0; i < n; ++i) bgneal@4: { bgneal@4: *output++ = key_press(*input++); bgneal@4: } bgneal@4: } bgneal@4: bgneal@4: std::string process_text(const std::string& input) bgneal@4: { bgneal@4: std::string result; bgneal@4: result.reserve(input.size()); bgneal@4: bgneal@4: for (const auto& c : input) bgneal@4: { bgneal@4: result += key_press(c); bgneal@4: } bgneal@4: return result; bgneal@4: } bgneal@4: bgneal@4: // for access to the plugboard for hill-climbing, etc bgneal@4: plugboard& get_plugboard() { return pb; } bgneal@4: bgneal@4: private: bgneal@4: rotor_vector rotors; bgneal@4: std::unique_ptr reflector; bgneal@4: plugboard pb; bgneal@4: rotor* r_rotor; // rightmost rotor bgneal@4: rotor* m_rotor; // 2nd to right rotor bgneal@4: rotor* l_rotor; // 3rd to right rotor bgneal@4: bgneal@4: void rotor_count_check(); bgneal@4: bgneal@4: void step_rotors() bgneal@4: { bgneal@4: // The right-most rotor's right-side ratchet is always over a pawl, and bgneal@4: // it has no neighbor to the right, so it always rotates. bgneal@4: // bgneal@4: // The middle rotor will rotate if either: bgneal@4: // 1) The right-most rotor's left side notch is over the 2nd pawl bgneal@4: // or bgneal@4: // 2) It has a left-side notch over the 3rd pawl bgneal@4: // bgneal@4: // The third rotor (from the right) will rotate only if the middle rotor bgneal@4: // has a left-side notch over the 3rd pawl. bgneal@4: // bgneal@4: // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost) bgneal@4: // does not rotate (they did not add a 4th pawl to the mechanism). bgneal@4: bgneal@4: const bool l_rotate = m_rotor->notch_over_pawl(); bgneal@4: const bool m_rotate = l_rotate || r_rotor->notch_over_pawl(); bgneal@4: bgneal@4: r_rotor->rotate(); bgneal@4: if (m_rotate) bgneal@4: { bgneal@4: m_rotor->rotate(); bgneal@4: } bgneal@4: if (l_rotate) bgneal@4: { bgneal@4: l_rotor->rotate(); bgneal@4: } bgneal@4: } bgneal@4: bgneal@4: // Simulate running an electric signal through the machine in order to bgneal@4: // perform an encrypt or decrypt operation bgneal@4: // signal_num - the wire (0-25) that the simulated current occurs on bgneal@4: // Returns a lamp number to light (an integer 0-25). bgneal@4: int electric_signal(int signal_num) bgneal@4: { bgneal@4: int pos = pb.signal(signal_num); bgneal@4: bgneal@4: for (auto r = rotors.rbegin(); r != rotors.rend(); ++r) bgneal@4: { bgneal@4: pos = (*r)->signal_in(pos); bgneal@4: } bgneal@4: bgneal@4: pos = reflector->signal_in(pos); bgneal@4: bgneal@4: for (const auto& r : rotors) bgneal@4: { bgneal@4: pos = r->signal_out(pos); bgneal@4: } bgneal@4: bgneal@4: return pb.signal(pos); bgneal@4: } bgneal@4: }; bgneal@4: } bgneal@4: bgneal@4: #endif