Class: RotorMachine::Rotor

Inherits:
Object
  • Object
show all
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

Instance Method Summary collapse

Constructor Details

#initialize(rotor, start_on = 0, step_size = 1) ⇒ Rotor

Initialize a new rotor.


88
89
90
91
92
93
# File 'lib/rotor_machine/rotor.rb', line 88

def initialize(rotor, start_on=0, step_size=1)
  @letters = rotor.chars.freeze
  self.position = start_on
  @step_size = step_size
  @wrapped = nil
end

Instance Attribute Details

#lettersObject (readonly)

Returns the value of attribute letters


32
33
34
# File 'lib/rotor_machine/rotor.rb', line 32

def letters
  @letters
end

#positionObject

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_sizeObject

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) ⇒ Object


203
204
205
206
207
# File 'lib/rotor_machine/rotor.rb', line 203

def ==(another_rotor)
  @letters == another_rotor.letters &&
  position == another_rotor.position &&
  step_size == another_rotor.step_size
end

#current_letterString

Get the current letter position of the rotor.


158
159
160
# File 'lib/rotor_machine/rotor.rb', line 158

def current_letter
  @letters[@position]
end

#forward(letter) ⇒ String

Return the “forward” (left-to-right) transposition of the supplied letter.


121
122
123
124
125
126
127
# File 'lib/rotor_machine/rotor.rb', line 121

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.


135
136
137
138
139
140
141
# File 'lib/rotor_machine/rotor.rb', line 135

def reverse(letter)
  if ALPHABET.include?(letter)
    ALPHABET[((@letters.index(letter) - self.position) % @letters.length)]
  else
    letter
  end
end

#rotor_kindString

Return the current rotor's “kind” (a string containing the mappings of the rotor.


167
168
169
# File 'lib/rotor_machine/rotor.rb', line 167

def rotor_kind
  @letters.join("")
end

#rotor_kind_nameSymbol

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.


179
180
181
182
# File 'lib/rotor_machine/rotor.rb', line 179

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.


148
149
150
151
152
# File 'lib/rotor_machine/rotor.rb', line 148

def step(step_size=@step_size)
  old_position = @position
  @position = (@position + step_size) % @letters.length
  @wrapped = (old_position > @position)
end

#to_sString

Generate a human-readable representation of the RotorMachine::Rotor's state.


199
200
201
# File 'lib/rotor_machine/rotor.rb', line 199

def to_s
  return "a RotorMachine::Rotor of type '#{self.rotor_kind_name}', position=#{self.position} (#{self.current_letter}), step_size=#{@step_size}"
end

#wrapped?Booleam

Check if the last #step operation caused the rotor to wrap around in position. This is used by the Machine to determine whether to advance the adjacent rotor.


191
192
193
# File 'lib/rotor_machine/rotor.rb', line 191

def wrapped?
  @wrapped
end