enigma_machine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,5 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  .rvmrc
6
+ doc/*
7
+ .yardoc
data/Rakefile CHANGED
@@ -5,3 +5,6 @@ require "rspec/core/rake_task"
5
5
  RSpec::Core::RakeTask.new(:spec)
6
6
 
7
7
  task :default => :spec
8
+
9
+ require 'yard'
10
+ YARD::Rake::YardocTask.new
@@ -18,4 +18,6 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_development_dependency "rspec"
20
20
  s.add_development_dependency "rake"
21
+ s.add_development_dependency "yard", '~> 0.8.2'
22
+ s.add_development_dependency "redcarpet"
21
23
  end
@@ -1,8 +1,47 @@
1
1
  class EnigmaMachine
2
- ConfigurationError = Class.new(StandardError)
2
+ class ConfigurationError < StandardError; end
3
3
 
4
4
  ALPHABET = ('A'..'Z').to_a
5
5
 
6
+ # Constructs an enigma machine with given reflector, rotors and plugboard settings.
7
+ #
8
+ # Examples:
9
+ #
10
+ # You can use the standard reflectors and rotors:
11
+ #
12
+ # EnigmaMachine.new(
13
+ # :reflector => :B,
14
+ # :rotors => [[:i, 10], [:ii, 14], [:iii, 21]],
15
+ # :plug_pairs => %w(AP BR CM FZ GJ IL NT OV QS WX)
16
+ # )
17
+ #
18
+ # Or you can use custom reflector and rotor configurations:
19
+ #
20
+ # EnigmaMachine.new(
21
+ # :reflector => %w(AF BV CP DJ EI GO HY KR LZ MX NW TQ SU),
22
+ # :rotors => [
23
+ # ['BDFHJLCPRTXVZNYEIWGAKMUSQO_V', 10],
24
+ # ['JPGVOUMFYQBENHZRDKASXLICTW_MZ', 14],
25
+ # ['ABCDEFGHIJKLMNOPQRSTUVWXYZ_AHT', 21]
26
+ # ],
27
+ # :plug_pairs => %w(AP BR CM FZ GJ IL NT OV QS WX)
28
+ # )
29
+ #
30
+ # When specifying a custom rotor, the letters after the _ indicate the notch positions.
31
+ #
32
+ # @param config [Hash] A Hash of configuration params
33
+ # @option config [Symbol,Array<String>] :reflector Which reflector to use. Reference one of the
34
+ # standard ones by Symbol, or pass in an array of
35
+ # letter pairs for a custom configuration.
36
+ # @option config [Array<Array>] :rotors Array of details for 3 or more rotors. Specified from
37
+ # left to right. Each one is specified as a tuple of the rotor spec,
38
+ # and ring setting. The rotor_spec can be one of the standard rotors
39
+ # referenced by Symbol, or a custom rotor described by a config string
40
+ # (see Examples).
41
+ # @option config [Array<String>] :plug_pairs an Optional array of letter pairs specifying the plugboard
42
+ # configuration. If omitted, no plugboard substitutions will be performed.
43
+ #
44
+ # @raise [ConfigurationError] if an invalid configuration was specified
6
45
  def initialize(config)
7
46
  @reflector = Reflector.new config[:reflector]
8
47
  @rotors = []
@@ -12,17 +51,35 @@ class EnigmaMachine
12
51
  @plugboard = Plugboard.new(config[:plug_pairs] || [], @rotors.last)
13
52
  end
14
53
 
54
+ # Set the rotors to the given positions.
55
+ #
56
+ # Pass in Positions for the rotors from left to right
57
+ #
58
+ # @param positions [List<String>] the positions to set the rotors to
59
+ # @return [void]
15
60
  def set_rotors(*positions)
16
61
  positions.each_with_index do |position, i|
17
62
  @rotors[i].position = position
18
63
  end
19
64
  end
20
65
 
66
+ # Simulates pressing a given key on the machine keyboard.
67
+ #
68
+ # Advances the rotors, and then translates the letter
69
+ #
70
+ # @param letter [String] the letter to be translated
71
+ # @return [String] the translated letter
21
72
  def press_key(letter)
22
73
  advance_rotors
23
74
  @plugboard.translate(letter)
24
75
  end
25
76
 
77
+ # Translates the given message using the current settings
78
+ #
79
+ # This will pass through space and - unmodified, and discard any other non-alpha characters.
80
+ #
81
+ # @param message [String] the message to be translated
82
+ # @return [String] the translated message
26
83
  def translate(message)
27
84
  message.upcase.each_char.map do |letter|
28
85
  case letter
@@ -1,16 +1,35 @@
1
1
  class EnigmaMachine
2
2
  class Plugboard
3
- def initialize(mapping_pairs, decorated)
3
+ # Construct a new plugboard
4
+ #
5
+ # Example:
6
+ #
7
+ # Plugboard.new(%w(AB CD EF GH), @rotor)
8
+ #
9
+ # @param mapping_pairs [Array<String>] A list of letter pairs to be connected
10
+ # @param rotor [#translate] the rightmost rotor that will be called next in the processing chain
11
+ # @raise [ConfigurationError] if an invalid mapping passed in (not pairs of leters, or a letter connected more than once)
12
+ def initialize(mapping_pairs, rotor)
4
13
  build_mapping(mapping_pairs)
5
- @decorated = decorated
14
+ @decorated = rotor
6
15
  end
7
16
 
17
+ # Translate a letter
18
+ #
19
+ # This performs a substitution, calls the rotor to do the rest of the translation, and then
20
+ # substitutes the result on the way back out.
21
+ #
22
+ # @param letter [String] the letter to be translated
23
+ # @return [String] the translated letter
8
24
  def translate(letter)
9
25
  step = substitute(letter)
10
26
  step = @decorated.translate(step)
11
27
  substitute(step)
12
28
  end
13
29
 
30
+ # Substitutes a letter according the configured plug pairs
31
+ # @param letter [String] the letter to be substituted
32
+ # @return [String] the substituted letter
14
33
  def substitute(letter)
15
34
  @mapping[letter] || letter
16
35
  end
@@ -8,6 +8,17 @@ class EnigmaMachine
8
8
  :Cthin => %w(AR BD CO EJ FN GT HK IV LM PW QZ SX UY),
9
9
  }
10
10
 
11
+ # Construct a new reflector
12
+ #
13
+ # Examples:
14
+ #
15
+ # Reflector.new(:B)
16
+ # Reflector.new(%w(AY BR CU DH EQ FS GL IP JX KN MO TZ VW))
17
+ #
18
+ # @param mapping [Symbol,Array<String>] A symbol representing one of the standard reflectors or
19
+ # an array of 13 letter pairs to be swapped by the reflector
20
+ # @raise [ConfigurationError] if an invalid mapping is passed in (unrecognised standard reflector,
21
+ # not 13 pairs of letters, or a letter appearing more than once)
11
22
  def initialize(mapping)
12
23
  if mapping.is_a?(Symbol)
13
24
  raise ConfigurationError unless STANDARD_MAPPINGS.has_key?(mapping)
@@ -17,6 +28,11 @@ class EnigmaMachine
17
28
  build_mapping(mapping)
18
29
  end
19
30
 
31
+ # @!method translate(letter)
32
+ # simply perform a substitution
33
+ #
34
+ # @param letter [String] the letter to be substituted
35
+ # @return [String] the substituted letter
20
36
  alias :translate :substitute
21
37
  end
22
38
  end
@@ -13,6 +13,20 @@ class EnigmaMachine
13
13
  :gamma => "FSOKANUERHMBTIYCWLQPZXVGJD_", # in position 4, and hence have no notches.
14
14
  }
15
15
 
16
+ # Construct a new rotor
17
+ #
18
+ # Examples:
19
+ #
20
+ # Rotor.new(:ii, 12, @next)
21
+ # Rotor.new("BDFHJLCPRTXVZNYEIWGAKMUSQO_V", 15, @next)
22
+ #
23
+ # When specifying a custom rotor_spec, the letters after the _ indicate the notch positions
24
+ #
25
+ # @param rotor_spec [Symbol, String] A symbol representing one of the standard rotors or
26
+ # a string specifying the mapping and notch positions (see Examples)
27
+ # @param ring_setting [Integer] The ring setting for the rotor.
28
+ # @param decorated [#translate] The next component in the processing chain (the next rotor, or the reflector)
29
+ # @raise [ConfigurationError] if an invalid rotor_spec is passed in (unrecognised standard rotor)
16
30
  def initialize(rotor_spec, ring_setting, decorated)
17
31
  if rotor_spec.is_a?(Symbol)
18
32
  raise ConfigurationError unless STANDARD_ROTORS.has_key?(rotor_spec)
@@ -26,33 +40,56 @@ class EnigmaMachine
26
40
  self.position = 'A'
27
41
  end
28
42
 
43
+ # Set the position of the rotor
44
+ #
45
+ # @param letter [String] the letter position to set the rotor to
46
+ # @return [void]
29
47
  def position=(letter)
30
48
  @position = ALPHABET.index(letter)
31
49
  end
50
+ # @return [String] the current position of the rotor
32
51
  def position
33
52
  ALPHABET[@position]
34
53
  end
35
54
 
55
+ # Advance the position of the rotor.
56
+ # @return [void]
36
57
  def advance_position
37
58
  @position = (@position + 1).modulo(26)
38
59
  end
39
60
 
61
+ # Is the rotor currently at a notch position.
40
62
  def at_notch?
41
63
  @notch_positions.include?(self.position)
42
64
  end
43
65
 
66
+ # Perform the forward translation of a letter (i.e. the path towards the reflector)
67
+ #
68
+ # @param letter [String] the letter to be translated
69
+ # @return [String] the translated letter
44
70
  def forward(letter)
45
71
  index = add_offset ALPHABET.index(letter)
46
72
  new_index = sub_offset @mapping[index]
47
73
  ALPHABET[new_index]
48
74
  end
49
75
 
76
+ # Perform the reverse translation of a letter (i.e. the path returning from the reflector)
77
+ #
78
+ # @param letter [String] the letter to be translated
79
+ # @return [String] the translated letter
50
80
  def reverse(letter)
51
81
  index = add_offset ALPHABET.index(letter)
52
82
  new_index = sub_offset @mapping.index(index)
53
83
  ALPHABET[new_index]
54
84
  end
55
85
 
86
+ # Translate a letter
87
+ #
88
+ # This performs a forward substitution, calls the next component to do the rest of the translation, and then
89
+ # reverse substitutes the result on the way back out.
90
+ #
91
+ # @param letter [String] the letter to be translated
92
+ # @return [String] the translated letter
56
93
  def translate(input)
57
94
  step = forward(input)
58
95
  step = @decorated.translate(step)
@@ -1,3 +1,3 @@
1
1
  class EnigmaMachine
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: enigma_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-22 00:00:00.000000000 Z
12
+ date: 2012-07-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &18339180 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,47 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *18339180
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: rake
27
- requirement: &18357640 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 0.8.2
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.2
62
+ - !ruby/object:Gem::Dependency
63
+ name: redcarpet
64
+ requirement: !ruby/object:Gem::Requirement
28
65
  none: false
29
66
  requirements:
30
67
  - - ! '>='
@@ -32,7 +69,12 @@ dependencies:
32
69
  version: '0'
33
70
  type: :development
34
71
  prerelease: false
35
- version_requirements: *18357640
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
36
78
  description: Enigma machine simulator
37
79
  email:
38
80
  - alex@tomlins.org.uk
@@ -71,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
71
113
  version: '0'
72
114
  segments:
73
115
  - 0
74
- hash: 2207890655996414444
116
+ hash: -1642108846997951420
75
117
  required_rubygems_version: !ruby/object:Gem::Requirement
76
118
  none: false
77
119
  requirements:
@@ -80,11 +122,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
122
  version: '0'
81
123
  segments:
82
124
  - 0
83
- hash: 2207890655996414444
125
+ hash: -1642108846997951420
84
126
  requirements: []
85
127
  rubyforge_project:
86
- rubygems_version: 1.8.10
128
+ rubygems_version: 1.8.24
87
129
  signing_key:
88
130
  specification_version: 3
89
131
  summary: Enigma machine simulator
90
- test_files: []
132
+ test_files:
133
+ - spec/enigma_machine_spec.rb
134
+ - spec/integration_spec.rb
135
+ - spec/plugboard_spec.rb
136
+ - spec/reflector_spec.rb
137
+ - spec/rotor_spec.rb
138
+ - spec/spec_helper.rb
139
+ has_rdoc: