bgneal@3: #ifndef CPP_ENIGMA_PLUGBOARD_H
bgneal@3: #define CPP_ENIGMA_PLUGBOARD_H
bgneal@3: // Copyright (C) 2012 by Brian Neal.
bgneal@3: // This file is part of Cpp-Enigma, the Enigma Machine simulation.
bgneal@3: // Cpp-Enigma is released under the MIT License (see License.txt).
bgneal@3: //
bgneal@3: // plugboard.h - This file contains the plugboard class.
bgneal@3: 
bgneal@3: #include <vector>
bgneal@3: #include <utility>
bgneal@3: #include <string>
bgneal@3: #include <cstddef>
bgneal@3: #include "enigma_types.h"
bgneal@3: 
bgneal@3: namespace enigma
bgneal@3: {
bgneal@3:    class plugboard_error : public enigma_error
bgneal@3:    {
bgneal@3:    public:
bgneal@3:       explicit plugboard_error(const std::string& what_arg)
bgneal@3:        : enigma_error(what_arg)
bgneal@3:       {}
bgneal@3:    };
bgneal@3: 
bgneal@3:    // The plugboard allows the operator to swap letters before and after the
bgneal@3:    // entry wheel. This is accomplished by connecting cables between pairs of
bgneal@3:    // plugs that are marked with letters (Heer & Luftwaffe models) or numbers
bgneal@3:    // (Kriegsmarine). Ten cables were issued with each machine; thus up to 10 of
bgneal@3:    // these swappings could be used as part of a machine setup.
bgneal@3: 
bgneal@3:    // Each cable swaps both the input and output signals. Thus if A is connected
bgneal@3:    // to B, A crosses to B in the keyboard to entry wheel direction and also in
bgneal@3:    // the reverse entry wheel to lamp direction.
bgneal@3: 
bgneal@3:    class plugboard
bgneal@3:    {
bgneal@3:    public:
bgneal@3:       const static std::size_t max_pairs = 10;
bgneal@3: 
bgneal@3:       typedef std::vector<std::pair<int, int>> pair_vector;
bgneal@3: 
bgneal@3:       // Construct a plugboard with no connections:
bgneal@3:       plugboard();
bgneal@3: 
bgneal@3:       // Construct from a vector of integer pairs that describe the
bgneal@3:       // connections. Each integer must be between [0-25], and the
bgneal@3:       // vector can have no more than max_pairs pairs. Each plug should
bgneal@3:       // be present at most once. A plugboard_error will be thrown if
bgneal@3:       // the pair_vector is invalid.
bgneal@3: 
bgneal@3:       explicit plugboard(const pair_vector& pairs);
bgneal@3: 
bgneal@3:       // Configure the plugboard according to a settings string as you may
bgneal@3:       // find on a key sheet.
bgneal@3:       //
bgneal@3:       // Two syntaxes are supported, the Heer/Luftwaffe and Kriegsmarine styles:
bgneal@3:       //
bgneal@3:       // In the Heer syntax, the settings are given as a string of
bgneal@3:       // alphabetic pairs. For example: 'PO ML IU KJ NH YT GB VF RE DC'
bgneal@3:       //
bgneal@3:       // In the Kriegsmarine syntax, the settings are given as a string of number
bgneal@3:       // pairs, separated by a '/'. Note that the numbering uses 1-26, inclusive.
bgneal@3:       // For example: '18/26 17/4 21/6 3/16 19/14 22/7 8/1 12/25 5/9 10/15'
bgneal@3:       //
bgneal@3:       // To specify no plugboard connections, settings can be an empty string.
bgneal@3:       //
bgneal@3:       // A PlugboardError will be raised if the settings string is invalid, or if
bgneal@3:       // it contains more than max_pairs pairs. Each plug should be present at
bgneal@3:       // most once in the settings string.
bgneal@3: 
bgneal@3:       explicit plugboard(const std::string& settings);
bgneal@3: 
bgneal@3:       // Return the current settings as a vector of pairs:
bgneal@3:       pair_vector get_pairs() const;
bgneal@3: 
bgneal@3:       // Return the current settings as a string in Heer (army) format:
bgneal@3:       std::string army_str() const;
bgneal@3: 
bgneal@3:       // Return the current settings as a string in Kriegsmarine (navy) format:
bgneal@3:       std::string navy_str() const;
bgneal@3: 
bgneal@3:       // Simulate a signal entering the plugboard on wire n, where n must be
bgneal@3:       // an integer between 0 and 25.
bgneal@3:       //
bgneal@3:       // Returns the wire number of the output signal (0-25).
bgneal@3:       //
bgneal@3:       // Note that since the plugboard always crosses pairs of wires, it doesn't
bgneal@3:       // matter what direction (keyboard -> entry wheel or vice versa) the signal
bgneal@3:       // is coming from.
bgneal@3: 
bgneal@3:       int signal(int n) const
bgneal@3:       {
bgneal@3:          return wiring_map[n];
bgneal@3:       }
bgneal@3: 
bgneal@3:       //
bgneal@3:       // Functions to support hill-climbing:
bgneal@3:       //
bgneal@3: 
bgneal@3:       // Return the internal state of the wiring:
bgneal@3:       alpha_int_array get_wiring() const
bgneal@3:       {
bgneal@3:          return wiring_map;
bgneal@3:       }
bgneal@3: 
bgneal@3:       // Sets the internal state of the wiring:
bgneal@3:       void set_wiring(const alpha_int_array& wiring)
bgneal@3:       {
bgneal@3:          wiring_map = wiring;
bgneal@3:       }
bgneal@3: 
bgneal@3:       // Returns true if connection n has a cable attached to it.
bgneal@3:       // 0 <= n < 26
bgneal@3:       bool is_wired(int n) const
bgneal@3:       {
bgneal@3:          return wiring_map[n] != n;
bgneal@3:       }
bgneal@3: 
bgneal@3:       // Returns true if connection n has no cable attached to it.
bgneal@3:       // 0 <= n < 26
bgneal@3:       bool is_free(int n) const
bgneal@3:       {
bgneal@3:          return wiring_map[n] == n;
bgneal@3:       }
bgneal@3: 
bgneal@3:       // Removes cable from plug number n [0-25].
bgneal@3:       void disconnect(int n)
bgneal@3:       {
bgneal@3:          const int x = wiring_map[n];
bgneal@3:          wiring_map[x] = x;
bgneal@3:          wiring_map[n] = n;
bgneal@3:       }
bgneal@3: 
bgneal@3:       // Connects plug x to plug y, removing any existing connection first.
bgneal@3:       // x & y must be in [0-25].
bgneal@3:       void connect(int x, int y)
bgneal@3:       {
bgneal@3:          // disconnect any existing connections
bgneal@3:          const int m = wiring_map[x];
bgneal@3:          const int n = wiring_map[y];
bgneal@3:          wiring_map[m] = m;
bgneal@3:          wiring_map[n] = n;
bgneal@3: 
bgneal@3:          wiring_map[x] = y;
bgneal@3:          wiring_map[y] = x;
bgneal@3:       }
bgneal@3: 
bgneal@3:       // Returns true if plug x is connected to plug y.
bgneal@3:       // x & y must be in [0-25].
bgneal@3:       bool is_connected(int x, int y)
bgneal@3:       {
bgneal@3:          return wiring_map[x] == y && wiring_map[y] == x;
bgneal@3:       }
bgneal@3: 
bgneal@10:       // Unplugs all cables
bgneal@10:       void unplug_all()
bgneal@10:       {
bgneal@10:          for (auto i = 0U; i < wiring_map.size(); ++i)
bgneal@10:          {
bgneal@10:             wiring_map[i] = i;
bgneal@10:          }
bgneal@10:       }
bgneal@10: 
bgneal@3:    private:
bgneal@3:       alpha_int_array wiring_map;
bgneal@3: 
bgneal@3:       // common constructor code:
bgneal@3:       void construct_wiring(const pair_vector& pairs);
bgneal@3:    };
bgneal@3: 
bgneal@3: 
bgneal@3:    // This class can be used to save & restore the state of a plugboard
bgneal@3:    // in RAII style:
bgneal@3: 
bgneal@3:    class plugboard_state_saver
bgneal@3:    {
bgneal@3:    public:
bgneal@3:       explicit plugboard_state_saver(plugboard& pb)
bgneal@3:        : pb(pb)
bgneal@3:       {
bgneal@3:          state = pb.get_wiring();
bgneal@3:       }
bgneal@3: 
bgneal@3:       ~plugboard_state_saver()
bgneal@3:       {
bgneal@3:          pb.set_wiring(state);
bgneal@3:       }
bgneal@3: 
bgneal@3:       // disable copying & assignment
bgneal@3:       plugboard_state_saver(const plugboard_state_saver&) = delete;
bgneal@3:       plugboard_state_saver& operator=(const plugboard_state_saver&) = delete;
bgneal@3: 
bgneal@3:    private:
bgneal@3:       plugboard& pb;
bgneal@3:       alpha_int_array state;
bgneal@3:    };
bgneal@3: 
bgneal@3: }
bgneal@3: 
bgneal@3: #endif