comparison enigma/machine.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
children db1216d380b3
comparison
equal deleted inserted replaced
3:f4e25e6b76c3 4:2792ca4ffa84
1 #ifndef CPP_ENIGMA_MACHINE_H
2 #define CPP_ENIGMA_MACHINE_H
3 // Copyright (C) 2012 by Brian Neal.
4 // This file is part of Cpp-Enigma, the Enigma Machine simulation.
5 // Cpp-Enigma is released under the MIT License (see License.txt).
6 //
7 // machine.h - This file contains the main Enigma machine class.
8
9 #include <memory>
10 #include <string>
11 #include <vector>
12 #include <cassert>
13 #include "enigma_types.h"
14 #include "rotor.h"
15 #include "plugboard.h"
16
17 namespace enigma
18 {
19 typedef std::vector<std::unique_ptr<rotor>> rotor_vector;
20
21 class enigma_machine_error : public enigma_error
22 {
23 public:
24 explicit enigma_machine_error(const std::string& what_arg)
25 : enigma_error(what_arg)
26 {}
27 };
28
29 class enigma_machine
30 {
31 public:
32 // construct an Enigma machine from component parts:
33 enigma_machine(rotor_vector rv,
34 std::unique_ptr<rotor> reflector,
35 const plugboard& pb);
36
37 // construct an Enigma machine with a default plugboard (no cables connected):
38 enigma_machine(rotor_vector rv,
39 std::unique_ptr<rotor> reflector);
40
41 // key-sheet style constructors:
42 enigma_machine(const std::vector<std::string>& rotor_types,
43 const std::vector<int>& ring_settings,
44 const std::string& reflector_name = "B",
45 const std::string& plugboard_settings = "");
46
47 // set the rotor display (starting position) - 3 rotor version
48 void set_display(char left, char mid, char right)
49 {
50 assert(rotors.size() == 3);
51
52 rotors[0]->set_display(left);
53 rotors[1]->set_display(mid);
54 rotors[2]->set_display(right);
55 }
56
57 // set the rotor display (starting position) - 4 rotor version
58 void set_display(char c0, char c1, char c2, char c3)
59 {
60 assert(rotors.size() == 4);
61
62 rotors[0]->set_display(c0);
63 rotors[1]->set_display(c1);
64 rotors[2]->set_display(c2);
65 rotors[3]->set_display(c3);
66 }
67
68 // return the rotor display (starting position) as a string
69 std::string get_display() const
70 {
71 std::string result;
72 for (const auto& r : rotors)
73 {
74 result += r->get_display();
75 }
76 return result;
77 }
78
79 // simulate front panel key press; returns the lamp character that is lit
80 char key_press(char c)
81 {
82 step_rotors();
83 return electric_signal(c - 'A') + 'A';
84 }
85
86 // Process a buffer of text of length n, placing the result in an output buffer.
87 void process_text(const char* input, char* output, std::size_t n)
88 {
89 for (std::size_t i = 0; i < n; ++i)
90 {
91 *output++ = key_press(*input++);
92 }
93 }
94
95 std::string process_text(const std::string& input)
96 {
97 std::string result;
98 result.reserve(input.size());
99
100 for (const auto& c : input)
101 {
102 result += key_press(c);
103 }
104 return result;
105 }
106
107 // for access to the plugboard for hill-climbing, etc
108 plugboard& get_plugboard() { return pb; }
109
110 private:
111 rotor_vector rotors;
112 std::unique_ptr<rotor> reflector;
113 plugboard pb;
114 rotor* r_rotor; // rightmost rotor
115 rotor* m_rotor; // 2nd to right rotor
116 rotor* l_rotor; // 3rd to right rotor
117
118 void rotor_count_check();
119
120 void step_rotors()
121 {
122 // The right-most rotor's right-side ratchet is always over a pawl, and
123 // it has no neighbor to the right, so it always rotates.
124 //
125 // The middle rotor will rotate if either:
126 // 1) The right-most rotor's left side notch is over the 2nd pawl
127 // or
128 // 2) It has a left-side notch over the 3rd pawl
129 //
130 // The third rotor (from the right) will rotate only if the middle rotor
131 // has a left-side notch over the 3rd pawl.
132 //
133 // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost)
134 // does not rotate (they did not add a 4th pawl to the mechanism).
135
136 const bool l_rotate = m_rotor->notch_over_pawl();
137 const bool m_rotate = l_rotate || r_rotor->notch_over_pawl();
138
139 r_rotor->rotate();
140 if (m_rotate)
141 {
142 m_rotor->rotate();
143 }
144 if (l_rotate)
145 {
146 l_rotor->rotate();
147 }
148 }
149
150 // Simulate running an electric signal through the machine in order to
151 // perform an encrypt or decrypt operation
152 // signal_num - the wire (0-25) that the simulated current occurs on
153 // Returns a lamp number to light (an integer 0-25).
154 int electric_signal(int signal_num)
155 {
156 int pos = pb.signal(signal_num);
157
158 for (auto r = rotors.rbegin(); r != rotors.rend(); ++r)
159 {
160 pos = (*r)->signal_in(pos);
161 }
162
163 pos = reflector->signal_in(pos);
164
165 for (const auto& r : rotors)
166 {
167 pos = r->signal_out(pos);
168 }
169
170 return pb.signal(pos);
171 }
172 };
173 }
174
175 #endif