@ondoher/enigma 1.0.1 → 1.0.3

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/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  # Table of Contents
6
6
  [Getting Started](#getting-started)
7
+
7
8
  [Simulation](#simulation)
8
9
  - [Events](#events)
9
10
  - [Plugboard](#plugboard)
@@ -37,7 +38,7 @@ You can then import this into your code like this:
37
38
  ```JavaScript
38
39
  import {Enigma} from '@ondoher/enigma';
39
40
 
40
- var enigma = new Enigma({reflector: 'B'});
41
+ var enigma = new Enigma("I", {reflector: 'B'});
41
42
  ```
42
43
 
43
44
  In addition to this api, there is also [documentation](https://github.com/Ondoher/enigma/blob/main/docs/enigma.md)
@@ -419,7 +420,7 @@ An array of the installed rotors
419
420
  ```javascript
420
421
  import {Enigma} from '@ondoher/enigma';
421
422
 
422
- var enigma = new Enigma({reflector: 'B'});
423
+ var enigma = new Enigma("I", {reflector: 'B'});
423
424
 
424
425
  enigma.configure({
425
426
  rotors: ['III', 'VI', 'VIII'],
package/jsconfig.json CHANGED
@@ -1,3 +1,9 @@
1
1
  {
2
-
2
+ "compilerOptions": {
3
+ "target": "es2020", // Or a newer version like "es2020"
4
+ "module": "commonjs"
5
+ },
6
+ "exclude": [
7
+ "node_modules"
8
+ ]
3
9
  }
@@ -10,14 +10,21 @@ export default class Encoder {
10
10
  * Constructor for the base encoder
11
11
  *
12
12
  * @param {string} name
13
+ * @param {ComponentType} type
13
14
  * @param {EncoderSetup} settings
14
15
  */
15
- constructor(name, settings) {
16
+ constructor(name, type, settings) {
16
17
  let {cb, alphabet = STANDARD_ALPHABET} = settings;
17
18
  this.name = name;
19
+ this.type = type;
18
20
  this.alphabet = alphabet;
19
21
  this.contactCount = alphabet.length;
20
- this.cb = cb;
22
+ /** @type {{[name: string]: Listener}} */
23
+ this.listeners = {}
24
+
25
+ if (cb) {
26
+ this.listeners["constructor"] = cb;
27
+ }
21
28
  }
22
29
 
23
30
  /**
@@ -31,6 +38,52 @@ export default class Encoder {
31
38
  return (connector + this.contactCount * 2) % this.contactCount;
32
39
  }
33
40
 
41
+ /**
42
+ *
43
+ * @param {string} letter
44
+ *
45
+ * @returns {boolean}
46
+ */
47
+ verifyLetter(letter) {
48
+ if (letter.length !== 1 || this.alphabet.indexOf(letter) === -1) {
49
+ if (letter !== ' ') {
50
+ console.warn(`Unexpected character ${letter}`);
51
+ }
52
+ return false;
53
+ }
54
+
55
+ return true;
56
+ }
57
+
58
+ /**
59
+ * Call this method to convert a letter to a connector value
60
+ *
61
+ * @param {string} letter
62
+ * @returns {number | undefined}
63
+ */
64
+ letterToConnector(letter) {
65
+ if (!this.verifyLetter(letter)) {
66
+ return;
67
+ }
68
+
69
+ return this.alphabet.indexOf(letter);
70
+ }
71
+
72
+ /**
73
+ * Call this method to turn a connector to a letter value
74
+ *
75
+ * @param {number} connector
76
+ * @returns {string | undefined}
77
+ */
78
+ connectorToLetter(connector) {
79
+ if (connector >= this.alphabet.length) {
80
+ console.warn(`Unexpected connector ${connector}`);
81
+
82
+ }
83
+
84
+ return this.alphabet[connector];
85
+ }
86
+
34
87
  /**
35
88
  * Given an alphabetic connection map, convert that into an array of
36
89
  * numbers. The index into the array or string is the input connector, and
@@ -79,23 +132,78 @@ export default class Encoder {
79
132
  }
80
133
 
81
134
  /**
82
- * Call this method to set a function to be called when important events
83
- * happen to a component.
135
+ *
136
+ * @param {number | string} start
137
+ * @param {number | string} stop
138
+ * @param {Direction} direction
139
+ */
140
+ fireEncodeSet(start, stop, direction) {
141
+ /** @type {EventData} */
142
+ let eventData = {
143
+ name: this.name,
144
+ type: this.type,
145
+ description: `input: ${start}`,
146
+ event: 'input',
147
+ input: start
148
+ }
149
+
150
+ this.fire('input', this.name, eventData);
84
151
 
85
- * @param {Listener} cb the function to be called.
152
+ if (typeof start === 'number') start = this.connectorToLetter(start)
153
+ if (typeof stop === 'number') stop = this.connectorToLetter(stop)
154
+ eventData = {
155
+ name: this.name,
156
+ type: this.type,
157
+ description: `${this.type}: ${this.name} translate ${start} to ${stop}`,
158
+ event: 'translate',
159
+ direction,
160
+ start,
161
+ stop
162
+ }
163
+ this.fire('translate', this.name, eventData);
164
+
165
+ eventData = {
166
+ name: this.name,
167
+ type: this.type,
168
+ description: `output: ${stop}`,
169
+ event: 'output',
170
+ output: stop
171
+ }
172
+ this.fire('output', this.name, eventData);
173
+ }
174
+
175
+ /**
176
+ * Call this method to add a function to be called when important events
177
+ * happen to a component. The name can be used to later remove the listener
178
+ *
179
+ * @param {string} name - the name of the listener
180
+ * @param {Listener} cb - the function to be called.
181
+ */
182
+ listen(name, cb) {
183
+ this.listeners[name] = cb;
184
+ }
185
+
186
+ /**
187
+ * Call this method to remove a listener
188
+ *
189
+ * @param {string} name - the name of the listener
86
190
  */
87
- listen(cb) {
88
- this.cb = cb;
191
+ unlisten(name) {
192
+ delete this.listeners[name];
89
193
  }
90
194
 
91
195
  /**
92
- * Call this method to call the event listener
196
+ * Call this method to call any event listeners
93
197
  *
94
- * @param {String} name the name of the event
95
- * @param {unknown[]} rest the parameters to pass to the callback
198
+ * @param {EventName} event - the event being fired
199
+ * @param {String} name - the name of the component firing the event
200
+ * @param {EventData} data - the event data
96
201
  */
97
- fire(name, ...rest) {
98
- if (this.cb) this.cb(name, ...rest);
202
+ fire(event, name, data) {
203
+ let listeners = Object.values(this.listeners);
204
+ for (let cb of listeners) {
205
+ cb(event, name, data)
206
+ }
99
207
  }
100
208
 
101
209
  }
@@ -11,49 +11,35 @@ import Encoder from "./Encoder.js";
11
11
  * parameters to the constructor and the config method reference the names of
12
12
  * standard Enigma parts. These are retrieved from the Inventory instance
13
13
  */
14
- export default class Enigma {
14
+ export default class Enigma extends Encoder {
15
15
  /**
16
16
  * The constructor for the Enigma. This represents the unconfigurable
17
17
  * settings of the device.
18
18
  *
19
+ * @param {string} name
19
20
  * @param {EnigmaSetup} settings
20
21
  */
21
- constructor(settings) {
22
- let {entryDisc = 'default', reflector, alphabet = STANDARD_ALPHABET, model = "Enigma"} = settings;
22
+ constructor(name, settings) {
23
+ super(name, "Enigma", settings)
24
+ let {entryDisc = 'default', reflector, alphabet = STANDARD_ALPHABET} = settings;
23
25
  let entryDiscSettings = inventory.getEntryDisc(entryDisc)
24
26
 
25
27
  let reflectorSettings = inventory.getReflector(reflector)
26
28
  this.alphabet = alphabet;
27
- this.plugboard = new PlugBoard('plugboard', {});
28
- this.entryDisc = new EntryDisc('entry-disc', entryDiscSettings);
29
- this.reflector = new Reflector('reflector', reflectorSettings)
29
+ this.plugboard = new PlugBoard('default', {});
30
+ this.entryDisc = new EntryDisc('default', entryDiscSettings);
31
+ this.reflector = new Reflector(reflector, reflectorSettings)
30
32
  this.length = alphabet.length;
31
33
  /** @type {Rotor[]} */
32
34
  this._rotors = [];
33
35
  /** @type {{[rotor: number]: boolean}} */
34
36
  this.pending = [];
35
- this.name = model;
36
37
  /** @type {Encoder[]} */
37
38
  this.encoders = [];
38
39
  /**@type {SimplifiedConfiguration & {reflector: string}} */
39
40
  this._configuration = {reflector}
40
41
  }
41
42
 
42
- /**
43
- * Call this method to normalize a connector number to be within the
44
- * the length of the current alphabet
45
- *
46
- * @param {Number} connector the number to be normalized
47
- *
48
- * @returns {Number} the normalized connector number
49
- */
50
- normalize(connector) {
51
- connector += this.length;
52
- connector = connector % this.length
53
-
54
- return connector;
55
- }
56
-
57
43
  /**
58
44
  * the configured rotors
59
45
  *
@@ -94,7 +80,7 @@ export default class Enigma {
94
80
  /** @type {number[]} */
95
81
  let ringOffsets = []
96
82
 
97
- // because the rotors are specified in the reverse other they are used,
83
+ // because the rotors are specified in the reverse order they are used,
98
84
  // we have to do the same for the ringSettings.
99
85
  if (Array.isArray(ringSettings)) {
100
86
  ringSettings = ringSettings.reverse();
@@ -114,7 +100,7 @@ export default class Enigma {
114
100
  }
115
101
 
116
102
  this._rotors = useRotors.map(function(name, idx) {
117
- return new Rotor(`rotor-${name}`, {...inventory.getRotor(name), alphabet: this.alphabet, ringSetting: ringOffsets[idx], cb: this.cb});
103
+ return new Rotor(`${name}`, {...inventory.getRotor(name), alphabet: this.alphabet, ringSetting: ringOffsets[idx], cb: this.cb});
118
104
  }, this);
119
105
 
120
106
  this.encoders = [this.plugboard, this.entryDisc, ...this.rotors];
@@ -131,19 +117,28 @@ export default class Enigma {
131
117
  * stepping between all rotors
132
118
  */
133
119
  step() {
134
- this._rotors.forEach(function(rotor, idx) {
120
+ this._rotors.forEach((rotor, idx) => {
135
121
  if (rotor.isFixed()) return;
136
122
 
137
123
  // This is the double stepping. Only do this for the middle rotor
138
124
  if (rotor.willTurnover() && idx === 1) {
139
125
  this.pending[idx] = true
126
+ /** @type {EventData} */
127
+ let eventData = {
128
+ name: rotor.name,
129
+ event: "double-step",
130
+ offset: rotor.offset,
131
+ description: `rotor ${rotor.name} double stepping from ${rotor.offset}`
132
+ }
133
+
134
+ this.fire('double-step', rotor.name, eventData);
140
135
  };
141
136
 
142
137
  if (this.pending[idx]) {
143
138
  this.pending[idx] = false;
144
139
  if (rotor.step()) this.pending[idx + 1] = true;
145
140
  }
146
- }, this);
141
+ });
147
142
 
148
143
  // The first rotor is always stepping
149
144
  this.pending[0] = true;
@@ -190,23 +185,22 @@ export default class Enigma {
190
185
  */
191
186
  keyPress(letter) {
192
187
  letter = letter.toUpperCase();
193
- if (letter.length !== 1 || this.alphabet.indexOf(letter) === -1) {
194
- if (letter !== ' ')
195
- console.warn(`Unexpected character ${letter}`);
188
+ if (!this.verifyLetter(letter)) {
196
189
  return;
197
190
  }
198
191
 
199
- this.fire('input', this.name, `input ${letter}`, {letter})
192
+ let connector = this.letterToConnector(letter);
193
+
200
194
  this.step();
201
195
 
202
196
  // encode to the right
203
- let connector = this.encoders.reduce(function(connector, encoder, idx) {
197
+ connector = this.encoders.reduce(function(connector, encoder, idx) {
204
198
  connector = encoder.encode('right', connector);
205
199
  return connector;
206
200
  }.bind(this), this.alphabet.indexOf(letter));
207
201
 
208
202
  // reflector
209
- connector = this.reflector.encode('right', connector);
203
+ connector = this.reflector.encode('turn-around', connector);
210
204
 
211
205
  // encode to the left
212
206
  connector = this.encoders.reduceRight(function(connector, encoder) {
@@ -214,9 +208,10 @@ export default class Enigma {
214
208
  return connector;
215
209
  }.bind(this), connector)
216
210
 
217
- letter = this.alphabet[connector];
218
- this.fire('output', this.name, `output ${letter}`, {letter})
219
- return letter;
211
+ const outputLetter = this.connectorToLetter(connector);
212
+
213
+ this.fireEncodeSet(letter, outputLetter, "end-to-end")
214
+ return outputLetter;
220
215
  }
221
216
 
222
217
  /**
@@ -238,27 +233,17 @@ export default class Enigma {
238
233
  }
239
234
 
240
235
  /**
241
- * Call this method to call the event listener
242
236
  *
243
- * @param {String} type the name of the event
244
- * @param {...any} rest the parameters to pass to the callback
237
+ * @param {string} name
238
+ * @param {Listener} cb
245
239
  */
246
- fire(type, ...rest) {
247
- if (this.cb) this.cb(type, ...rest);
248
- }
249
-
250
- /**
251
- * Call this method to set a function to be called when important events
252
- * happen to a component.
240
+ listen(name, cb) {
241
+ super.listen(name, cb);
253
242
 
254
- * @param {Listener} cb the function to be called.
255
- */
256
- listen(cb) {
257
- this.cb = cb;
258
- this.encoders.forEach(function(encoder) {
259
- encoder.listen(cb);
260
- });
243
+ for (let encoder of this.encoders) {
244
+ encoder.listen(name, cb)
245
+ }
261
246
 
262
- this.reflector.listen(cb);
247
+ this.reflector.listen(name, cb);
263
248
  }
264
249
  }
@@ -1,21 +1,6 @@
1
- // type Listener = (
2
- // /** name of the event being fired*/
3
- // event: string,
4
1
 
5
- // /** the name of th component firing the event */
6
- // name: string,
7
2
 
8
- // /** a human readable description of the event */
9
- // message: string,
10
-
11
- // /** event specific data */
12
- // info: object
13
-
14
- // ) => void;
15
-
16
-
17
-
18
- type Listener = (...params: unknown[]) => void;
3
+ type ComponentType = ("Entry Wheel" | "Plugboard" | "Reflector" | "Rotor" | "Enigma")
19
4
 
20
5
  interface EncoderSetup {
21
6
  /** if specified defines an alternate alphabet for the enigma */
@@ -98,4 +83,111 @@ interface RotorSetup extends EncoderSetup {
98
83
  turnovers?: string;
99
84
  }
100
85
 
101
- type Direction = ("right"|"left")
86
+ /**
87
+ * Defines the possible directions for a translation.
88
+ *
89
+ * - **right** - the signal is moving from the right to the left
90
+ * - **left** - the signal is moving from left to right
91
+ * - **turn-around** - the signal is transitioning between directions, this is
92
+ * sent from the reflector
93
+ */
94
+ type Direction = ("right" | "left" | "turn-around" | "end-to-end");
95
+
96
+ /**
97
+ * Defines the possible events sent from the enigma components.
98
+ *
99
+ * **translate** - fired when the input signal is changed from one value to another
100
+ * **input** - fired when a signal first enters the component
101
+ * **output** - fired when the signal exits the component
102
+ *
103
+ */
104
+ type EventName = ("translate" | "input" | "output" | "step" | "double-step");
105
+
106
+ /**
107
+ * Defines the common data for an event
108
+ */
109
+ type EventBase = {
110
+ /** the name of the component sending the event */
111
+ name: string;
112
+
113
+ type: ComponentType;
114
+
115
+ /** a human readable description of the event */
116
+ description: string;
117
+ }
118
+
119
+ /**
120
+ * Defines the data sent for a translate event
121
+ */
122
+ type TranslateData = {
123
+ /** the event name */
124
+ event: EventName = "translate";
125
+
126
+ /** the direction the signal was sent */
127
+ direction: Direction;
128
+
129
+ /** the starting value */
130
+ start: number | string;
131
+
132
+ /** the translated value */
133
+ stop: number | string;
134
+ }
135
+
136
+ /**
137
+ * defines the data sent for a rotor step
138
+ */
139
+ type StepData = {
140
+ /** the event name */
141
+ event: EventName = "step";
142
+
143
+ /** the starting rotor position */
144
+ start: number | string;
145
+
146
+ /** the ending rotor position */
147
+ stop: number | string
148
+
149
+ /** the locations of the turnover points for this rotor */
150
+ turnover: boolean;
151
+ }
152
+
153
+ type DoubleStepData = {
154
+ eventName: EventName = "double-step",
155
+ rotor: string;
156
+ offset: number;
157
+ }
158
+
159
+ /**
160
+ * Defines the data sent with an input event
161
+ */
162
+ type InputData = {
163
+ /** the event name */
164
+ event: EventName = "input";
165
+
166
+ /** the input connection */
167
+ input: number | string;
168
+ }
169
+
170
+ /**
171
+ * Defines the data sent with an output event
172
+ */
173
+ type OutputData = {
174
+ /** the event name */
175
+ event: EventName = "output";
176
+
177
+ /** the output connection */
178
+ output: number | string;
179
+ }
180
+
181
+ /** Defines the discriminated union for all the event data */
182
+ type EventData = EventBase & (TranslateData | StepData | InputData | OutputData);
183
+
184
+ type Listener = (
185
+ /** name of the event being fired*/
186
+ event: string,
187
+
188
+ /** the name of th component firing the event */
189
+ name: string,
190
+
191
+ /** event specific data */
192
+ data: EventData
193
+ ) => void;
@@ -15,7 +15,7 @@ export default class EntryDisc extends Encoder {
15
15
  * between input and output contacts
16
16
  */
17
17
  constructor(name, settings) {
18
- super(name, settings);
18
+ super(name, "Entry Wheel", settings);
19
19
  var {map = STANDARD_ALPHABET} = settings;
20
20
  this.rightMap = this.makeMap(map);
21
21
  this.leftMap = this.makeReverseMap(this.rightMap);
@@ -31,14 +31,8 @@ export default class EntryDisc extends Encoder {
31
31
  */
32
32
  encode(direction, input) {
33
33
  var result = direction === 'right' ? this.rightMap[input]: this.leftMap[input];
34
- var evName = direction === 'right' ? 'encode-right' : 'encode-left';
35
34
 
36
- this.fire(evName, this.name,
37
- `${evName} ${this.name}, input: ${input}, output: ${result}`, {
38
- input: input,
39
- output: result,
40
- }
41
- );
35
+ this.fireEncodeSet(input, result, direction);
42
36
  return result;
43
37
  }
44
38
  }
@@ -14,7 +14,7 @@ export default class PlugBoard extends Encoder {
14
14
  * using an alternate alphabet
15
15
  */
16
16
  constructor(name = 'plugboard', settings = {}) {
17
- super(name, settings);
17
+ super(name, "Plugboard", settings);
18
18
 
19
19
  var {alphabet = STANDARD_ALPHABET, map} = settings;
20
20
  this.alphabet = alphabet;
@@ -64,13 +64,8 @@ export default class PlugBoard extends Encoder {
64
64
  */
65
65
  encode(direction, input) {
66
66
  var result = direction === 'right' ? this.rightMap[input]: this.leftMap[input];
67
- var evName = direction === 'right' ? 'encode-right' : 'encode-left';
68
- this.fire(evName, this.name,
69
- `${evName} ${this.name}, input: ${input}, output: ${result}`, {
70
- input: input,
71
- output: result,
72
- }
73
- );
67
+
68
+ this.fireEncodeSet(input, result, direction);
74
69
 
75
70
  return result;
76
71
  }
@@ -13,7 +13,7 @@ export default class Reflector extends Encoder {
13
13
  * @param {EncoderSetup} settings The definition of the reflector
14
14
  */
15
15
  constructor(name, settings) {
16
- super(name, settings);
16
+ super(name, "Reflector", settings);
17
17
  var {map = STANDARD_ALPHABET} = settings;
18
18
 
19
19
  this.map = this.makeMap(map);
@@ -30,14 +30,10 @@ export default class Reflector extends Encoder {
30
30
  *
31
31
  * @returns {Number} the mapped output connector
32
32
  */
33
- encode(_direction, input) {
33
+ encode(direction, input) {
34
34
  var result = this.map[input];
35
- this.fire('encode', this.name,
36
- `encode ${this.name} ${input} ${result}`,
37
- {
38
- input: input,
39
- output: result,
40
- });
35
+
36
+ this.fireEncodeSet(input, result, direction)
41
37
 
42
38
  return result;
43
39
  }
@@ -17,8 +17,8 @@ export default class Rotor extends Encoder {
17
17
  * define the the rotor and how it is configured.
18
18
  */
19
19
  constructor(name, settings) {
20
- super(name, settings);
21
- var { map = STANDARD_ALPHABET, turnovers = '', ringSetting = 0} = settings
20
+ super(name, "Rotor", settings);
21
+ let { map = STANDARD_ALPHABET, turnovers = '', ringSetting = 0} = settings
22
22
 
23
23
  this.map = [...map];
24
24
  this.rightMap = this.makeMap(map);
@@ -29,10 +29,13 @@ export default class Rotor extends Encoder {
29
29
  this.turnoverLookup = this.makeMap(turnovers);
30
30
  this.offset = 0;
31
31
  if (turnovers === '') this.fixed = true;
32
- this.turnovers = this.turnoverLookup.reduce(function(turnovers, turnover) {
33
- turnovers[turnover] = true;
32
+ this.turnovers = turnovers;
33
+ /** @type {Set<number>} */
34
+ let turnoverSet = new Set();
35
+ this._turnovers = this.turnoverLookup.reduce((turnovers, turnover) => {
36
+ turnovers.add(turnover)
34
37
  return turnovers;
35
- }.bind(this), {});
38
+ }, turnoverSet);
36
39
  }
37
40
 
38
41
  /**
@@ -45,7 +48,7 @@ export default class Rotor extends Encoder {
45
48
  * ring setting.
46
49
  */
47
50
  setStartPosition(connector) {
48
- var pos = this.alphabet.indexOf(connector);
51
+ let pos = this.alphabet.indexOf(connector);
49
52
  this.offset = this.normalize(pos - this.ringOffset);
50
53
  }
51
54
 
@@ -63,54 +66,49 @@ export default class Rotor extends Encoder {
63
66
  * @returns {Number} the output connector in physical space.
64
67
  */
65
68
  encode(direction, input) {
66
- var map = direction === 'right' ? this.rightMap : this.leftMap;
67
- var evName = direction === 'right' ? 'encode-right' : 'encode-left';
69
+ let map = direction === 'right' ? this.rightMap : this.leftMap;
68
70
 
69
71
  //find the logical position of the input connector in the wiring map
70
- var index = this.normalize(input + this.offset);
72
+ let index = this.normalize(input + this.offset);
71
73
 
72
74
  // get the logical output connector and convert that to the physical
73
75
  // output connector
74
- var output = map[index];
75
- var result = this.normalize(output - this.offset);
76
-
77
- this.fire(evName, this.name,
78
- `${evName} ${this.name}, input: ${input} output: ${result} relative input: ${index}, relative output: ${output} rotation: ${this.offset}`,
79
- {
80
- input : input,
81
- output: result,
82
- logicalInput: index,
83
- logicalOutput: output,
84
- rotation: this.offset,
85
- }
86
- );
76
+ let output = map[index];
77
+ let result = this.normalize(output - this.offset);
87
78
 
79
+ this.fireEncodeSet(input, result, direction);
88
80
  return result;
89
81
  }
90
82
 
91
83
  /**
92
84
  * Call this method to step the rotor
93
85
  *
94
- * @returns {Boolean} true true if the next rotor should be stepped
86
+ * @returns {Boolean} true if the next rotor should be stepped
95
87
  */
96
88
  step() {
97
89
  // Because the turnover notch is attached to the outer ring, and the
98
90
  // logical coordinates are based on the wiring, the logical position of
99
91
  // the notch for turnover needs to be adjusted to remove the ring offset.
92
+ let start = this.offset;
100
93
  this.offset = this.normalize(this.offset + 1);
101
- var turnoverOffset = this.normalize(this.offset + this.ringOffset);
94
+ let stop = this.offset;
95
+ let turnoverOffset = this.normalize(this.offset + this.ringOffset);
96
+
102
97
 
103
98
  // turnover happens when we step past the turnover point
104
- var turnover = this.turnovers[this.normalize(turnoverOffset - 1)];
99
+ let turnover = this._turnovers.has(this.normalize(turnoverOffset - 1));
100
+
101
+ /** @type {EventData} */
102
+ let eventData = {
103
+ name: this.name,
104
+ type: this.type,
105
+ description: `step ${this.name}, ${this.offset} ${this.normalize(this.offset + this.ringOffset)} ${turnoverOffset} ${Boolean(turnover)}`,
106
+ event: 'step',
107
+ turnover, start, stop
108
+ }
105
109
 
106
- this.fire('step', this.name,
107
- `step ${this.name}, ${this.offset} ${this.normalize(this.offset + this.ringOffset)} ${turnoverOffset} ${Boolean(turnover)}`,
108
- {
109
- rotation: this.offset,
110
- ringSetting: this.ringOffset,
111
- turnover: Boolean(turnover),
112
- }
113
- );
110
+ // console.log("firing", eventData);
111
+ this.fire('step', this.name, eventData);
114
112
 
115
113
  return turnover;
116
114
  }
@@ -123,10 +121,18 @@ export default class Rotor extends Encoder {
123
121
  * @returns true if this rotor will turnover on the next step
124
122
  */
125
123
  willTurnover() {
126
- var turnoverOffset = this.normalize(this.offset + this.ringOffset);
124
+ let turnoverOffset = this.normalize(this.offset + this.ringOffset);
127
125
 
128
126
  // double stepping happens when we step to the turnover point
129
- return this.turnovers[turnoverOffset];
127
+ return this._turnovers.has(turnoverOffset);
128
+ }
129
+
130
+ shouldTurnover() {
131
+ let turnoverOffset = this.normalize(this.offset + 1 + this.ringOffset);
132
+
133
+ // double stepping happens when we step to the turnover point
134
+ return this._turnovers.has(turnoverOffset);
135
+
130
136
  }
131
137
 
132
138
  /**
@@ -5,11 +5,12 @@ import Enigma from "../Enigma.js";
5
5
  import { enigmaData } from './EnigmaData.js';
6
6
 
7
7
  describe('Enigma Test Cases', function() {
8
+ /** @type {Enigma} */
8
9
  var enigma;
9
10
 
10
11
  function messageLoop(messages, which, cb) {
11
12
  return messages.find(function(message) {
12
- var enigma = new Enigma({
13
+ var enigma = new Enigma("Test", {
13
14
  reflector: message.setup.reflector
14
15
  });
15
16
 
@@ -29,39 +30,42 @@ describe('Enigma Test Cases', function() {
29
30
  describe('Stepping', function() {
30
31
  var steps = {}
31
32
  beforeEach(function() {
32
- enigma = new Enigma({
33
+ enigma = new Enigma("I", {
33
34
  reflector: 'B',
34
35
  });
35
36
 
36
37
  enigma.configure({rotors: ['I', 'II', 'III']});
37
38
 
38
- enigma.listen(function(event, name, message, info) {
39
+ listen()
40
+ });
41
+
42
+ function listen() {
43
+ enigma.listen('test', (event, name, data) => {
39
44
  if (event === 'step') {
40
45
  steps[name] = steps[name] || [];
41
- steps[name].push(info);
46
+ steps[name].push(data);
42
47
  }
43
48
  })
44
-
45
- });
49
+ }
46
50
 
47
51
  it ('should step only the right-most rotor when not at turnover', function() {
48
52
  steps = {};
49
53
  enigma.encode('AAA', 'A');
50
54
  var stepped = Object.keys(steps);
51
55
  expect(stepped.length).toBe(1);
52
- expect(stepped[0]).toBe('rotor-III');
56
+ expect(stepped[0]).toBe('III');
53
57
  });
54
58
 
55
59
  it ('should step the next rotor when the previous turns over', function() {
56
60
  steps = {};
57
61
  enigma.encode('AAV', 'A');
58
- expect(steps['rotor-II'].length).toBe(1);
62
+ expect(steps['II'].length).toBe(1);
59
63
  });
60
64
 
61
65
  it ('should double step when reaching the turn over', function() {
62
66
  steps = {};
63
67
  enigma.encode('ADV', 'AA');
64
- expect(steps['rotor-II'].length).toBe(2);
68
+ expect(steps['II'].length).toBe(2);
65
69
  });
66
70
 
67
71
  it ('should double step on first step', function() {
@@ -69,8 +73,10 @@ describe('Enigma Test Cases', function() {
69
73
 
70
74
  enigma.configure({rotors: ['III', 'VI', 'VIII'], ringSettings: [1, 8, 13]});
71
75
 
76
+ listen();
77
+
72
78
  enigma.encode('UZV', 'AA');
73
- expect(steps['rotor-VI'].length).toBe(1);
79
+ expect(steps['VI'].length).toBe(1);
74
80
  });
75
81
  })
76
82
 
@@ -4,7 +4,7 @@ export const rotorData = {
4
4
  map: 'BCDA',
5
5
  reverseMap: [3, 0, 1, 2],
6
6
  turnovers: 'AM',
7
- turnoversMap: {0: true, 12: true}
7
+ turnoversMap: [0, 12]
8
8
  },
9
9
  I: {
10
10
  map: 'EKMFLGDQVZNTOWYHXUSPAIBRCJ',
@@ -29,7 +29,9 @@ describe('Rotor Test Cases', function() {
29
29
  turnovers: rotorData.createData.turnovers,
30
30
  });
31
31
 
32
- expect(rotor.turnovers).toEqual(rotorData.createData.turnoversMap);
32
+ let turnovers = [...rotor._turnovers];
33
+
34
+ expect(turnovers).toEqual(rotorData.createData.turnoversMap);
33
35
  })
34
36
  })
35
37
 
@@ -136,7 +136,7 @@ export default class Generator {
136
136
  */
137
137
  createRandomEnigma(model = "Enigma", reflectors = ['A', 'B', 'C']) {
138
138
  let reflector = Random.pickOne(reflectors);
139
- return new Enigma({model, reflector});
139
+ return new Enigma(model, {model, reflector});
140
140
  }
141
141
 
142
142
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ondoher/enigma",
3
- "version": "1.0.1",
4
- "description": "Enigma machine simulator reference with tools and documentation to help you build your own.",
3
+ "version": "1.0.3",
4
+ "description": "Enigma machine simulator implementation with tools and documentation to help you build your own.",
5
5
  "main": "./lib/index.js",
6
6
  "scripts": {
7
7
  "test": "jasmine"