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