bgneal@0
|
1 // Copyright (C) 2012 by Brian Neal.
|
bgneal@0
|
2 // This file is part of Cpp-Enigma, the Enigma Machine simulation.
|
bgneal@0
|
3 // Cpp-Enigma is released under the MIT License (see License.txt).
|
bgneal@0
|
4 //
|
bgneal@0
|
5 // rotor.cpp - Implementation file for the rotor class.
|
bgneal@0
|
6
|
bgneal@0
|
7 #include <set>
|
bgneal@0
|
8 #include <array>
|
bgneal@0
|
9 #include <algorithm>
|
bgneal@0
|
10 #include "rotor.h"
|
bgneal@1
|
11 #include "enigma_utils.h"
|
bgneal@0
|
12
|
bgneal@0
|
13 using namespace enigma;
|
bgneal@0
|
14
|
bgneal@1
|
15 ////////////////////////////////////////////////////////////////////////////////
|
bgneal@1
|
16
|
bgneal@0
|
17 namespace
|
bgneal@0
|
18 {
|
bgneal@0
|
19 const char* const ucase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
bgneal@1
|
20
|
bgneal@1
|
21 // Turned into a function to avoid translation unit initialization order problems.
|
bgneal@1
|
22 const std::set<char>& get_ucase_set()
|
bgneal@1
|
23 {
|
bgneal@1
|
24 static const std::set<char> the_set(ucase, ucase + 26);
|
bgneal@1
|
25 return the_set;
|
bgneal@1
|
26 }
|
bgneal@0
|
27 }
|
bgneal@0
|
28
|
bgneal@1
|
29 ////////////////////////////////////////////////////////////////////////////////
|
bgneal@1
|
30
|
bgneal@0
|
31 rotor::rotor(const char* name, const char* wiring, int ring_setting, const char* stepping)
|
bgneal@1
|
32 : rotor_name{name},
|
bgneal@1
|
33 wiring_str{wiring},
|
bgneal@1
|
34 ring_setting{ring_setting},
|
bgneal@1
|
35 pos{0},
|
bgneal@1
|
36 display_val{'A'},
|
bgneal@1
|
37 entry_map(),
|
bgneal@1
|
38 exit_map(),
|
bgneal@1
|
39 display_map(),
|
bgneal@1
|
40 pos_map(),
|
bgneal@1
|
41 step_map()
|
bgneal@0
|
42 {
|
bgneal@0
|
43 // check wiring length
|
bgneal@0
|
44 if (wiring_str.size() != 26)
|
bgneal@0
|
45 {
|
bgneal@0
|
46 throw rotor_error("invalid wiring length");
|
bgneal@0
|
47 }
|
bgneal@0
|
48
|
bgneal@0
|
49 // ensure wiring contains only uppercase letters & every char must appear
|
bgneal@0
|
50 // exactly once:
|
bgneal@1
|
51 const std::set<char>& ucase_set(get_ucase_set());
|
bgneal@0
|
52
|
bgneal@1
|
53 // g++ 4.6.3 warns about missing braces unless we double them up, below:
|
bgneal@1
|
54 std::array<int, 26> letter_counts = {{ 0 }};
|
bgneal@0
|
55 for (int i = 0; i < 26; ++i)
|
bgneal@0
|
56 {
|
bgneal@1
|
57 const char c{wiring_str[i]};
|
bgneal@0
|
58 if (ucase_set.find(c) == ucase_set.end())
|
bgneal@0
|
59 {
|
bgneal@0
|
60 throw rotor_error("invalid wiring");
|
bgneal@0
|
61 }
|
bgneal@0
|
62 ++letter_counts[c - 'A'];
|
bgneal@0
|
63 }
|
bgneal@0
|
64
|
bgneal@0
|
65 if (std::find_if(letter_counts.begin(),
|
bgneal@0
|
66 letter_counts.end(),
|
bgneal@0
|
67 [](int n) { return n != 1; }) != letter_counts.end())
|
bgneal@0
|
68 {
|
bgneal@0
|
69 throw rotor_error("invalid wiring; duplicate letter");
|
bgneal@0
|
70 }
|
bgneal@1
|
71
|
bgneal@1
|
72 if (ring_setting < 0 || ring_setting >= 26)
|
bgneal@1
|
73 {
|
bgneal@1
|
74 throw rotor_error("invalid ring setting");
|
bgneal@1
|
75 }
|
bgneal@1
|
76 set_ring_setting(ring_setting);
|
bgneal@1
|
77
|
bgneal@1
|
78 // Initialize our two arrays that describe the internal wiring. Arrays are used
|
bgneal@1
|
79 // to do fast lookup from both entry (from the right) and exit (from the
|
bgneal@1
|
80 // left).
|
bgneal@1
|
81
|
bgneal@1
|
82 for (int i = 0; i < 26; ++i)
|
bgneal@1
|
83 {
|
bgneal@1
|
84 const int v = wiring_str[i] - 'A';
|
bgneal@1
|
85 entry_map[i] = v;
|
bgneal@1
|
86 exit_map[v] = i;
|
bgneal@1
|
87 }
|
bgneal@1
|
88
|
bgneal@1
|
89 // Build a lookup table that tells us when the pawls are allowed to step.
|
bgneal@1
|
90 // The index to this array is the current display letter [A-Z] - 'A'.
|
bgneal@1
|
91
|
bgneal@1
|
92 if (stepping != nullptr)
|
bgneal@1
|
93 {
|
bgneal@2
|
94 for (char c = *stepping; *stepping != '\0'; c = *(++stepping))
|
bgneal@1
|
95 {
|
bgneal@1
|
96 if (ucase_set.find(c) != ucase_set.end())
|
bgneal@1
|
97 {
|
bgneal@1
|
98 step_map[c - 'A'] = true;
|
bgneal@1
|
99 }
|
bgneal@1
|
100 else
|
bgneal@1
|
101 {
|
bgneal@1
|
102 throw rotor_error("invalid stepping");
|
bgneal@1
|
103 }
|
bgneal@1
|
104 }
|
bgneal@1
|
105 }
|
bgneal@1
|
106
|
bgneal@1
|
107 // set initial position
|
bgneal@1
|
108 set_display('A');
|
bgneal@0
|
109 }
|
bgneal@1
|
110
|
bgneal@1
|
111 ////////////////////////////////////////////////////////////////////////////////
|
bgneal@1
|
112
|
bgneal@1
|
113 void rotor::set_ring_setting(int n)
|
bgneal@1
|
114 {
|
bgneal@1
|
115 ring_setting = n;
|
bgneal@1
|
116
|
bgneal@1
|
117 // Build a mapping from window display values to positions
|
bgneal@1
|
118 // and a reverse mapping of position to display value:
|
bgneal@1
|
119 for (int i = 0; i < 26; ++i)
|
bgneal@1
|
120 {
|
bgneal@1
|
121 const int n = alpha_mod(i - ring_setting);
|
bgneal@1
|
122 display_map[i] = n;
|
bgneal@1
|
123 pos_map[n] = i + 'A';
|
bgneal@1
|
124 }
|
bgneal@1
|
125 }
|