changeset 16:2ce2e8c5a5be

Decided to get rid of the numeric rotor display for now. Updated comments to make distinction between Wehrmacht & Heer.
author Brian Neal <bgneal@gmail.com>
date Sun, 27 May 2012 19:41:59 -0500
parents 7ea7da689dbd
children ce3840115214
files enigma/__init__.py enigma/machine.py enigma/plugboard.py enigma/rotors/data.py enigma/rotors/factory.py enigma/rotors/rotor.py enigma/tests/test_rotor.py
diffstat 7 files changed, 52 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/enigma/__init__.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/__init__.py	Sun May 27 19:41:59 2012 -0500
@@ -2,11 +2,11 @@
 # This file is part of Py-Enigma, the Enigma Machine simulation.
 # Py-Enigma is released under the MIT License (see License.txt).
 
-"""This package is a simulation of the wartime Enigma Machines used by the
-German military branches. These include the army (Wehrmacht), air force
-(Luftwaffe), and navy (Kriegsmarine). For our purposes, the Wehrmacht and
-Luftwaffe versions are identical. The Kriegsmarine models M3 and M4 can be
-simulated by configuring an EnigmaMachine object with the suitable rotors.
+"""This package is a simulation of the Enigma Machines used by the German armed
+forces (Wehrmacht) during World War II. These include the army (Heer), air force
+(Luftwaffe), and navy (Kriegsmarine). For our purposes, the Heer and Luftwaffe
+versions are identical. The Kriegsmarine models M3 and M4 can be simulated by
+configuring an EnigmaMachine object with the suitable rotors and reflectors.
 
 For a list of the rotors and reflectors we simulate, see the module rotors.data.
 
--- a/enigma/machine.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/machine.py	Sun May 27 19:41:59 2012 -0500
@@ -31,7 +31,7 @@
         the left-most rotor, and the last rotor is the right-most (from the
         operator's perspective sitting at the machine).
 
-        reflector - a rotor object to represent the reflector
+        reflector - a rotor object to represent the reflector (UKW)
 
         plugboard - a plugboard object to represent the state of the plugboard
 
--- a/enigma/plugboard.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/plugboard.py	Sun May 27 19:41:59 2012 -0500
@@ -2,14 +2,17 @@
 # This file is part of Py-Enigma, the Enigma Machine simulation.
 # Py-Enigma is released under the MIT License (see License.txt).
 
-"""Contains the Plugboard class for simulating the plugboard component."""
+"""Contains the Plugboard class for simulating the plugboard (Steckerbrett)
+component of the Enigma Machine.
+
+"""
 
 import collections
 from itertools import chain
 import string
 
 
-# On Wehrmacht models, the plugs are labeled with upper case letters
+# On Heer & Luftwaffe (?) models, the plugs are labeled with upper case letters
 WEHRMACHT_LABELS = string.ascii_uppercase
 
 # The number of plugboard cables supplied with a machine:
@@ -23,9 +26,9 @@
 class Plugboard:
     """The plugboard allows the operator to swap letters before and after the 
     entry wheel. This is accomplished by connecting cables between pairs of
-    plugs that are marked with letters (Wehrmacht) or numbers (Kriegsmarine).
-    Ten cables were issued with each machine; thus up to 10 of these swappings
-    could be used as part of a machine setup.
+    plugs that are marked with letters (Heer & Luftwaffe models) or numbers
+    (Kriegsmarine). Ten cables were issued with each machine; thus up to 10 of
+    these swappings could be used as part of a machine setup.
 
     Each cable swaps both the input and output signals. Thus if A is connected
     to B, A crosses to B in the keyboard to entry wheel direction and also in
@@ -37,7 +40,7 @@
         pairs, or None.
 
         A value of None or an empty list/tuple indicates no plugboard
-        connections are to be used (a straight mapping).
+        connections are to be used (i.e. a straight mapping).
 
         Otherwise wiring_pairs must be an iterable of integer pairs, where each
         integer is between 0-25, inclusive. At most 10 such pairs can be
@@ -80,9 +83,9 @@
         """Configure the plugboard according to a settings string as you may
         find on a key sheet.
 
-        Two syntaxes are supported, the Wehrmacht and Kriegsmarine styles:
+        Two syntaxes are supported, the Heer/Luftwaffe and Kriegsmarine styles:
 
-        In the Wehrmacht syntax, the settings are given as a string of
+        In the Heer syntax, the settings are given as a string of
         alphabetic pairs. For example: 'PO ML IU KJ NH YT GB VF RE DC' 
 
         In the Kriegsmarine syntax, the settings are given as a string of number
@@ -115,7 +118,7 @@
 
                 wiring_pairs.append((m - 1, n - 1))
         else:
-            # Wehrmacht syntax
+            # Heer/Luftwaffe syntax
             pairs = settings.upper().split()
 
             for p in pairs:
--- a/enigma/rotors/data.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/rotors/data.py	Sun May 27 19:41:59 2012 -0500
@@ -11,12 +11,12 @@
 # website: "Techical Details of the Engigma Machine"
 # http://users.telenet.be/d.rijmenants/en/enigmatech.htm
 #
-# Rotors I-V were used by the Wehrmacht, Luftwaffe, and Kriegsmarine. The
+# Rotors I-V were used by the Heer, Luftwaffe, and Kriegsmarine. The
 # Kriegsmarine added rotors VI-VIII to the M3 model, and added Beta & Gamma to
 # the M4 model (used with thin reflectors only). Note that Beta & Gamma rotors
 # did not rotate.
 #
-# The Wehrmacht, Luftwaffe, & Kriegsmarine M3 machines used reflectors B & C,
+# The Heer, Luftwaffe, & Kriegsmarine M3 machines used reflectors B & C,
 # while the Kriegsmarine M4 used thin reflectors B & C.
 #
 
--- a/enigma/rotors/factory.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/rotors/factory.py	Sun May 27 19:41:59 2012 -0500
@@ -9,13 +9,12 @@
 from .data import ROTORS, REFLECTORS
 
 
-def create_rotor(model, ring_setting=0, alpha_labels=True):
+def create_rotor(model, ring_setting=0):
     """Factory function to create and return a rotor of the given model name."""
 
     if model in ROTORS:
         data = ROTORS[model]
-        return Rotor(model, data['wiring'], ring_setting, data['stepping'],
-                alpha_labels)
+        return Rotor(model, data['wiring'], ring_setting, data['stepping'])
 
     raise RotorError("Unknown rotor type: %s" % model)
 
--- a/enigma/rotors/rotor.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/rotors/rotor.py	Sun May 27 19:41:59 2012 -0500
@@ -11,11 +11,10 @@
 
 
 ALPHA_LABELS = string.ascii_uppercase
-NUMERIC_LABELS = ['{:02d}'.format(n) for n in range(1, 27)]
 
 # In specifying a wiring for a rotor, every letter must be included exactly
 # once. This variable helps us enforce that:
-WIRING_FREQ_SET = set((letter, 1) for letter in string.ascii_uppercase)
+WIRING_FREQ_SET = set((letter, 1) for letter in ALPHA_LABELS)
 
 
 class Rotor:
@@ -36,12 +35,12 @@
     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 axle, 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 axle.
+    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
@@ -60,8 +59,7 @@
     
     """
 
-    def __init__(self, model_name, wiring, ring_setting=0, stepping=None,
-            alpha_labels=True):
+    def __init__(self, model_name, wiring, ring_setting=0, stepping=None):
         """Establish rotor characteristics:
 
         model_name - e.g. "I", "II", "III", "Beta", "Gamma"
@@ -69,8 +67,8 @@
         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 & Luftwaffe Enigma type I
-        rotor the mapping is "EKMFLGDQVZNTOWYHXUSPAIBRCJ".
+        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.
@@ -85,17 +83,19 @@
         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 to the left when the next
-        key is depressed.
+        will allow the pawl to push up on the rotor *and* the rotor to the left
+        when the next key is depressed.
 
-        alpha_labels - when True, the letters A-Z are used for the rotor ring
-        labels. If False, numeric string labels (01-26) are used.
+        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. In the future we may allow either
+        display.
 
         """
         self.name = model_name
         self.wiring_str = wiring.upper()
         self.ring_setting = ring_setting
-        self.alpha_labels = alpha_labels
         self.pos = 0
         self.rotations = 0
 
@@ -105,7 +105,7 @@
 
         # check wiring format; must contain A-Z
         for c in self.wiring_str:
-            if c not in string.ascii_uppercase:
+            if c not in ALPHA_LABELS:
                 raise RotorError("invalid wiring: %s" % wiring)
 
         # check wiring format; ensure every letter appears exactly once
@@ -126,7 +126,7 @@
             self.exit_map[v] = i
 
         # configure ring labels
-        self.ring_labels = ALPHA_LABELS if alpha_labels else NUMERIC_LABELS
+        self.ring_labels = ALPHA_LABELS
 
         # for mapping window display values to indices
         self.display_map = dict(zip(self.ring_labels, range(26)))
@@ -149,13 +149,9 @@
         rotates the pins and contacts accordingly.
 
         A value of 'A' for example puts the rotor in position 0, assuming an
-        internal ring setting of 0 and alphabetic labels.
+        internal ring setting of 0.
 
-        If the rotor is using alphabetic ring labels, val must be a string in
-        'A' - 'Z'.
-
-        If the rotor is not using alphabetic ring labels, val must be a string
-        of the form '01' - '26'.
+        The parameter val must be a string in 'A' - 'Z'.
 
         Setting the display resets the internal rotation counter to 0.
 
--- a/enigma/tests/test_rotor.py	Sun May 27 18:51:40 2012 -0500
+++ b/enigma/tests/test_rotor.py	Sun May 27 19:41:59 2012 -0500
@@ -8,7 +8,7 @@
 import collections
 import string
 
-from ..rotors.rotor import Rotor, ALPHA_LABELS, NUMERIC_LABELS
+from ..rotors.rotor import Rotor, ALPHA_LABELS
 from ..rotors import RotorError
 
 
@@ -39,41 +39,27 @@
 
     def test_bad_stepping(self):
 
-        for alpha in True, False:
-            self.assertRaises(RotorError, Rotor, 'I', WIRING,
-                    alpha_labels=alpha, stepping="0")
-            self.assertRaises(RotorError, Rotor, 'I', WIRING,
-                    alpha_labels=alpha, stepping="A0")
-            self.assertRaises(RotorError, Rotor, 'I', WIRING,
-                    alpha_labels=alpha, stepping=[1])
-            self.assertRaises(RotorError, Rotor, 'I', WIRING,
-                    alpha_labels=alpha, stepping=['A', '%', '14'])
-            self.assertRaises(RotorError, Rotor, 'I', WIRING,
-                    alpha_labels=alpha, stepping=('A', '%', '14'))
+        self.assertRaises(RotorError, Rotor, 'I', WIRING, stepping="0")
+        self.assertRaises(RotorError, Rotor, 'I', WIRING, stepping="A0")
+        self.assertRaises(RotorError, Rotor, 'I', WIRING, stepping=[1])
+        self.assertRaises(RotorError, Rotor, 'I', WIRING, stepping=['A', '%', '14'])
+        self.assertRaises(RotorError, Rotor, 'I', WIRING, stepping=('A', '%', '14'))
     
-    def test_alpha_display(self):
+    def test_display(self):
 
         for r in range(26):
-            rotor = Rotor('I', WIRING, ring_setting=r, alpha_labels=True)
+            rotor = Rotor('I', WIRING, ring_setting=r)
             for s in ALPHA_LABELS:
                 rotor.set_display(s)
                 self.assertEqual(s, rotor.get_display())
 
-    def test_numeric_display(self):
-
-        for r in range(26):
-            rotor = Rotor('I', WIRING, ring_setting=r, alpha_labels=False)
-            for s in NUMERIC_LABELS:
-                rotor.set_display(s)
-                self.assertEqual(s, rotor.get_display())
-
     def test_wiring(self):
         """Loop through all ring settings & rotor positions and test the
         wiring.
         
         """
         for r in range(26):
-            rotor = Rotor('I', WIRING, ring_setting=r, alpha_labels=True)
+            rotor = Rotor('I', WIRING, ring_setting=r)
 
             for n, d in enumerate(ALPHA_LABELS):
                 rotor.set_display(d)