diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/machine.h	Sun Jun 24 18:39:05 2012 -0500
@@ -0,0 +1,175 @@
+#ifndef CPP_ENIGMA_MACHINE_H
+#define CPP_ENIGMA_MACHINE_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).
+//
+// machine.h - This file contains the main Enigma machine class.
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <cassert>
+#include "enigma_types.h"
+#include "rotor.h"
+#include "plugboard.h"
+
+namespace enigma
+{
+   typedef std::vector<std::unique_ptr<rotor>> rotor_vector;
+
+   class enigma_machine_error : public enigma_error
+   {
+   public:
+      explicit enigma_machine_error(const std::string& what_arg)
+       : enigma_error(what_arg)
+      {}
+   };
+
+   class enigma_machine
+   {
+   public:
+      // construct an Enigma machine from component parts:
+      enigma_machine(rotor_vector rv,
+                     std::unique_ptr<rotor> reflector,
+                     const plugboard& pb);
+
+      // construct an Enigma machine with a default plugboard (no cables connected):
+      enigma_machine(rotor_vector rv,
+                     std::unique_ptr<rotor> reflector);
+
+      // key-sheet style constructors:
+      enigma_machine(const std::vector<std::string>& rotor_types,
+                     const std::vector<int>& ring_settings,
+                     const std::string& reflector_name = "B",
+                     const std::string& plugboard_settings = "");
+
+      // set the rotor display (starting position) - 3 rotor version
+      void set_display(char left, char mid, char right)
+      {
+         assert(rotors.size() == 3);
+
+         rotors[0]->set_display(left);
+         rotors[1]->set_display(mid);
+         rotors[2]->set_display(right);
+      }
+
+      // set the rotor display (starting position) - 4 rotor version
+      void set_display(char c0, char c1, char c2, char c3)
+      {
+         assert(rotors.size() == 4);
+
+         rotors[0]->set_display(c0);
+         rotors[1]->set_display(c1);
+         rotors[2]->set_display(c2);
+         rotors[3]->set_display(c3);
+      }
+
+      // return the rotor display (starting position) as a string
+      std::string get_display() const
+      {
+         std::string result;
+         for (const auto& r : rotors)
+         {
+            result += r->get_display();
+         }
+         return result;
+      }
+
+      // simulate front panel key press; returns the lamp character that is lit
+      char key_press(char c)
+      {
+         step_rotors();
+         return electric_signal(c - 'A') + 'A';
+      }
+
+      // Process a buffer of text of length n, placing the result in an output buffer.
+      void process_text(const char* input, char* output, std::size_t n)
+      {
+         for (std::size_t i = 0; i < n; ++i)
+         {
+            *output++ = key_press(*input++);
+         }
+      }
+
+      std::string process_text(const std::string& input)
+      {
+         std::string result;
+         result.reserve(input.size());
+
+         for (const auto& c : input)
+         {
+            result += key_press(c);
+         }
+         return result;
+      }
+
+      // for access to the plugboard for hill-climbing, etc
+      plugboard& get_plugboard() { return pb; }
+
+   private:
+      rotor_vector rotors;
+      std::unique_ptr<rotor> reflector;
+      plugboard pb;
+      rotor* r_rotor;      // rightmost rotor
+      rotor* m_rotor;      // 2nd to right rotor
+      rotor* l_rotor;      // 3rd to right rotor
+
+      void rotor_count_check();
+
+      void step_rotors()
+      {
+         // The right-most rotor's right-side ratchet is always over a pawl, and
+         // it has no neighbor to the right, so it always rotates.
+         //
+         // The middle rotor will rotate if either:
+         //   1) The right-most rotor's left side notch is over the 2nd pawl
+         //       or
+         //   2) It has a left-side notch over the 3rd pawl
+         //
+         // The third rotor (from the right) will rotate only if the middle rotor
+         // has a left-side notch over the 3rd pawl.
+         //
+         // Kriegsmarine model M4 has 4 rotors, but the 4th rotor (the leftmost)
+         // does not rotate (they did not add a 4th pawl to the mechanism).
+
+         const bool l_rotate = m_rotor->notch_over_pawl();
+         const bool m_rotate = l_rotate || r_rotor->notch_over_pawl();
+
+         r_rotor->rotate();
+         if (m_rotate)
+         {
+            m_rotor->rotate();
+         }
+         if (l_rotate)
+         {
+            l_rotor->rotate();
+         }
+      }
+
+      // Simulate running an electric signal through the machine in order to
+      // perform an encrypt or decrypt operation
+      // signal_num - the wire (0-25) that the simulated current occurs on
+      // Returns a lamp number to light (an integer 0-25).
+      int electric_signal(int signal_num)
+      {
+         int pos = pb.signal(signal_num);
+
+         for (auto r = rotors.rbegin(); r != rotors.rend(); ++r)
+         {
+            pos = (*r)->signal_in(pos);
+         }
+
+         pos = reflector->signal_in(pos);
+
+         for (const auto& r : rotors)
+         {
+            pos = r->signal_out(pos);
+         }
+
+         return pb.signal(pos);
+      }
+   };
+}
+
+#endif