annotate 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
rev   line source
bgneal@0 1 #ifndef CPP_ENIGMA_ROTOR_H
bgneal@0 2 #define CPP_ENIGMA_ROTOR_H
bgneal@0 3 // Copyright (C) 2012 by Brian Neal.
bgneal@0 4 // This file is part of Cpp-Enigma, the Enigma Machine simulation.
bgneal@0 5 // Cpp-Enigma is released under the MIT License (see License.txt).
bgneal@0 6 //
bgneal@0 7 // rotor.h - This file contains the rotor class.
bgneal@0 8
bgneal@0 9 #include <string>
bgneal@0 10 #include "enigma_types.h"
bgneal@1 11 #include "enigma_utils.h"
bgneal@0 12
bgneal@0 13
bgneal@0 14 namespace enigma
bgneal@0 15 {
bgneal@0 16 class rotor_error : public enigma_error
bgneal@0 17 {
bgneal@0 18 public:
bgneal@0 19 explicit rotor_error(const std::string& what_arg)
bgneal@0 20 : enigma_error(what_arg)
bgneal@0 21 {}
bgneal@0 22 };
bgneal@0 23
bgneal@0 24 // The rotor class represents the Enigma Machine rotors (Walzen).
bgneal@0 25 //
bgneal@0 26 // A rotor has 26 circularly arranged pins on the right (entry) side and 26
bgneal@0 27 // contacts on the left side. Each pin is connected to a single contact by
bgneal@0 28 // internal wiring, thus establishing a substitution cipher. We represent this
bgneal@0 29 // wiring by establishing a mapping from a pin to a contact (and vice versa for
bgneal@0 30 // the return path). Internally we number the pins and contacts from 0-25 in a
bgneal@0 31 // clockwise manner with 0 being the "top".
bgneal@0 32 //
bgneal@0 33 // An alphabetic or numeric ring is fastened to the rotor by the operator. The
bgneal@0 34 // labels of this ring are displayed to the operator through a small window on
bgneal@0 35 // the top panel. The ring can be fixed to the rotor in one of 26 different
bgneal@0 36 // positions; this is called the ring setting (Ringstellung). We will number
bgneal@0 37 // the ring settings from 0-25 where 0 means no offset (e.g. the letter "A" is
bgneal@0 38 // mapped to pin 0 on an alphabetic ring). A ring setting of 1 means the letter
bgneal@0 39 // "B" is mapped to pin 0.
bgneal@0 40 //
bgneal@0 41 // Each rotor can be in one of 26 positions on the spindle, with position 0
bgneal@0 42 // where pin/contact 0 is being indicated in the operator window. The rotor
bgneal@0 43 // rotates towards the operator by mechanical means during normal operation as
bgneal@0 44 // keys are being pressed during data entry. Position 1 is thus defined to be
bgneal@0 45 // one step from position 0. Likewise, position 25 is the last position before
bgneal@0 46 // another step returns it to position 0, completing 1 trip around the spindle.
bgneal@0 47 //
bgneal@0 48 // Finally, a rotor has a "stepping" or "turnover" parameter. Physically this
bgneal@0 49 // is implemented by putting a notch on the alphabet ring and it controls when
bgneal@0 50 // the rotor will "kick" the rotor to its left, causing the neighbor rotor to
bgneal@0 51 // rotate. Most rotors had one notch, but some Kriegsmarine rotors had 2
bgneal@0 52 // notches and thus rotated twice as fast.
bgneal@0 53 //
bgneal@0 54 // Note that due to the system of ratchets and pawls, the middle rotor (in a 3
bgneal@0 55 // rotor Enigma) can "double-step". The middle rotor will advance on the next
bgneal@0 56 // step of the first rotor a second time in a row, if the middle rotor is in
bgneal@0 57 // its own turnover position.
bgneal@0 58 //
bgneal@0 59 // Note that we allow the stepping parameter to be None. This indicates the
bgneal@0 60 // rotor does not rotate. This allows us to model the entry wheel and
bgneal@0 61 // reflectors as stationary rotors.
bgneal@0 62
bgneal@0 63 class rotor
bgneal@0 64 {
bgneal@0 65 public:
bgneal@0 66 // rotor constructor:
bgneal@0 67 //
bgneal@0 68 // model_name - e.g. "I", "II", "III", "Beta", "Gamma"
bgneal@0 69 //
bgneal@0 70 // wiring - this should be a string of 26 alphabetic characters that
bgneal@0 71 // represents the internal wiring transformation of the signal as it enters
bgneal@0 72 // from the right side. This is the format used in various online
bgneal@0 73 // resources. For example, for the Wehrmacht Enigma type I rotor the
bgneal@0 74 // mapping is "EKMFLGDQVZNTOWYHXUSPAIBRCJ".
bgneal@0 75 //
bgneal@0 76 // ring_setting - this should be an integer from 0-25, inclusive, which
bgneal@0 77 // indicates the Ringstellung. A value of 0 means there is no offset; e.g.
bgneal@0 78 // the letter "A" is fixed to pin 0. A value of 1 means "B" is mapped to
bgneal@0 79 // pin 0.
bgneal@0 80 //
bgneal@0 81 // stepping - this is the stepping or turnover parameter. It should be
bgneal@0 82 // a string such as "Q". This will indicate that when the rotor transitions
bgneal@0 83 // from "Q" to "R" (by observing the operator window), the rotor will "kick"
bgneal@0 84 // the rotor to its left, causing it to rotate. If the rotor has more than one
bgneal@0 85 // notch, a string of length 2 could be used, e.g. "ZM". Another way to think
bgneal@0 86 // of this parameter is that when a character in the stepping string is visible
bgneal@0 87 // in the operator window, a notch is lined up with the pawl on the left side
bgneal@0 88 // of the rotor. This will allow the pawl to push up on the rotor *and* the
bgneal@0 89 // rotor to the left when the next key is depressed.
bgneal@0 90 //
bgneal@0 91 // Note that for purposes of simulation, our rotors will always use
bgneal@0 92 // alphabetic labels A-Z. In reality, the Heer & Luftwaffe devices used
bgneal@0 93 // numbers 01-26, and Kriegsmarine devices used A-Z. Our usage of A-Z is
bgneal@0 94 // simply for simulation convenience.
bgneal@0 95 // display.
bgneal@0 96
bgneal@1 97 rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = nullptr);
bgneal@0 98
bgneal@0 99 // Returns the rotor name:
bgneal@0 100 const std::string& name() const { return rotor_name; }
bgneal@0 101
bgneal@1 102 // Spin the rotor such that the char val appears in the operator window:
bgneal@1 103 void set_display(char val)
bgneal@1 104 {
bgneal@1 105 pos = display_map[val - 'A'];
bgneal@1 106 display_val = val;
bgneal@1 107 }
bgneal@0 108
bgneal@0 109 // Returns what is currently being displayed in the operator window:
bgneal@1 110 char get_display() const { return display_val; }
bgneal@1 111
bgneal@1 112
bgneal@1 113 // sets the ring setting to n, where n [0-25]:
bgneal@1 114 void set_ring_setting(int n);
bgneal@1 115
bgneal@1 116 // get the current ring setting:
bgneal@1 117 int get_ring_setting() const { return ring_setting; }
bgneal@0 118
bgneal@0 119 // Simulate a signal entering the rotor from the right at a given pin:
bgneal@0 120 // n must be an integer between 0 and 25.
bgneal@0 121 // Returns the contact number of the output signal (0-25).
bgneal@1 122 int signal_in(int n) const
bgneal@1 123 {
bgneal@1 124 // determine what pin we have at that position due to rotation
bgneal@1 125 const int pin = (n + pos) % 26;
bgneal@1 126
bgneal@1 127 // run it through the internal wiring
bgneal@1 128 const int contact = entry_map[pin];
bgneal@1 129
bgneal@1 130 // turn back into a position due to rotation
bgneal@1 131 return alpha_mod(contact - pos);
bgneal@1 132 }
bgneal@0 133
bgneal@0 134 // Simulate a signal entering the rotor from the left at a given contact position n.
bgneal@0 135 // n must be an integer between 0 and 25.
bgneal@0 136 // Returns the pin number of the output signal (0-25).
bgneal@1 137 int signal_out(int n) const
bgneal@1 138 {
bgneal@1 139 // determine what contact we have at that position due to rotation
bgneal@1 140 const int contact = (n + pos) % 26;
bgneal@1 141
bgneal@1 142 // run it through the internal wiring
bgneal@1 143 const int pin = exit_map[contact];
bgneal@1 144
bgneal@1 145 // turn back into a position due to rotation
bgneal@1 146 return alpha_mod(pin - pos);
bgneal@1 147 }
bgneal@0 148
bgneal@0 149 // Return true if this rotor has a notch in the stepping position and false otherwise:
bgneal@1 150 bool notch_over_pawl() const
bgneal@1 151 {
bgneal@1 152 return step_map[display_val - 'A'];
bgneal@1 153 }
bgneal@0 154
bgneal@0 155 // Rotate the rotor forward one step:
bgneal@1 156 void rotate()
bgneal@1 157 {
bgneal@1 158 pos = (pos + 1) % 26;
bgneal@1 159 display_val = pos_map[pos];
bgneal@1 160 }
bgneal@0 161
bgneal@0 162 private:
bgneal@0 163 std::string rotor_name;
bgneal@0 164 std::string wiring_str;
bgneal@0 165 int ring_setting;
bgneal@0 166 int pos;
bgneal@1 167 char display_val;
bgneal@1 168 alpha_int_array entry_map;
bgneal@1 169 alpha_int_array exit_map;
bgneal@1 170 alpha_int_array display_map;
bgneal@1 171 alpha_int_array pos_map;
bgneal@1 172 alpha_bool_array step_map;
bgneal@0 173 };
bgneal@0 174 }
bgneal@0 175
bgneal@0 176 #endif