changeset 7:39297f695cff

First version of EnigmaMachine class. Simple main to test operations.
author Brian Neal <bgneal@gmail.com>
date Sat, 26 May 2012 17:21:22 -0500
parents 7a90beffd8f2
children 19f6859a3d19
files enigma/__init__.py enigma/machine.py enigma/main.py enigma/rotors/factory.py enigma/rotors/rotor.py
diffstat 5 files changed, 141 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/enigma/__init__.py	Sat May 26 15:19:48 2012 -0500
+++ b/enigma/__init__.py	Sat May 26 17:21:22 2012 -0500
@@ -0,0 +1,13 @@
+# Copyright (C) 2012 by Brian Neal.
+# 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.
+
+For a list of the rotors and reflectors we simulate, see the module rotors.data.
+
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/machine.py	Sat May 26 17:21:22 2012 -0500
@@ -0,0 +1,89 @@
+# Copyright (C) 2012 by Brian Neal.
+# This file is part of Py-Enigma, the Enigma Machine simulation.
+# Py-Enigma is released under the MIT License (see License.txt).
+
+"""This module contains the top-level EnigmaMachine class for the Enigma Machine
+simulation.
+
+"""
+import string
+
+class EnigmaError(Exception):
+    pass
+
+
+class EnigmaMachine:
+    """Top-level class for the Enigma Machine simulation."""
+
+    def __init__(self, rotors, reflector):
+        """Configures the Enigma Machine. Parameters are as follows:
+
+        rotors - a list containing 3 or 4 (for the Kriegsmarine M4 version)
+        Rotor objects. The order of the list is important. The first rotor is
+        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
+
+        Note that on the military Enigma machines we are simulating, the entry
+        wheel is a simple straight-pass through and is not simulated here. It
+        would not be too hard to add a parameter for the entry wheel and pass a
+        Rotor object for it if it is desired to simulate a non-military Enigma
+        machine.
+
+        """
+        if len(rotors) not in [3, 4]:
+            raise EnigmaError("Must supply 3 or 4 rotors")
+
+        self.rotors = rotors
+        self.rotor_count = len(rotors)
+        self.reflector = reflector
+
+
+    def set_display(self, val):
+
+        for i, rotor in enumerate(self.rotors):
+            self.rotors[i].set_display(val[i])
+
+
+    def cipher(self, plaintext):
+
+        # TODO: This is just placeholder code until I can figure out what I am
+        # doing...!
+
+        if len(plaintext) != 1:
+            raise EnigmaError("not implemented yet")
+        if plaintext[0] not in string.ascii_uppercase:
+            raise EnigmaError("invalid input: %s" % plaintext)
+
+        x = ord(plaintext[0]) - ord('A')
+
+        x = self.rotors[-1].signal_in(x)
+        print(chr(x + ord('A')))
+        x = self.rotors[-2].signal_in(x)
+        print(chr(x + ord('A')))
+        x = self.rotors[-3].signal_in(x)
+        print(chr(x + ord('A')))
+
+        if self.rotor_count == 4:
+            x = self.rotors[-4].signal_in(x)
+            print(chr(x + ord('A')))
+
+        x = self.reflector.signal_in(x)
+        print(chr(x + ord('A')))
+
+        x = self.rotors[0].signal_out(x)
+        print(chr(x + ord('A')))
+        x = self.rotors[1].signal_out(x)
+        print(chr(x + ord('A')))
+        x = self.rotors[2].signal_out(x)
+        print(chr(x + ord('A')))
+
+        if self.rotor_count == 4:
+            x = self.rotors[3].signal_out(x)
+            print(chr(x + ord('A')))
+
+        ciphertext = chr(x + ord('A'))
+
+        print("%s => %s" % (plaintext, ciphertext))
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/enigma/main.py	Sat May 26 17:21:22 2012 -0500
@@ -0,0 +1,34 @@
+# Copyright (C) 2012 by Brian Neal.
+# This file is part of Py-Enigma, the Enigma Machine simulation.
+# Py-Enigma is released under the MIT License (see License.txt).
+
+
+from rotors.factory import create_rotor
+from rotors.factory import create_reflector
+from machine import EnigmaMachine
+
+def main():
+
+    rotors = []
+    rotors.append(create_rotor('I'))
+    rotors.append(create_rotor('II'))
+    rotors.append(create_rotor('III'))
+
+    reflector = create_reflector('B')
+
+    machine = EnigmaMachine(rotors=rotors, reflector=reflector)
+
+    machine.set_display('AAB')
+    machine.cipher('A')
+    machine.set_display('AAC')
+    machine.cipher('A')
+    machine.set_display('AAD')
+    machine.cipher('A')
+    machine.set_display('AAE')
+    machine.cipher('A')
+    machine.set_display('AAF')
+    machine.cipher('A')
+
+
+if __name__ == '__main__':
+    main()
--- a/enigma/rotors/factory.py	Sat May 26 15:19:48 2012 -0500
+++ b/enigma/rotors/factory.py	Sat May 26 17:21:22 2012 -0500
@@ -4,12 +4,12 @@
 
 """Contains factory functions for creating rotors and reflectors."""
 
-from enigma.rotors import RotorError
-from enigma.rotors.rotor import Rotor
-from enigma.rotors.data import ROTORS, REFLECTORS
+from . import RotorError
+from .rotor import Rotor
+from .data import ROTORS, REFLECTORS
 
 
-def create_rotor(model, ring_setting, alpha_labels=True):
+def create_rotor(model, ring_setting=0, alpha_labels=True):
     """Factory function to create and return a rotor of the given model name."""
 
     if model in ROTORS:
--- a/enigma/rotors/rotor.py	Sat May 26 15:19:48 2012 -0500
+++ b/enigma/rotors/rotor.py	Sat May 26 17:21:22 2012 -0500
@@ -96,7 +96,7 @@
         self.wiring_str = wiring.upper()
         self.ring_setting = ring_setting
         self.alpha_labels = alpha_labels
-        self.pos = None     # not installed in an Enigma machine yet
+        self.pos = 0
 
         # check wiring length
         if len(self.wiring_str) != 26: