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
|