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