4 <title>Pocket enigma
</title>
5 <meta http-equiv=
"Content-Type" content=
"text/html; charset=UTF-8"/>
6 <style type=
"text/css">
15 h1 { font-size:
3em; }
16 h2 { font-size:
2em; }
17 h3 { font-size:
1.6em; }
19 text-decoration: none;
22 -moz-border-radius:
5px;
23 -web-border-radius:
5px;
31 text-shadow:
0 0 20px #
333;
37 text-shadow:
0 0 20px #
333;
46 <textarea id=
"source">
50 ![centre-aligned Pocket Engima](pocket-enigma-small.jpg)
58 Emulates the Enigma machine from WWII
60 Mechanical cipher machine
63 * Substitution changes with every letter
65 Ciphering method: advance the wheel, then follow the lines to encipher the letter
67 ## Stateful enciphering
69 The cipher depends on the position of the wheel
71 We need to encapsulate that state
77 # The PocketEnigma object
79 What do we want it to do?
81 What data should the object hold?
85 # The PocketEnigma object
87 What do we want it to do?
89 * Initialise with the appropriate wheel (and possible starting position)
90 * Spin the wheel to a given position
91 * Advance the wheel one position
92 * Look up a letter given the wheel position
93 * Encipher a letter (advance the wheel then look up the letter)
94 * Encipher a message (optionally give the key)
95 * Make aliases for deciphering (same as enciphering)
98 * Accept user-defined wheels
99 * ...and validate them
101 What data should it hold?
103 * A description of the wheel being used
104 * The current position of the wheel
110 What's a convenient representation of the wheel
112 1. for the object to use internally
113 2. for a person to use to describe the wheel
115 They may not be the same, and we'll have to translate between them
121 ### Internal use: list of transpositions.
124 [
2,
3,
0,
1,
22,
8,
15,
12,
5, ...
127 so position
0 ('a') swaps with position
2 ('c'), position
3 ('d') swaps with position
1 ('b'), and so on.
129 * This will be a nightmare to enter correctly
131 ### Exernal use: list of pairs
134 [('a', 'c'), ('b', 'd'), ...]
139 * Need to validate the human-entered list, to check it's valid
143 # Validating the wheel description
149 # Validating the wheel specification
155 * ...and
26 letters mentioned overall
157 Raise exceptions if the specification is invalid
161 # Making the PocketEnigma class
164 class PocketEnigma(object):
165 def __init__(self, wheel=
1, position='a'):
166 self.wheel1 = [('a', 'z'), ('b', 'e'), ('c', 'x'), ('d', 'k'),
167 ('f', 'h'), ('g', 'j'), ('i', 'm'), ('l', 'r'), ('n', 'o'),
168 ('p', 'v'), ('q', 't'), ('s', 'u'), ('w', 'y')]
169 self.wheel2 = [('a', 'c'), ('b', 'd'), ('e', 'w'), ('f', 'i'),
170 ('g', 'p'), ('h', 'm'), ('j', 'k'), ('l', 'n'), ('o', 'q'),
171 ('r', 'z'), ('s', 'u'), ('t', 'v'), ('x', 'y')]
172 # Rest of initialisation code here
174 def make_wheel_map(self, wheel_spec):
179 def validate_wheel_spec(self, wheel_spec):
180 if len(wheel_spec) !=
13:
181 raise ValueError(
"Wheel specification has {} pairs, requires 13".
182 format(len(wheel_spec)))
190 Testing's easier if everything returns a meaningful value
192 Saves having to look up different values after performing each operation
194 `__init__` can't return a value (restriction of Python)
197 if __name__ ==
"__main__":
199 doctest.testmod(extraglobs={'pe': PocketEnigma(
1, 'a')})
202 `pe` is now available in all tests.
206 # Looking up the enciphered version of a letter
208 *Not* advancing the wheel before
210 Keep `self.position` to record where the wheel is
212 * `__init__` can be passed a letter, but internally it's a number
214 But the wheel map only works if the wheel arrow is pointing at 'a'
218 1. Rotate the source letter back `position` spaces
220 3. Rotate the destination letter forward `position` spaces
224 i.e. source → subtract position → lookup destination → add position
234 Advance the wheel, then look up the letter
242 for letter in plaintext:
243 ciphertext += encipher_letter(letter)
247 Have to be explicit as the order of the operations is important
249 * Something like `map` might choose an order different from strict left-to-right
251 ## Test it against the physical object
254 <script src=
"http://gnab.github.io/remark/downloads/remark-0.6.0.min.js" type=
"text/javascript">
257 <script type=
"text/javascript"
258 src=
"http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML&delayStartupUntil=configured"></script>
260 <script type=
"text/javascript">
261 var slideshow = remark.create({ ratio:
"16:9" });
266 skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
269 MathJax.Hub.Queue(function() {
270 $(MathJax.Hub.getAllJax()).map(function(index, elem) {
271 return(elem.SourceElement());
272 }).parent().addClass('has-jax');
274 MathJax.Hub.Configured();