annotate enigma/rotor.cpp @ 16:280facb82b80

Add the ability to change ring settings at the machine level.
author Brian Neal <bgneal@gmail.com>
date Sat, 07 Jul 2012 11:42:31 -0500
parents 713fa2a9ea9a
children
rev   line source
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 }