@ondoher/enigma 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/enigma/Encoder.js +102 -0
- package/lib/enigma/Enigma.js +246 -0
- package/lib/enigma/EntryDisc.js +36 -0
- package/lib/enigma/Inventory.js +115 -0
- package/lib/enigma/PlugBoard.js +74 -0
- package/lib/enigma/Reflector.js +51 -0
- package/lib/enigma/Rotor.js +170 -0
- package/lib/enigma/consts.js +1 -0
- package/lib/enigma/index.js +7 -0
- package/lib/enigma/standardInventory.js +95 -0
- package/lib/enigma/tests/EnigmaData.js +236 -0
- package/lib/enigma/tests/EnigmaSpec.js +123 -0
- package/lib/enigma/tests/PlugBoardData.js +4 -0
- package/lib/enigma/tests/PlugBoardSpec.js +46 -0
- package/lib/enigma/tests/RotorData.js +13 -0
- package/lib/enigma/tests/RotorSpec.js +263 -0
- package/lib/generator/Generator.js +452 -0
- package/lib/generator/hamlet.js +1675 -0
- package/lib/generator/index.js +1 -0
- package/lib/index.js +2 -0
- package/package.json +16 -0
- package/spec/helpers/consoleReporter.js +11 -0
- package/spec/support/jasmine.json +14 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { STANDARD_ALPHABET } from "./consts.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This is the listener for Enigma and component events
|
|
5
|
+
* @callback Listener
|
|
6
|
+
* @param {String} event a string that identifies the event being fired
|
|
7
|
+
* @param {String} name the name of the component firing the event
|
|
8
|
+
* @param {String} message a human readable description of the evebnt details
|
|
9
|
+
* @param {Object} into event specific data for the event
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* This is the base class for an encoder. The default implementation of the
|
|
14
|
+
* encode method is to return the input as the output
|
|
15
|
+
*/
|
|
16
|
+
export default class Encoder {
|
|
17
|
+
constructor(name, {cb, alphabet = STANDARD_ALPHABET}) {
|
|
18
|
+
this.name = name;
|
|
19
|
+
this.alphabet = alphabet;
|
|
20
|
+
this.contactCount = alphabet.length;
|
|
21
|
+
this.cb = cb;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* given a connector number, normalize it to be between 0 and 25 inclusive.
|
|
26
|
+
*
|
|
27
|
+
* @param {Number} connector the connector being normalized
|
|
28
|
+
*
|
|
29
|
+
* @returns {Number} value between 0 and 25
|
|
30
|
+
*/
|
|
31
|
+
normalize(connector) {
|
|
32
|
+
return (connector + this.contactCount * 2) % this.contactCount;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Given an alphabetic connection map, convert that into an array of
|
|
37
|
+
* numbers. The index into the array or string is the input connector, and
|
|
38
|
+
* the value at that position is the output connector
|
|
39
|
+
*
|
|
40
|
+
* @param {String} map connections map.
|
|
41
|
+
* @returns {Array.<Number>} the numerical map
|
|
42
|
+
*/
|
|
43
|
+
makeMap(map) {
|
|
44
|
+
var letters = [...map];
|
|
45
|
+
return letters.map(function(letter) {
|
|
46
|
+
return this.alphabet.indexOf(letter);
|
|
47
|
+
}, this)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* given an existing connection map from inoput to out put, create a new map
|
|
52
|
+
* that has the connections going in the other direction, output to input.
|
|
53
|
+
*
|
|
54
|
+
* @param {Array.<Number>} map connection map
|
|
55
|
+
* @returns {Array.<Number>} the reversed map
|
|
56
|
+
*/
|
|
57
|
+
makeReverseMap(map) {
|
|
58
|
+
var reverseMap = new Array(map.length);
|
|
59
|
+
|
|
60
|
+
map.forEach(function(input, idx) {
|
|
61
|
+
reverseMap[input] = idx;
|
|
62
|
+
}, this);
|
|
63
|
+
|
|
64
|
+
return reverseMap;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Call this method to convert the input connector number to the output in
|
|
69
|
+
* the given direction The default encode method just passes the input value
|
|
70
|
+
* through
|
|
71
|
+
*
|
|
72
|
+
* @param {String} direction either right for moving towards the reflector
|
|
73
|
+
* or left if moving back
|
|
74
|
+
* @param {Number} input the specific connection receiving an input
|
|
75
|
+
*
|
|
76
|
+
* @returns {Number} The translated output connector number
|
|
77
|
+
*/
|
|
78
|
+
encode(direction, input) {
|
|
79
|
+
return input;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Call this method to set a function to be called when important events
|
|
84
|
+
* happen to a component.
|
|
85
|
+
|
|
86
|
+
* @param {Listener} cb the function to be called.
|
|
87
|
+
*/
|
|
88
|
+
listen(cb) {
|
|
89
|
+
this.cb = cb;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Call this method to call the event listener
|
|
94
|
+
*
|
|
95
|
+
* @param {String} name the name of the event
|
|
96
|
+
* @param {...any} rest the parameters to pass to the callback
|
|
97
|
+
*/
|
|
98
|
+
fire(name, ...rest) {
|
|
99
|
+
if (this.cb) this.cb(name, ...rest);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import EntryDisc from "./EntryDisc.js";
|
|
2
|
+
import PlugBoard from "./PlugBoard.js";
|
|
3
|
+
import Rotor from "./Rotor.js";
|
|
4
|
+
import Reflector from "./Reflector.js";
|
|
5
|
+
import inventory from './Inventory.js'
|
|
6
|
+
import { STANDARD_ALPHABET } from "./consts.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Construct this class to get a new instance of the Enigma. Many of the
|
|
10
|
+
* parameters to the constructor and the config method reference the names of
|
|
11
|
+
* standard Enigma parts. These are retrived from the Inventory instance
|
|
12
|
+
*/
|
|
13
|
+
export default class Enigma {
|
|
14
|
+
/**
|
|
15
|
+
* The constructor for the Enigma. This represents the unconfigurable
|
|
16
|
+
* settings of the device.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} settings the settings for the Enigma
|
|
19
|
+
* @property {String} [entryDisc] the name of entry disc in the inventory
|
|
20
|
+
* this defaults 'default'
|
|
21
|
+
* @param {String} reflector specifies one of possible reflectors, the
|
|
22
|
+
* predefined reflectors are A, B, C, Thin-B and Thin-C
|
|
23
|
+
* @param {String} [alphabet] the alphabet used by the system, usually just
|
|
24
|
+
* the uppercase latin letters
|
|
25
|
+
*/
|
|
26
|
+
constructor(settings) {
|
|
27
|
+
var {entryDisc = 'default', reflector, alphabet = STANDARD_ALPHABET} = settings;
|
|
28
|
+
var entryDiscSettings = inventory.getEntryDisc(entryDisc)
|
|
29
|
+
|
|
30
|
+
var reflectorSettings = inventory.getReflector(reflector)
|
|
31
|
+
this.alphabet = alphabet;
|
|
32
|
+
this.plugboard = new PlugBoard('plugboard', {});
|
|
33
|
+
this.entryDisc = new EntryDisc('entry-disc', entryDiscSettings);
|
|
34
|
+
this.reflector = new Reflector('reflector', reflectorSettings)
|
|
35
|
+
this.length = alphabet.length;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Call this method to normalize a connector number to be within the
|
|
40
|
+
* the length of the currrent alphabet
|
|
41
|
+
*
|
|
42
|
+
* @param {Number} connector the number to be normalized
|
|
43
|
+
*
|
|
44
|
+
* @returns {Number} the normalized connector number
|
|
45
|
+
*/
|
|
46
|
+
normalize(connector) {
|
|
47
|
+
connector += this.length;
|
|
48
|
+
connector = connector % this.length
|
|
49
|
+
|
|
50
|
+
return connector;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Configure the Enigma for encoding.
|
|
55
|
+
*
|
|
56
|
+
* @param {Object} settings the configuration of the Enigma. These settings
|
|
57
|
+
* represent the aspects of the Enigma that can can change for daily
|
|
58
|
+
* configuration.
|
|
59
|
+
* @property {Array.<String>|String} [plugs] array of strings with each
|
|
60
|
+
* element being a pair of letters from the alphabet that are being swapped
|
|
61
|
+
* on the plug board
|
|
62
|
+
* @property {Array.<String>} rotors the array of installed rotors. The
|
|
63
|
+
* order here is signicant and is given in the left to right direction.
|
|
64
|
+
* This means that last name in this list is the first rotor used in the
|
|
65
|
+
* forward direction and last used in the backward direction. Each element
|
|
66
|
+
* is the name of the rotor to use in the corresponding position. Stepping
|
|
67
|
+
* stops at the first fixed rotor
|
|
68
|
+
* @property {String|Array<Number>} [ringSettings] each letter in this
|
|
69
|
+
* string represents the offset of the key settings from the rotor start
|
|
70
|
+
* position. If it is an array, then each value is the one based key
|
|
71
|
+
* setting for the related rotor,
|
|
72
|
+
*/
|
|
73
|
+
configure(settings) {
|
|
74
|
+
var { rotors, ringSettings = [], plugs = [] } = settings;
|
|
75
|
+
|
|
76
|
+
// make copies of these configurations so that we don't change the
|
|
77
|
+
// values from the caller, which in JavaScript are passed by reference.
|
|
78
|
+
rotors = JSON.parse(JSON.stringify(rotors));
|
|
79
|
+
ringSettings = JSON.parse(JSON.stringify(ringSettings));
|
|
80
|
+
|
|
81
|
+
// the rotors are given in the left to right direction, but are actually
|
|
82
|
+
// used in the right to left direction. So, here we reverse them
|
|
83
|
+
rotors = rotors.reverse();
|
|
84
|
+
|
|
85
|
+
this.plugboard.configure({plugs})
|
|
86
|
+
|
|
87
|
+
var ringOffsets = []
|
|
88
|
+
|
|
89
|
+
// because the rotors are secified in the reverse other they are used,
|
|
90
|
+
// we have to do the same for the ringSettings.
|
|
91
|
+
if (Array.isArray(ringSettings)) {
|
|
92
|
+
ringSettings = ringSettings.reverse();
|
|
93
|
+
|
|
94
|
+
// When specified with numbers they will be in the range 1-26, we
|
|
95
|
+
// need to shift these to be 0-25;
|
|
96
|
+
ringSettings.forEach(function(offset) {
|
|
97
|
+
ringOffsets.push(this.normalize(offset - 1))
|
|
98
|
+
}, this);
|
|
99
|
+
} else {
|
|
100
|
+
var letters = [...ringSettings];
|
|
101
|
+
letters = letters.reverse();
|
|
102
|
+
letters.forEach(function(letter) {
|
|
103
|
+
var offset = this.alphabet.indexOf(letter);
|
|
104
|
+
ringOffsets.push(offset);
|
|
105
|
+
}, this)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
this.rotors = rotors.map(function(name, idx) {
|
|
109
|
+
return new Rotor(`rotor-${name}`, {...inventory.getRotor(name), alphabet: this.alphabet, ringSetting: ringOffsets[idx], cb: this.cb});
|
|
110
|
+
}, this);
|
|
111
|
+
|
|
112
|
+
this.encoders = [this.plugboard, this.entryDisc, ...this.rotors];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Call this method to step the rotors one time. This method will manage the
|
|
117
|
+
* stepping between all rotors
|
|
118
|
+
*/
|
|
119
|
+
step() {
|
|
120
|
+
this.rotors.forEach(function(rotor, idx) {
|
|
121
|
+
if (rotor.isFixed()) return;
|
|
122
|
+
|
|
123
|
+
// This is the double stepping. Only do this for the middle rotor
|
|
124
|
+
if (rotor.willTurnover() && idx === 1) {
|
|
125
|
+
this.pending[idx] = true
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (this.pending[idx]) {
|
|
129
|
+
this.pending[idx] = false;
|
|
130
|
+
if (rotor.step()) this.pending[idx + 1] = true;
|
|
131
|
+
}
|
|
132
|
+
}, this);
|
|
133
|
+
|
|
134
|
+
// The first rotor is always stepping
|
|
135
|
+
this.pending[0] = true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Call this method to set the starting rotation for the messages to encrypt
|
|
140
|
+
*
|
|
141
|
+
* @param {Array.Number>|String} the length of the string or the array
|
|
142
|
+
* should match the number of rotors and are given left to right. If start
|
|
143
|
+
* is a string then the letters of the string specify the start value seen
|
|
144
|
+
* in the window for the corresponding rotor. If it is an array then each
|
|
145
|
+
* number will be the one-based rotation.
|
|
146
|
+
*/
|
|
147
|
+
setStart(start) {
|
|
148
|
+
if (Array.isArray(start)) {
|
|
149
|
+
var charArray = start.map(function(number) {
|
|
150
|
+
number--;
|
|
151
|
+
return this.alphabet[number];
|
|
152
|
+
}, this);
|
|
153
|
+
|
|
154
|
+
start = charArray.join('');
|
|
155
|
+
}
|
|
156
|
+
start = [...start].reverse();
|
|
157
|
+
|
|
158
|
+
// reset the rotation pending state
|
|
159
|
+
this.pending = {0: true};
|
|
160
|
+
|
|
161
|
+
this.rotors.forEach(function(rotor, idx) {
|
|
162
|
+
rotor.setStartPosition(start[idx]);
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Call this method to simulate a keypress on the Enigma. This will output
|
|
168
|
+
* the encoded letter
|
|
169
|
+
*
|
|
170
|
+
* @param {String} letter the key pressed
|
|
171
|
+
* @returns {String} the encoded letter
|
|
172
|
+
*/
|
|
173
|
+
keyPress(letter) {
|
|
174
|
+
letter = letter.toUpperCase();
|
|
175
|
+
if (letter.length !== 1 || this.alphabet.indexOf(letter) === -1) {
|
|
176
|
+
if (letter !== ' ')
|
|
177
|
+
console.warn(`Unexected character ${letter}`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.fire('input', this.name, `input ${letter}`, {letter})
|
|
182
|
+
this.step();
|
|
183
|
+
|
|
184
|
+
// encode to the right
|
|
185
|
+
var connector = this.encoders.reduce(function(connector, encoder) {
|
|
186
|
+
connector = encoder.encode('right', connector);
|
|
187
|
+
return connector;
|
|
188
|
+
}.bind(this), this.alphabet.indexOf(letter));
|
|
189
|
+
|
|
190
|
+
// reflector
|
|
191
|
+
connector = this.reflector.encode('', connector);
|
|
192
|
+
|
|
193
|
+
// encode to the left
|
|
194
|
+
connector = this.encoders.reduceRight(function(connector, encoder) {
|
|
195
|
+
connector = encoder.encode('left', connector);
|
|
196
|
+
return connector;
|
|
197
|
+
}.bind(this), connector)
|
|
198
|
+
|
|
199
|
+
letter = this.alphabet[connector];
|
|
200
|
+
this.fire('output', this.name, `output ${letter}`, {letter})
|
|
201
|
+
return letter;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Call this shortcut method to encode a whole string
|
|
206
|
+
*
|
|
207
|
+
* @param {String} start the starting positon for the rotors
|
|
208
|
+
* @param {String} text the text to encode
|
|
209
|
+
*
|
|
210
|
+
* @returns {String} the encoded string.
|
|
211
|
+
*/
|
|
212
|
+
encode(start, text) {
|
|
213
|
+
this.setStart(start)
|
|
214
|
+
var letters = [...text];
|
|
215
|
+
var output = letters.map(function(letter) {
|
|
216
|
+
return this.keyPress(letter);
|
|
217
|
+
}, this)
|
|
218
|
+
|
|
219
|
+
return output.join('');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Call this method to call the event listener
|
|
224
|
+
*
|
|
225
|
+
* @param {String} name the name of the event
|
|
226
|
+
* @param {...any} rest the parameters to pass to the callback
|
|
227
|
+
*/
|
|
228
|
+
fire(type, ...rest) {
|
|
229
|
+
if (this.cb) this.cb(type, ...rest);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Call this method to set a function to be called when important events
|
|
234
|
+
* happen to a component.
|
|
235
|
+
|
|
236
|
+
* @param {Listener} cb the function to be called.
|
|
237
|
+
*/
|
|
238
|
+
listen(cb) {
|
|
239
|
+
this.cb = cb;
|
|
240
|
+
this.encoders.forEach(function(encoder) {
|
|
241
|
+
encoder.listen(cb);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
this.reflector.listen(cb);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Encoder from './Encoder.js';
|
|
2
|
+
import { STANDARD_ALPHABET } from "./consts.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* This is the class for an entry disc. Entry discs are fixed disks of connector
|
|
7
|
+
* pins.
|
|
8
|
+
*/
|
|
9
|
+
export default class EntryDisc extends Encoder {
|
|
10
|
+
/**
|
|
11
|
+
* Constuctor for the entry disc.
|
|
12
|
+
*
|
|
13
|
+
* @param {String} name thge name of this entry disc
|
|
14
|
+
* @param {Object} settings contains the alphabet being used, and the map
|
|
15
|
+
* between input and output contacts
|
|
16
|
+
*/
|
|
17
|
+
constructor(name, settings) {
|
|
18
|
+
super(name, settings);
|
|
19
|
+
var {map} = settings;
|
|
20
|
+
this.rightMap = this.makeMap(map);
|
|
21
|
+
this.leftMap = this.makeReverseMap(this.rightMap);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
encode(direction, input) {
|
|
25
|
+
var result = direction === 'right' ? this.rightMap[input]: this.leftMap[input];
|
|
26
|
+
var evName = direction === 'right' ? 'encode-right' : 'encode-left';
|
|
27
|
+
|
|
28
|
+
this.fire(evName, this.name,
|
|
29
|
+
`${evName} ${this.name}, input: ${input}, output: ${result}`, {
|
|
30
|
+
input: input,
|
|
31
|
+
output: result,
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the class used to manage the standard inmventory of components. An
|
|
3
|
+
* instance of this class is exported as the default of this module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class Inventory {
|
|
7
|
+
/**
|
|
8
|
+
* Constructor for the inventory class. Starts out empty
|
|
9
|
+
*/
|
|
10
|
+
constructor() {
|
|
11
|
+
this.entryDiscs = {};
|
|
12
|
+
this.rotors = {};
|
|
13
|
+
this.fixedRotors = {};
|
|
14
|
+
this.reflectors = {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Call this method to add a new Rotor type to the inventory.
|
|
19
|
+
*
|
|
20
|
+
* @param {String} name the name of the rotor being added. This name will be
|
|
21
|
+
* used when specifying the rotors to use for the Enigma configuration.
|
|
22
|
+
* @param {String} map a string specifying the connector mapping. The index
|
|
23
|
+
* of the string is the logical coordinate of the connector, the character
|
|
24
|
+
* at that index is the output connector. To be exact, it would be the
|
|
25
|
+
* position of that character in the given alphabet. So, in the map '
|
|
26
|
+
* EKMFLGDQVZNTOWYHXUSPAIBRCJ', input connector 0 would map to output
|
|
27
|
+
* connector 4 and input connector 1 would map to output connector 10.
|
|
28
|
+
* Remember that the connectors are numbered starting at 0.
|
|
29
|
+
* @param {String} turnovers this is a string of characters representing the
|
|
30
|
+
* turnover locations on the disk. These letter would be the value shown in
|
|
31
|
+
* the window to during turnover. In Rotor I this is 'Q' in rotors VI, VII,
|
|
32
|
+
* and VIII there are two turnover locations, 'M' and 'Z'. Pass an empty
|
|
33
|
+
* string if this is a fixed rotor
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
addRotor(name, map, turnovers) {
|
|
37
|
+
this.rotors[name] = {map, turnovers}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Call this method to add a new reflector definition.
|
|
42
|
+
*
|
|
43
|
+
* @param {String} name this is the name that will be used to reference this
|
|
44
|
+
* reflector when constructing an Enigma class.
|
|
45
|
+
* @param {String} map the mapping between connectors. this uses the same
|
|
46
|
+
* format used in the addRotor method
|
|
47
|
+
*/
|
|
48
|
+
addReflector(name, map) {
|
|
49
|
+
this.reflectors[name] = {map};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Call this method to add a new entry disc. There was only one used in the
|
|
54
|
+
* standard military models, but there were other versions that defined it
|
|
55
|
+
* differently.
|
|
56
|
+
*
|
|
57
|
+
* @param {*} name this is the name that will be used to reference this
|
|
58
|
+
* entry disc when constructing an Enigma class.
|
|
59
|
+
* @param {*} map the mapping between connectors. this uses the same format
|
|
60
|
+
* used in the addRotor method
|
|
61
|
+
*/
|
|
62
|
+
addEntryDisc(name, map) {
|
|
63
|
+
this.entryDiscs[name] = {map};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Call this method to get the setup for a defined rotor.
|
|
68
|
+
*
|
|
69
|
+
* @param {String} name the name of the rotor as it was added to the
|
|
70
|
+
* inventory.
|
|
71
|
+
*
|
|
72
|
+
* @returns {Object} the rotor defintion
|
|
73
|
+
* @property {String} map the connection map for the rotor
|
|
74
|
+
* @property {String} turnovers the locations where turnovers happen
|
|
75
|
+
*/
|
|
76
|
+
getRotor(name) {
|
|
77
|
+
return this.rotors[name];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Call this method to get the setup for a defined reflector.
|
|
82
|
+
*
|
|
83
|
+
* @param {String} name the name of the reflector as it was added to the
|
|
84
|
+
* inventory.
|
|
85
|
+
* @returns {Object} the reflector definition
|
|
86
|
+
* @property {String} the connection map for the reflector
|
|
87
|
+
*/
|
|
88
|
+
getReflector(name) {
|
|
89
|
+
return this.reflectors[name];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Call this method to get the setup for a defined entry disc.
|
|
94
|
+
*
|
|
95
|
+
* @param {String} name the name of the entry disc as it was added to the
|
|
96
|
+
* inventory.
|
|
97
|
+
* @returns {Object} the entry disc definition
|
|
98
|
+
* @property {String} the connection map for the entry disc
|
|
99
|
+
*/
|
|
100
|
+
getEntryDisc(name) {
|
|
101
|
+
return this.entryDiscs[name];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Call this method to get the names of all the rotors in the inventory
|
|
106
|
+
*
|
|
107
|
+
* @returns {Array.<String>} the names of the rotors
|
|
108
|
+
*/
|
|
109
|
+
getRotorNames() {
|
|
110
|
+
return Object.keys(this.rotors);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
export default new Inventory();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import Encoder from "./Encoder.js";
|
|
2
|
+
import { STANDARD_ALPHABET } from "./consts.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This class represents the plugboard. There is only one type of plugboard
|
|
6
|
+
*/
|
|
7
|
+
export default class PlugBoard extends Encoder {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Constructor for the plugboard.
|
|
11
|
+
*
|
|
12
|
+
* @param {String} name the name for the plugboard, defaults to 'plugboard'
|
|
13
|
+
* @param {Object} [settings] the settings for the plugboard. Only needed if
|
|
14
|
+
* using an alternate alphabet
|
|
15
|
+
*/
|
|
16
|
+
constructor(name = 'plugboard', settings = {}) {
|
|
17
|
+
super(name, settings);
|
|
18
|
+
|
|
19
|
+
var {alphabet = STANDARD_ALPHABET, map} = settings;
|
|
20
|
+
this.alphabet = alphabet;
|
|
21
|
+
this.map = map || alphabet;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Call this method to configure the plug board. This will be used to
|
|
26
|
+
* provide the plug connections
|
|
27
|
+
*
|
|
28
|
+
* @param {Object} [settings] the configuration options for the plug
|
|
29
|
+
* board
|
|
30
|
+
* @property {Array.<String>|String} [plugs] either an array of strings or a
|
|
31
|
+
* single string. If it is a string, it must be a space separated list of
|
|
32
|
+
* letter pairs that connects one input letter to another. If it is an
|
|
33
|
+
* array then then each item is a pair of letters to specify how the plugs
|
|
34
|
+
* are connected
|
|
35
|
+
*/
|
|
36
|
+
configure(settings = {}) {
|
|
37
|
+
var map = this.map;
|
|
38
|
+
var {plugs = []} = settings;
|
|
39
|
+
|
|
40
|
+
if (typeof plugs === 'string') plugs = plugs.split(' ');
|
|
41
|
+
plugs.forEach(function(plug) {
|
|
42
|
+
var firstIdx = this.alphabet.indexOf(plug[0]);
|
|
43
|
+
var secondIdx = this.alphabet.indexOf(plug[1]);
|
|
44
|
+
var first = map[firstIdx];
|
|
45
|
+
var second = map[secondIdx];
|
|
46
|
+
map = map.slice(0, firstIdx) + second + map.slice(firstIdx + 1);
|
|
47
|
+
map = map.slice(0, secondIdx) + first + map.slice(secondIdx + 1);
|
|
48
|
+
}, this)
|
|
49
|
+
|
|
50
|
+
this.rightMap = this.makeMap(map);
|
|
51
|
+
this.leftMap = this.makeReverseMap(this.rightMap);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Call this method to convert the input connector number to the output in
|
|
56
|
+
* the given direction.
|
|
57
|
+
*
|
|
58
|
+
* @param {String} direction either right for moving towards the reflector or
|
|
59
|
+
* left if moving back
|
|
60
|
+
* @param {Number} input the input connector
|
|
61
|
+
* @returns {Number} the output connector
|
|
62
|
+
*/
|
|
63
|
+
encode(direction, input) {
|
|
64
|
+
var result = direction === 'right' ? this.rightMap[input]: this.leftMap[input];
|
|
65
|
+
var evName = direction === 'right' ? 'encode-right' : 'encode-left';
|
|
66
|
+
this.fire(evName, this.name,
|
|
67
|
+
`${evName} ${this.name}, input: ${input}, output: ${result}`, {
|
|
68
|
+
input: input,
|
|
69
|
+
output: result,
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Encoder from './Encoder.js';
|
|
2
|
+
import { STANDARD_ALPHABET } from './consts.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Make in instance of this class to construct a reflector
|
|
6
|
+
*/
|
|
7
|
+
export default class Reflector extends Encoder {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* constructor for the reflector class.
|
|
11
|
+
*
|
|
12
|
+
* @param {String} name the name of the reflector instance
|
|
13
|
+
* @param {Obect} The definition of the reflector
|
|
14
|
+
* @property {String} [alphabet] a string of letters that are an alternative
|
|
15
|
+
* to the standard A-Z. Defaults to A-Z
|
|
16
|
+
* @property {String} map a string that defines the mapping between the
|
|
17
|
+
* input and output connectors. The index into the string is the input
|
|
18
|
+
* connector and the value of this string at that index is the output
|
|
19
|
+
* connector. For example, 'YRUHQSLDPXNGOKMIEBFZCWVJAT' which is the map for
|
|
20
|
+
* standard reflector B.
|
|
21
|
+
*/
|
|
22
|
+
constructor(name, settings) {
|
|
23
|
+
super(name, settings);
|
|
24
|
+
var {map} = settings;
|
|
25
|
+
|
|
26
|
+
this.map = this.makeMap(map);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Call this method when reversing the encoding direction of the Enigma. As
|
|
31
|
+
* the point where direction changes this does not have a distinction
|
|
32
|
+
* between a left and right signal path.
|
|
33
|
+
*
|
|
34
|
+
* @param {String} direction since this is the point where signal direction
|
|
35
|
+
* changes from right to left this parameter is not used.
|
|
36
|
+
* @param {Number} input this is the input connector
|
|
37
|
+
*
|
|
38
|
+
* @returns {Number} the mapped output connector
|
|
39
|
+
*/
|
|
40
|
+
encode(direction, input) {
|
|
41
|
+
var result = this.map[input];
|
|
42
|
+
this.fire('encode', this.name,
|
|
43
|
+
`encode ${this.name} ${input} ${result}`,
|
|
44
|
+
{
|
|
45
|
+
input: input,
|
|
46
|
+
output: result,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
}
|