bgneal@0: #ifndef CPP_ENIGMA_ROTOR_H bgneal@0: #define CPP_ENIGMA_ROTOR_H bgneal@0: // Copyright (C) 2012 by Brian Neal. bgneal@0: // This file is part of Cpp-Enigma, the Enigma Machine simulation. bgneal@0: // Cpp-Enigma is released under the MIT License (see License.txt). bgneal@0: // bgneal@0: // rotor.h - This file contains the rotor class. bgneal@0: bgneal@0: #include bgneal@0: #include "enigma_types.h" bgneal@1: #include "enigma_utils.h" bgneal@0: bgneal@0: bgneal@0: namespace enigma bgneal@0: { bgneal@0: class rotor_error : public enigma_error bgneal@0: { bgneal@0: public: bgneal@0: explicit rotor_error(const std::string& what_arg) bgneal@0: : enigma_error(what_arg) bgneal@0: {} bgneal@0: }; bgneal@0: bgneal@0: // The rotor class represents the Enigma Machine rotors (Walzen). bgneal@0: // bgneal@0: // A rotor has 26 circularly arranged pins on the right (entry) side and 26 bgneal@0: // contacts on the left side. Each pin is connected to a single contact by bgneal@0: // internal wiring, thus establishing a substitution cipher. We represent this bgneal@0: // wiring by establishing a mapping from a pin to a contact (and vice versa for bgneal@0: // the return path). Internally we number the pins and contacts from 0-25 in a bgneal@0: // clockwise manner with 0 being the "top". bgneal@0: // bgneal@0: // An alphabetic or numeric ring is fastened to the rotor by the operator. The bgneal@0: // labels of this ring are displayed to the operator through a small window on bgneal@0: // the top panel. The ring can be fixed to the rotor in one of 26 different bgneal@0: // positions; this is called the ring setting (Ringstellung). We will number bgneal@0: // the ring settings from 0-25 where 0 means no offset (e.g. the letter "A" is bgneal@0: // mapped to pin 0 on an alphabetic ring). A ring setting of 1 means the letter bgneal@0: // "B" is mapped to pin 0. bgneal@0: // bgneal@0: // Each rotor can be in one of 26 positions on the spindle, with position 0 bgneal@0: // where pin/contact 0 is being indicated in the operator window. The rotor bgneal@0: // rotates towards the operator by mechanical means during normal operation as bgneal@0: // keys are being pressed during data entry. Position 1 is thus defined to be bgneal@0: // one step from position 0. Likewise, position 25 is the last position before bgneal@0: // another step returns it to position 0, completing 1 trip around the spindle. bgneal@0: // bgneal@0: // Finally, a rotor has a "stepping" or "turnover" parameter. Physically this bgneal@0: // is implemented by putting a notch on the alphabet ring and it controls when bgneal@0: // the rotor will "kick" the rotor to its left, causing the neighbor rotor to bgneal@0: // rotate. Most rotors had one notch, but some Kriegsmarine rotors had 2 bgneal@0: // notches and thus rotated twice as fast. bgneal@0: // bgneal@0: // Note that due to the system of ratchets and pawls, the middle rotor (in a 3 bgneal@0: // rotor Enigma) can "double-step". The middle rotor will advance on the next bgneal@0: // step of the first rotor a second time in a row, if the middle rotor is in bgneal@0: // its own turnover position. bgneal@0: // bgneal@0: // Note that we allow the stepping parameter to be None. This indicates the bgneal@0: // rotor does not rotate. This allows us to model the entry wheel and bgneal@0: // reflectors as stationary rotors. bgneal@0: bgneal@0: class rotor bgneal@0: { bgneal@0: public: bgneal@0: // rotor constructor: bgneal@0: // bgneal@0: // model_name - e.g. "I", "II", "III", "Beta", "Gamma" bgneal@0: // bgneal@0: // wiring - this should be a string of 26 alphabetic characters that bgneal@0: // represents the internal wiring transformation of the signal as it enters bgneal@0: // from the right side. This is the format used in various online bgneal@0: // resources. For example, for the Wehrmacht Enigma type I rotor the bgneal@0: // mapping is "EKMFLGDQVZNTOWYHXUSPAIBRCJ". bgneal@0: // bgneal@0: // ring_setting - this should be an integer from 0-25, inclusive, which bgneal@0: // indicates the Ringstellung. A value of 0 means there is no offset; e.g. bgneal@0: // the letter "A" is fixed to pin 0. A value of 1 means "B" is mapped to bgneal@0: // pin 0. bgneal@0: // bgneal@0: // stepping - this is the stepping or turnover parameter. It should be bgneal@0: // a string such as "Q". This will indicate that when the rotor transitions bgneal@0: // from "Q" to "R" (by observing the operator window), the rotor will "kick" bgneal@0: // the rotor to its left, causing it to rotate. If the rotor has more than one bgneal@0: // notch, a string of length 2 could be used, e.g. "ZM". Another way to think bgneal@0: // of this parameter is that when a character in the stepping string is visible bgneal@0: // in the operator window, a notch is lined up with the pawl on the left side bgneal@0: // of the rotor. This will allow the pawl to push up on the rotor *and* the bgneal@0: // rotor to the left when the next key is depressed. bgneal@0: // bgneal@0: // Note that for purposes of simulation, our rotors will always use bgneal@0: // alphabetic labels A-Z. In reality, the Heer & Luftwaffe devices used bgneal@0: // numbers 01-26, and Kriegsmarine devices used A-Z. Our usage of A-Z is bgneal@0: // simply for simulation convenience. bgneal@0: // display. bgneal@0: bgneal@1: rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = nullptr); bgneal@0: bgneal@0: // Returns the rotor name: bgneal@0: const std::string& name() const { return rotor_name; } bgneal@0: bgneal@1: // Spin the rotor such that the char val appears in the operator window: bgneal@1: void set_display(char val) bgneal@1: { bgneal@1: pos = display_map[val - 'A']; bgneal@1: display_val = val; bgneal@1: } bgneal@0: bgneal@0: // Returns what is currently being displayed in the operator window: bgneal@1: char get_display() const { return display_val; } bgneal@1: bgneal@1: bgneal@1: // sets the ring setting to n, where n [0-25]: bgneal@1: void set_ring_setting(int n); bgneal@1: bgneal@1: // get the current ring setting: bgneal@1: int get_ring_setting() const { return ring_setting; } bgneal@0: bgneal@0: // Simulate a signal entering the rotor from the right at a given pin: bgneal@0: // n must be an integer between 0 and 25. bgneal@0: // Returns the contact number of the output signal (0-25). bgneal@1: int signal_in(int n) const bgneal@1: { bgneal@1: // determine what pin we have at that position due to rotation bgneal@1: const int pin = (n + pos) % 26; bgneal@1: bgneal@1: // run it through the internal wiring bgneal@1: const int contact = entry_map[pin]; bgneal@1: bgneal@1: // turn back into a position due to rotation bgneal@1: return alpha_mod(contact - pos); bgneal@1: } bgneal@0: bgneal@0: // Simulate a signal entering the rotor from the left at a given contact position n. bgneal@0: // n must be an integer between 0 and 25. bgneal@0: // Returns the pin number of the output signal (0-25). bgneal@1: int signal_out(int n) const bgneal@1: { bgneal@1: // determine what contact we have at that position due to rotation bgneal@1: const int contact = (n + pos) % 26; bgneal@1: bgneal@1: // run it through the internal wiring bgneal@1: const int pin = exit_map[contact]; bgneal@1: bgneal@1: // turn back into a position due to rotation bgneal@1: return alpha_mod(pin - pos); bgneal@1: } bgneal@0: bgneal@0: // Return true if this rotor has a notch in the stepping position and false otherwise: bgneal@1: bool notch_over_pawl() const bgneal@1: { bgneal@1: return step_map[display_val - 'A']; bgneal@1: } bgneal@0: bgneal@0: // Rotate the rotor forward one step: bgneal@1: void rotate() bgneal@1: { bgneal@1: pos = (pos + 1) % 26; bgneal@1: display_val = pos_map[pos]; bgneal@1: } bgneal@0: bgneal@0: private: bgneal@0: std::string rotor_name; bgneal@0: std::string wiring_str; bgneal@0: int ring_setting; bgneal@0: int pos; bgneal@1: char display_val; bgneal@1: alpha_int_array entry_map; bgneal@1: alpha_int_array exit_map; bgneal@1: alpha_int_array display_map; bgneal@1: alpha_int_array pos_map; bgneal@1: alpha_bool_array step_map; bgneal@0: }; bgneal@0: } bgneal@0: bgneal@0: #endif