Mercurial > public > cpp-enigma
changeset 0:74ebb2150658
Initial commit. Working on the rotor class.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Thu, 21 Jun 2012 21:05:26 -0500 |
parents | |
children | 1459e74fda3f |
files | .hgignore SConstruct enigma/SConscript enigma/enigma_types.h enigma/rotor.cpp enigma/rotor.h |
diffstat | 6 files changed, 235 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Thu Jun 21 21:05:26 2012 -0500 @@ -0,0 +1,4 @@ +syntax: glob +build +.*.swp +.sconsign.dblite
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SConstruct Thu Jun 21 21:05:26 2012 -0500 @@ -0,0 +1,5 @@ +env = Environment( + CXXFLAGS='-std=c++0x -Wall -Wextra -pedantic', +) + +SConscript(['enigma/SConscript'], exports='env', variant_dir='build', duplicate=0)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/SConscript Thu Jun 21 21:05:26 2012 -0500 @@ -0,0 +1,7 @@ +Import('env') + +sources = Split(""" + rotor.cpp + """) + +env.StaticLibrary('enigma', sources)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/enigma_types.h Thu Jun 21 21:05:26 2012 -0500 @@ -0,0 +1,33 @@ +#ifndef CPP_ENIGMA_ENIGMA_TYPES_H +#define CPP_ENIGMA_ENIGMA_TYPES_H +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// enigma_types.h - This file contains common types used throughout Cpp-Enigma. + +#include <exception> +#include <string> + +namespace enigma +{ + class enigma_error : public std::exception + { + public: + explicit enigma_error(const std::string& what_arg) + : what_arg(what_arg) + {} + + virtual ~enigma_error() throw() {} + + virtual const char* what() const throw() + { + return what_arg.c_str(); + } + + private: + std::string what_arg; + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/rotor.cpp Thu Jun 21 21:05:26 2012 -0500 @@ -0,0 +1,54 @@ +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// rotor.cpp - Implementation file for the rotor class. + +#include <set> +#include <array> +#include <algorithm> +#include "rotor.h" + +using namespace enigma; + +namespace +{ + const char* const ucase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const std::set<char> ucase_set(ucase, ucase + 26); +} + +rotor::rotor(const char* name, const char* wiring, int ring_setting, const char* stepping) + : rotor_name(name), + wiring_str(wiring), + ring_setting(ring_setting), + pos(0), + rotations(0) +{ + // check wiring length + if (wiring_str.size() != 26) + { + throw rotor_error("invalid wiring length"); + } + + // ensure wiring contains only uppercase letters & every char must appear + // exactly once: + + std::array<int, 26> letter_counts {{ 0 }}; + for (int i = 0; i < 26; ++i) + { + const char c(wiring_str[i]); + + if (ucase_set.find(c) == ucase_set.end()) + { + throw rotor_error("invalid wiring"); + } + ++letter_counts[c - 'A']; + } + + if (std::find_if(letter_counts.begin(), + letter_counts.end(), + [](int n) { return n != 1; }) != letter_counts.end()) + { + throw rotor_error("invalid wiring; duplicate letter"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/enigma/rotor.h Thu Jun 21 21:05:26 2012 -0500 @@ -0,0 +1,132 @@ +#ifndef CPP_ENIGMA_ROTOR_H +#define CPP_ENIGMA_ROTOR_H +// Copyright (C) 2012 by Brian Neal. +// This file is part of Cpp-Enigma, the Enigma Machine simulation. +// Cpp-Enigma is released under the MIT License (see License.txt). +// +// rotor.h - This file contains the rotor class. + +#include <string> +#include "enigma_types.h" + + +namespace enigma +{ + class rotor_error : public enigma_error + { + public: + explicit rotor_error(const std::string& what_arg) + : enigma_error(what_arg) + {} + }; + + // The rotor class represents the Enigma Machine rotors (Walzen). + // + // A rotor has 26 circularly arranged pins on the right (entry) side and 26 + // contacts on the left side. Each pin is connected to a single contact by + // internal wiring, thus establishing a substitution cipher. We represent this + // wiring by establishing a mapping from a pin to a contact (and vice versa for + // the return path). Internally we number the pins and contacts from 0-25 in a + // clockwise manner with 0 being the "top". + // + // An alphabetic or numeric ring is fastened to the rotor by the operator. The + // labels of this ring are displayed to the operator through a small window on + // the top panel. The ring can be fixed to the rotor in one of 26 different + // positions; this is called the ring setting (Ringstellung). We will number + // the ring settings from 0-25 where 0 means no offset (e.g. the letter "A" is + // mapped to pin 0 on an alphabetic ring). A ring setting of 1 means the letter + // "B" is mapped to pin 0. + // + // Each rotor can be in one of 26 positions on the spindle, with position 0 + // where pin/contact 0 is being indicated in the operator window. The rotor + // rotates towards the operator by mechanical means during normal operation as + // keys are being pressed during data entry. Position 1 is thus defined to be + // one step from position 0. Likewise, position 25 is the last position before + // another step returns it to position 0, completing 1 trip around the spindle. + // + // Finally, a rotor has a "stepping" or "turnover" parameter. Physically this + // is implemented by putting a notch on the alphabet ring and it controls when + // the rotor will "kick" the rotor to its left, causing the neighbor rotor to + // rotate. Most rotors had one notch, but some Kriegsmarine rotors had 2 + // notches and thus rotated twice as fast. + // + // Note that due to the system of ratchets and pawls, the middle rotor (in a 3 + // rotor Enigma) can "double-step". The middle rotor will advance on the next + // step of the first rotor a second time in a row, if the middle rotor is in + // its own turnover position. + // + // Note that we allow the stepping parameter to be None. This indicates the + // rotor does not rotate. This allows us to model the entry wheel and + // reflectors as stationary rotors. + + class rotor + { + public: + // rotor constructor: + // + // model_name - e.g. "I", "II", "III", "Beta", "Gamma" + // + // wiring - this should be a string of 26 alphabetic characters that + // represents the internal wiring transformation of the signal as it enters + // from the right side. This is the format used in various online + // resources. For example, for the Wehrmacht Enigma type I rotor the + // mapping is "EKMFLGDQVZNTOWYHXUSPAIBRCJ". + // + // ring_setting - this should be an integer from 0-25, inclusive, which + // indicates the Ringstellung. A value of 0 means there is no offset; e.g. + // the letter "A" is fixed to pin 0. A value of 1 means "B" is mapped to + // pin 0. + // + // stepping - this is the stepping or turnover parameter. It should be + // a string such as "Q". This will indicate that when the rotor transitions + // from "Q" to "R" (by observing the operator window), the rotor will "kick" + // the rotor to its left, causing it to rotate. If the rotor has more than one + // notch, a string of length 2 could be used, e.g. "ZM". Another way to think + // of this parameter is that when a character in the stepping string is visible + // in the operator window, a notch is lined up with the pawl on the left side + // of the rotor. This will allow the pawl to push up on the rotor *and* the + // rotor to the left when the next key is depressed. + // + // Note that for purposes of simulation, our rotors will always use + // alphabetic labels A-Z. In reality, the Heer & Luftwaffe devices used + // numbers 01-26, and Kriegsmarine devices used A-Z. Our usage of A-Z is + // simply for simulation convenience. + // display. + + rotor(const char* name, const char* wiring, int ring_setting = 0, const char* stepping = 0); + + // Returns the rotor name: + const std::string& name() const { return rotor_name; } + + // Spin the rotor such that the string val appears in the operator window: + void set_display(const char* val); + + // Returns what is currently being displayed in the operator window: + std::string get_display() const; + + // Simulate a signal entering the rotor from the right at a given pin: + // n must be an integer between 0 and 25. + // Returns the contact number of the output signal (0-25). + int signal_in(int n) const; + + // Simulate a signal entering the rotor from the left at a given contact position n. + // n must be an integer between 0 and 25. + // Returns the pin number of the output signal (0-25). + int signal_out(int n) const; + + // Return true if this rotor has a notch in the stepping position and false otherwise: + bool notch_over_pawl() const; + + // Rotate the rotor forward one step: + void rotate(); + + private: + std::string rotor_name; + std::string wiring_str; + int ring_setting; + int pos; + int rotations; + }; +} + +#endif