changeset 4:328790e5bc5d

Introduce shortcut notation for drum lugs.
author Brian Neal <bgneal@gmail.com>
date Sun, 02 Jun 2013 13:57:45 -0500
parents 0a2a066fb18c
children c1db39916ec9
files m209/converter.py m209/drum.py m209/tests/test_drum.py
diffstat 3 files changed, 48 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/m209/converter.py	Wed May 29 22:04:12 2013 -0500
+++ b/m209/converter.py	Sun Jun 02 13:57:45 2013 -0500
@@ -62,7 +62,7 @@
         If lug_list is passed as a string, it is assumed to be in key list
         format. That is, it must consist of at most 27 whitespace separated pairs
         of integers separated by dashes. For example:
-                '1-0 2-0 2-0 0-3 0-5 0-5 0-6 2-4 3-6'
+                '1-0 2-0 2-0 0-3 0-5 0-5 0-5 0-6 2-4 3-6'
 
         Each integer pair must be in the form 'm-n' where m & n are integers
         between 0 and 6, inclusive. Each integer represents a lug position where
@@ -74,6 +74,15 @@
 
         Order in a list or string doesn't matter.
 
+        An alternate shortcut notation is also supported:
+                '1-0 2-0*2 0-3 0-5*3 0-6 2-4 3-6'
+
+        Any pair that is suffixed by '*k', where k is a positive integer, means
+        there are k copies of the preceeding lug pair combination. In other
+        words, these two strings describe identical drum configurations:
+                '2-4 2-4 2-4 0-1 0-1'
+                '2-4*3 0-1*2'
+
         """
         if isinstance(lug_list, str):
             drum = Drum.from_key_list(lug_list)
@@ -96,7 +105,7 @@
             for kw in self.key_wheels:
                 kw.rotate()
 
-            self.letter_count += 1
+            self.letter_counter += 1
 
         if group:
             s = ' '.join(''.join(ciphertext[i:i+5]) for i in range(0,
@@ -110,7 +119,7 @@
 if __name__ == '__main__':
 
     m209 = M209()
-    m209.set_drum_lugs('1-0 2-0 2-0 2-0 2-0 0-3 0-4 0-4 0-4 0-5 0-5 0-5 0-6 0-6 0-6 0-6 0-6 0-6 0-6 0-6 0-6 0-6 0-6 2-5 2-6 3-4 4-5')
+    m209.set_drum_lugs('1-0 2-0*4 0-3 0-4*3 0-5*3 0-6*11 2-5 2-6 3-4 4-5')
     m209.set_pins(0, 'BFJKLOSTUWXZ')
     m209.set_pins(1, 'ABDJKLMORTUV')
     m209.set_pins(2, 'EHJKNPQRSX')
--- a/m209/drum.py	Wed May 29 22:04:12 2013 -0500
+++ b/m209/drum.py	Sun Jun 02 13:57:45 2013 -0500
@@ -18,7 +18,7 @@
     lugs can be slid into positions numbered 1-6 and/or 2 neutral positions
     numbered 0. As the drum rotates all 27 bars have a chance for their lugs to
     interact with 6 guide arms (one for each key wheel), which may or may not be
-    in position. The guide arms' positioning are controlled by the effective
+    in position. The positioning of the guide arms are controlled by the effective
     pins on the six key wheels. As each bar rotates past the 6 guide arms for
     each key wheel, if one or both lugs come into contact with a guide arm, the
     bar will quickly shift left. This causes the indicator disk to rotate once,
@@ -62,33 +62,37 @@
     @classmethod
     def from_key_list(cls, lug_list):
         """Creates a Drum instance from a string that might be found on a key
-        list. The must consist of at most 27 whitespace separated pairs of
-        integers separated by dashes. For example:
-                '1-0 2-0 2-0 0-3 0-5 0-5 0-6 2-4 3-6'
-
-        Each integer pair must be in the form 'm-n' where m & n are integers
-        between 0 and 6, inclusive. Each integer represents a lug position where
-        0 is a neutral position, and 1-6 correspond to key wheel positions. If
-        m & n are both non-zero, they cannot be equal.
-
-        If a string has less than 27 pairs of integers, it is assumed all
-        remaining bars have both lugs in the neutral (0) positions.
+        list. See the description of the M209.set_drum_lugs() method for the
+        format of the lug_list string argument.
 
         """
         bars = []
         lug_list = lug_list.split()
         for lug_pair in lug_list:
+
+            repeat = 1
+            pair = lug_pair
             try:
-                m, n = [int(x) for x in lug_pair.split('-')]
+                if '*' in lug_pair:
+                    pair, repeat = lug_pair.split('*')
+                    repeat = int(repeat)
+                    if repeat < 0:
+                        raise ValueError
+
+                m, n = [int(x) for x in pair.split('-')]
             except ValueError:
                 raise DrumError("Invalid lug pair {}".format(lug_pair))
 
+            t = None
             if m and n:
-                bars.append((m - 1, n - 1))
+                t = (m - 1, n - 1)
             elif m and not n:
-                bars.append((m - 1, ))
+                t = (m - 1, )
             elif not m and n:
-                bars.append((n - 1, ))
+                t = (n - 1, )
+
+            for i in range(repeat):
+                bars.append(t)
 
         return cls(lug_list=bars)
 
--- a/m209/tests/test_drum.py	Wed May 29 22:04:12 2013 -0500
+++ b/m209/tests/test_drum.py	Sun Jun 02 13:57:45 2013 -0500
@@ -81,6 +81,18 @@
         key_list = "2-0" * (Drum.NUM_BARS + 1)
         self.assertRaises(DrumError, Drum.from_key_list, key_list)
 
+        key_list = "2-0* 1-0 1-0 4-4 2-3"
+        self.assertRaises(DrumError, Drum.from_key_list, key_list)
+
+        key_list = "2-0 1-0*-3 1-0 4-4 2-3"
+        self.assertRaises(DrumError, Drum.from_key_list, key_list)
+
+        key_list = "2-0 1-0*-3 1-0 4-4 2*3"
+        self.assertRaises(DrumError, Drum.from_key_list, key_list)
+
+        key_list = "2-0*10 1-0*2 4-4*10 1-6*6"
+        self.assertRaises(DrumError, Drum.from_key_list, key_list)
+
     def test_valid_key_list(self):
         key_list = "0-6 1-4 2-5"
         drum = Drum.from_key_list(key_list)
@@ -88,10 +100,13 @@
 
         key_list = "2-4 " + ("0-6 1-4 " * 13).rstrip()
         drum = Drum.from_key_list(key_list)
-
         bars = [(1, 3)] + [(5, ), (0, 3)] * 13
         self.assertEqual(bars, drum.bars)
 
+        key_list = "2-4 0-6*13 1-4*13"
+        drum = Drum.from_key_list(key_list)
+        self.assertEqual(sorted(bars), sorted(drum.bars))
+
     def test_rotate(self):
         # These are just simple tests to flush out syntax errors, etc. Higher
         # level tests will verify the correct operation of the entire machine.