2019-01-03 17:36:56 +01:00
/ * *
* Emulation of the Enigma machine .
*
* @ author s2224834
* @ copyright Crown Copyright 2019
* @ license Apache - 2.0
* /
import Operation from "../Operation" ;
import OperationError from "../errors/OperationError" ;
2019-01-03 19:51:39 +01:00
import { ROTORS , LETTERS , ROTORS _OPTIONAL , REFLECTORS , Rotor , Reflector , Plugboard , EnigmaMachine } from "../lib/Enigma" ;
2019-01-03 17:36:56 +01:00
/ * *
* Enigma operation
* /
2019-01-03 19:51:39 +01:00
class Enigma extends Operation {
2019-01-03 17:36:56 +01:00
/ * *
* Enigma constructor
* /
constructor ( ) {
super ( ) ;
this . name = "Enigma" ;
this . module = "Default" ;
this . description = "Encipher/decipher with the WW2 Enigma machine.<br><br>The standard set of German military rotors and reflectors are provided. To configure the plugboard, enter a string of connected pairs of letters, e.g. <code>AB CD EF</code> connects A to B, C to D, and E to F. This is also used to create your own reflectors. To create your own rotor, enter the letters that the rotor maps A to Z to, in order, optionally followed by <code><</code> then a list of stepping points.<br>This is deliberately fairly permissive with rotor placements etc compared to a real Enigma (on which, for example, a four-rotor Enigma uses the thin reflectors and the beta or gamma rotor in the 4th slot)." ;
this . infoURL = "https://wikipedia.org/wiki/Enigma_machine" ;
this . inputType = "string" ;
this . outputType = "string" ;
this . args = [
{
name : "1st (right-hand) rotor" ,
type : "editableOption" ,
2019-01-03 19:51:39 +01:00
value : ROTORS ,
2019-01-03 17:36:56 +01:00
// Default config is the rotors I-III *left to right*
defaultIndex : 2
} ,
{
name : "1st rotor ring setting" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
name : "1st rotor initial value" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
2019-01-08 19:25:42 +01:00
name : "2nd (middle) rotor" ,
2019-01-03 17:36:56 +01:00
type : "editableOption" ,
2019-01-03 19:51:39 +01:00
value : ROTORS ,
2019-01-03 17:36:56 +01:00
defaultIndex : 1
} ,
{
name : "2nd rotor ring setting" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
name : "2nd rotor initial value" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
2019-01-08 19:25:42 +01:00
name : "3rd (left-hand) rotor" ,
2019-01-03 17:36:56 +01:00
type : "editableOption" ,
2019-01-03 19:51:39 +01:00
value : ROTORS ,
2019-01-03 17:36:56 +01:00
defaultIndex : 0
} ,
{
name : "3rd rotor ring setting" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
name : "3rd rotor initial value" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
2019-01-08 19:25:42 +01:00
name : "4th (left-most, only some models) rotor" ,
2019-01-03 17:36:56 +01:00
type : "editableOption" ,
2019-01-03 19:51:39 +01:00
value : ROTORS _OPTIONAL ,
2019-01-03 17:36:56 +01:00
defaultIndex : 10
} ,
2019-01-03 18:51:20 +01:00
{
name : "4th rotor ring setting" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 18:51:20 +01:00
} ,
2019-01-03 17:36:56 +01:00
{
name : "4th rotor initial value" ,
type : "option" ,
2019-01-03 19:51:39 +01:00
value : LETTERS
2019-01-03 17:36:56 +01:00
} ,
{
name : "Reflector" ,
type : "editableOption" ,
2019-01-03 19:51:39 +01:00
value : REFLECTORS
2019-01-03 17:36:56 +01:00
} ,
{
name : "Plugboard" ,
type : "string" ,
value : ""
} ,
{
name : "Strict output" ,
hint : "Remove non-alphabet letters and group output" ,
type : "boolean" ,
value : true
} ,
] ;
}
/ * *
* Helper - for ease of use rotors are specified as a single string ; this
* method breaks the spec string into wiring and steps parts .
*
* @ param { string } rotor - Rotor specification string .
* @ param { number } i - For error messages , the number of this rotor .
* @ returns { string [ ] }
* /
parseRotorStr ( rotor , i ) {
if ( rotor === "" ) {
throw new OperationError ( ` Rotor ${ i } must be provided. ` ) ;
}
if ( ! rotor . includes ( "<" ) ) {
return [ rotor , "" ] ;
}
return rotor . split ( "<" , 2 ) ;
}
/ * *
* @ param { string } input
* @ param { Object [ ] } args
* @ returns { string }
* /
run ( input , args ) {
2019-01-03 18:51:20 +01:00
const reflectorstr = args [ 12 ] ;
const plugboardstr = args [ 13 ] ;
const removeOther = args [ 14 ] ;
2019-01-03 17:36:56 +01:00
const rotors = [ ] ;
2019-01-03 18:51:20 +01:00
for ( let i = 0 ; i < 4 ; i ++ ) {
if ( i === 3 && args [ i * 3 ] === "" ) {
// No fourth rotor
break ;
}
const [ rotorwiring , rotorsteps ] = this . parseRotorStr ( args [ i * 3 ] , 1 ) ;
2019-01-03 19:51:39 +01:00
rotors . push ( new Rotor ( rotorwiring , rotorsteps , args [ i * 3 + 1 ] , args [ i * 3 + 2 ] ) ) ;
2019-01-03 17:36:56 +01:00
}
2019-01-03 19:51:39 +01:00
const reflector = new Reflector ( reflectorstr ) ;
const plugboard = new Plugboard ( plugboardstr ) ;
2019-01-03 17:36:56 +01:00
if ( removeOther ) {
input = input . replace ( /[^A-Za-z]/g , "" ) ;
}
2019-01-03 19:51:39 +01:00
const enigma = new EnigmaMachine ( rotors , reflector , plugboard ) ;
2019-01-03 17:36:56 +01:00
let result = enigma . crypt ( input ) ;
if ( removeOther ) {
// Five character cipher groups is traditional
result = result . replace ( /([A-Z]{5})(?!$)/g , "$1 " ) ;
}
return result ;
}
/ * *
* Highlight Enigma
* This is only possible if we ' re passing through non - alphabet characters .
*
* @ param { Object [ ] } pos
* @ param { number } pos [ ] . start
* @ param { number } pos [ ] . end
* @ param { Object [ ] } args
* @ returns { Object [ ] } pos
* /
highlight ( pos , args ) {
if ( args [ 13 ] === false ) {
return pos ;
}
}
/ * *
* Highlight Enigma in reverse
*
* @ param { Object [ ] } pos
* @ param { number } pos [ ] . start
* @ param { number } pos [ ] . end
* @ param { Object [ ] } args
* @ returns { Object [ ] } pos
* /
highlightReverse ( pos , args ) {
if ( args [ 13 ] === false ) {
return pos ;
}
}
}
2019-01-03 19:51:39 +01:00
export default Enigma ;