Class: RotorMachine::Rotor
- Inherits:
-
Object
- Object
- RotorMachine::Rotor
- Defined in:
- lib/rotor_machine/rotor.rb
Overview
Implment an Enigma machine rotor.
The Rotor is the central component of the Enigma machine’s polyalphabetic substitution cipher. Each rotor consisted of a ring with a series of internal connections and wiring which mapped input letters on the left side of the rotor to (different) output letters on the right side. The signal from the Enigma’s keyboard would pass twice through the rotor/reflector stack (and plugboard) in opposite directions before being displayed. This ensured the algorithm was symmetrical; without this property, the Enigma could not both encipher and decipher text.
Adding to the complexity of the algorithm, the rotors rotated after enciphering each character. In a standard 3-rotor Enigma machine, the rightmost rotor advanced position for each character. The middle rotor advanced one position with each full revolution of the right rotor, and the left rotor advanced one position with each full rotation of the middle rotor. These rotations permuted the signal path, so a sequence of several of the same input character would produce different output characters.
The Rotor as implemented here allows the ‘step_size` (the number of positions each rotor advances when it’s stepped) to be varied.
Constant Summary collapse
- ROTOR_IC =
Provides the configuration of the German IC Enigma RotorMachine::Rotor.
"DMTWSILRUYQNKFEJCAZBPGXOHV".freeze
- ROTOR_IIC =
Provides the configuration of the German IIC Enigma RotorMachine::Rotor.
"HQZGPJTMOBLNCIFDYAWVEUSRKX".freeze
- ROTOR_IIIC =
Provides the configuration of the German IIIC Enigma RotorMachine::Rotor.
"UQNTLSZFMREHDPXKIBVYGJCWOA".freeze
- ROTOR_I =
Provides the configuration of the German I Enigma RotorMachine::Rotor.
"JGDQOXUSCAMIFRVTPNEWKBLZYH".freeze
- ROTOR_II =
Provides the configuration of the German II Enigma RotorMachine::Rotor.
"NTZPSFBOKMWRCJDIVLAEYUXHGQ".freeze
- ROTOR_III =
Provides the configuration of the German III Enigma RotorMachine::Rotor.
"JVIUBHTCDYAKEQZPOSGXNRMWFL".freeze
- ROTOR_UKW =
Provides the configuration of the German UKW Enigma RotorMachine::Rotor.
"QYHOGNECVPUZTFDJAXWMKISRBL".freeze
- ROTOR_ETW =
Provides the configuration of the German ETW Enigma RotorMachine::Rotor.
"QWERTZUIOASDFGHJKPYXCVBNML".freeze
- ALPHABET =
Provides the alphabet in order. Used for mapping rotor indices, but could also be used as a RotorMachine::Rotor configuration.
"ABCDEFGHIJKLMNOPQRSTUVWXYZ".freeze
Instance Attribute Summary collapse
-
#letters ⇒ Object
readonly
Returns the value of attribute letters.
-
#position ⇒ Object
Query the current numeric position (0-based) of the rotor.
-
#step_size ⇒ Object
Get or set the ‘step_size` - the number of positions the rotor should advance every time it’s stepped.
Instance Method Summary collapse
-
#==(another_rotor) ⇒ Boolean
Compare this Rotor to another one.
-
#current_letter ⇒ String
Get the current letter position of the rotor.
-
#forward(letter) ⇒ String
Return the “forward” (left-to-right) transposition of the supplied letter.
-
#initialize(rotor, start_on = 0, step_size = 1) ⇒ Rotor
constructor
Initialize a new rotor.
-
#reverse(letter) ⇒ String
Return the “reverse” (right-to-left) transposition of the supplied letter.
-
#rotor_kind ⇒ String
Return the current rotor’s “kind” (a string containing the mappings of the rotor..
-
#rotor_kind_name ⇒ Symbol
Return the name of this kind of rotor.
-
#step(step_size = @step_size) ⇒ Object
Step the rotor.
-
#to_s ⇒ String
Generate a human-readable representation of the Rotor‘s state.
-
#wrapped? ⇒ Booleam
Check if the last #step operation caused the rotor to wrap around in position.
Constructor Details
#initialize(rotor, start_on = 0, step_size = 1) ⇒ Rotor
Initialize a new rotor.
88 89 90 91 92 93 94 |
# File 'lib/rotor_machine/rotor.rb', line 88 def initialize(rotor, start_on=0, step_size=1) raise ArgumentError, "Initialization string contains duplicate letters" unless rotor.is_uniq? @letters = rotor.chars.freeze self.position = start_on @step_size = step_size @wrapped = nil end |
Instance Attribute Details
#letters ⇒ Object (readonly)
Returns the value of attribute letters.
32 33 34 |
# File 'lib/rotor_machine/rotor.rb', line 32 def letters @letters end |
#position ⇒ Object
Query the current numeric position (0-based) of the rotor. The #position= method provides a setter for this property to allow for setting the RotorMachine::Rotor based on either a numeric position or a letter position.
30 31 32 |
# File 'lib/rotor_machine/rotor.rb', line 30 def position @position end |
#step_size ⇒ Object
Get or set the ‘step_size` - the number of positions the rotor should advance every time it’s stepped.
37 38 39 |
# File 'lib/rotor_machine/rotor.rb', line 37 def step_size @step_size end |
Instance Method Details
#==(another_rotor) ⇒ Boolean
Compare this RotorMachine::Rotor to another one.
Returns True if the configuration of the supplied RotorMachine::Rotor matches this one, false otherwise.
212 213 214 215 216 |
# File 'lib/rotor_machine/rotor.rb', line 212 def ==(another_rotor) @letters == another_rotor.letters && position == another_rotor.position && step_size == another_rotor.step_size end |
#current_letter ⇒ String
Get the current letter position of the rotor.
159 160 161 |
# File 'lib/rotor_machine/rotor.rb', line 159 def current_letter @letters[@position] end |
#forward(letter) ⇒ String
Return the “forward” (left-to-right) transposition of the supplied letter.
122 123 124 125 126 127 128 |
# File 'lib/rotor_machine/rotor.rb', line 122 def forward(letter) if ALPHABET.include?(letter) @letters[((ALPHABET.index(letter) + self.position) % @letters.length)] else letter end end |
#reverse(letter) ⇒ String
Return the “reverse” (right-to-left) transposition of the supplied letter.
136 137 138 139 140 141 142 |
# File 'lib/rotor_machine/rotor.rb', line 136 def reverse(letter) if ALPHABET.include?(letter) ALPHABET[((@letters.index(letter) - self.position) % @letters.length)] else letter end end |
#rotor_kind ⇒ String
Return the current rotor’s “kind” (a string containing the mappings of the rotor.
168 169 170 |
# File 'lib/rotor_machine/rotor.rb', line 168 def rotor_kind @letters.join("") end |
#rotor_kind_name ⇒ Symbol
Return the name of this kind of rotor.
If the rotor’s sequence matches one of the defined class constants for a standsard Enigma rotor, the name of the constant will be returned as a symbol. Otherwise, :CUSTOM is returned.
180 181 182 183 |
# File 'lib/rotor_machine/rotor.rb', line 180 def rotor_kind_name self.class.constants.each { |k| return k if (self.class.const_get(k) == rotor_kind) } return :CUSTOM end |
#step(step_size = @step_size) ⇒ Object
Step the rotor.
149 150 151 152 153 |
# File 'lib/rotor_machine/rotor.rb', line 149 def step(step_size=@step_size) old_position = @position @position = (@position + step_size) % @letters.length @wrapped = (old_position > @position) end |
#to_s ⇒ String
Generate a human-readable representation of the RotorMachine::Rotor‘s state.
200 201 202 |
# File 'lib/rotor_machine/rotor.rb', line 200 def to_s return "a RotorMachine::Rotor of type '#{self.rotor_kind_name}', position=#{self.position} (#{self.current_letter}), step_size=#{@step_size}" end |