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 Testing's easier if everything returns a meaningful value
112 Saves having to look up different values after performing each operation
114 `__init__` can't return a value (restriction of Python)
117 if __name__ ==
"__main__":
119 doctest.testmod(extraglobs={'pe': PocketEnigma(
1, 'a')})
122 `pe` is now available in all tests.
128 What's a convenient representation of the wheel
130 1. for the object to use internally
131 2. for a person to use to describe the wheel
133 They may not be the same, and we'll have to translate between them
139 ### Internal use: list of transpositions.
142 [
2,
3,
0,
1,
22,
8,
15,
12,
5, ...
145 so position
0 ('a') swaps with position
2 ('c'), position
3 ('d') swaps with position
1 ('b'), and so on.
147 * This will be a nightmare to enter correctly
149 ### Exernal use: list of pairs
152 [('a', 'c'), ('b', 'd'), ...]
157 * Need to validate the human-entered list, to check it's valid
161 # Validating the wheel description
167 # Validating the wheel specification
173 * ...and
26 letters mentioned overall
175 Raise exceptions if the specification is invalid
179 # Making the PocketEnigma class
182 class PocketEnigma(object):
183 def __init__(self, wheel=
1, position='a'):
184 self.wheel1 = [('a', 'z'), ('b', 'e'), ('c', 'x'), ('d', 'k'),
185 ('f', 'h'), ('g', 'j'), ('i', 'm'), ('l', 'r'), ('n', 'o'),
186 ('p', 'v'), ('q', 't'), ('s', 'u'), ('w', 'y')]
187 self.wheel2 = [('a', 'c'), ('b', 'd'), ('e', 'w'), ('f', 'i'),
188 ('g', 'p'), ('h', 'm'), ('j', 'k'), ('l', 'n'), ('o', 'q'),
189 ('r', 'z'), ('s', 'u'), ('t', 'v'), ('x', 'y')]
190 # Rest of initialisation code here
192 def make_wheel_map(self, wheel_spec):
197 def validate_wheel_spec(self, wheel_spec):
198 if len(wheel_spec) !=
13:
199 raise ValueError(
"Wheel specification has {} pairs, requires 13".
200 format(len(wheel_spec)))
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();