enigma_machine 0.0.1 → 0.0.2

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.
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: