substitution_solver 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,17 @@
1
1
  #!/usr/bin/env ruby
2
+ # This program basically is responsible for building a new english.dic file from
3
+ # a source ascii text file. Feeding it a novel is probably the best idea. You
4
+ # want the text to be plain english, the more of it the better.
5
+ # the format of this command is
6
+ # ruby dictionary_builder.rb <ascii_filename>
7
+
8
+ require 'optparse'
9
+ require 'rdoc/usage'
10
+
11
+ opts = OptionParser.new
12
+ opts.on("-h", "--help") {RDoc::usage}
13
+
14
+ opts.parse(ARGV)
2
15
 
3
16
  hash = Hash.new(0)
4
17
 
@@ -1,4 +1,19 @@
1
1
  #!/usr/bin/env ruby
2
+ # This program is so that you can examine the contents of a english.dic file.
3
+ # The file is a binary file that is in ruby's marshal format.
4
+ # The usage of this command is
5
+ # ruby dictionary_inspector.rb
6
+ #
7
+ # you don't need to supply a filename, it assumes that english.dic is in the
8
+ # currnet directory.
9
+
10
+ require 'optparse'
11
+ require 'rdoc/usage'
12
+
13
+ opts = OptionParser.new
14
+ opts.on("-h", "--help") {RDoc::usage}
15
+
16
+ opts.parse(ARGV)
2
17
 
3
18
  $dictionary = Hash.new(0) # The dictionary of tetragraph frequencies
4
19
 
@@ -1,24 +1,37 @@
1
1
  #!/usr/bin/env ruby
2
+ # This program is for solving simple substitution ciphers such as the
3
+ # cryptoquotes found in the newspaper. The usage of the command is as follows
4
+ # ruby substitution_solver.rb <filename>
5
+ #
6
+ # where <filename> is the name of an ascii text file that contains the
7
+ # ciphertext that you would like to retrieve the plaintext of
8
+ #
9
+ # this command requires that english.dic be in the current working directory in
10
+ # order to function properly.
11
+ #
12
+ # also be aware that this program will never return, it has no way of knowing
13
+ # when it has achieved the correct answer, so you must hit CTRL-C to exith the
14
+ # program
2
15
 
3
- $iteration = 0 # To record how many iterations the programs
4
- # had to churn through
16
+ require 'optparse'
17
+ require 'rdoc/usage'
5
18
 
6
- ciphertext = String.new
19
+ opts = OptionParser.new
20
+ opts.on("-h", "--help") {RDoc::usage}
7
21
 
8
- File::readlines(ARGV[0]).each do |line| # Grab the input from the standard input
9
- ciphertext << line
10
- end
22
+ opts.parse(ARGV)
11
23
 
12
- ciphertext.gsub!(/[^a-zA-Z]/, "").upcase! # get rid of any non-alphabetic characters
24
+ $iteration = 0 # To record how many iterations the programs
25
+ # had to churn through
13
26
 
14
- key = Hash.new # Create a hash that will represent the translation key
15
-
16
- $dictionary = Hash.new(0) # The dictionary of tetragraph frequencies
17
- File.open("english.dic") do |f| # Open the saved tetragraph information
18
- $dictionary = Marshal.load(f) # And load this information into our dictionary
19
- end
20
-
21
- def score(string) # This function will score a string against the tetragraph statistics
27
+ # this function is responsible for scoring a string against the tetragraph
28
+ # statistics
29
+ # Parameters:
30
+ # * string = the string that you want to score
31
+ # Return Value:
32
+ # * A number representing the score, the higher the score, the better (ie more
33
+ # likely to be english)
34
+ def score(string)
22
35
  $iteration += 1 # Increment the iteration count as this is probably the most fundamental loop to the program
23
36
  tally = 0 # Set a counter to 0
24
37
  0.upto(string.length-4) do |x| # Iterate through the string
@@ -27,7 +40,14 @@ def score(string) # This funct
27
40
  return tally # and return our grand total when we're finished adding it all up
28
41
  end
29
42
 
30
- def small_adj!(key) # this function makes small random adjustments to the key when we've hill climbed our way into a dead end
43
+ # this function makes small random adjustments to the key when we've hill
44
+ # climbed our way into a dead end
45
+ # Parameters:
46
+ # * key = A hash that represents the current translation mapping from ciphertext
47
+ # to plaintext
48
+ # Return Value:
49
+ # * none
50
+ def small_adj!(key)
31
51
  for i in 0...rand(5) # pick a random number of changes to make
32
52
  j = rand(26) # now pick two random letters in the alphabet to swap
33
53
  k = rand(26)
@@ -38,8 +58,16 @@ def small_adj!(key) # this funct
38
58
  end
39
59
  end
40
60
  end
41
-
42
- def plaintext(ciphertext, key) # This function will return the decoded ciphertext using a given key to do the decoding
61
+
62
+ # This function will return the decoded ciphertext using a given key to do the
63
+ # decoding
64
+ # Parameters:
65
+ # * ciphertext = A string that represents the ciphertext
66
+ # * key = A hash that represents how to translate the ciphertext into
67
+ # plaintext
68
+ # Return Value:
69
+ # * Returns the deciphered plaintext according to the key that was supplied.
70
+ def plaintext(ciphertext, key)
43
71
  return_string = String.new # create a return string
44
72
 
45
73
  for x in 0...ciphertext.length # loop through the ciphertext
@@ -47,8 +75,13 @@ def plaintext(ciphertext, key) # This funct
47
75
  end
48
76
  return return_string # return the answer
49
77
  end
50
-
51
- def randomize!(key) # completely randomize the key, ie start over from scratch
78
+
79
+ # completely randomize the key, ie start over from scratch
80
+ # Parameters:
81
+ # key = key as a Hash that needs to be randomized
82
+ # Return Value:
83
+ # none
84
+ def randomize!(key)
52
85
  array = Array.new # create an array of letters to pick from
53
86
 
54
87
  for x in 0...26
@@ -62,52 +95,80 @@ def randomize!(key) # completely
62
95
  end
63
96
  end
64
97
 
65
- print "best overall = ", score(ciphertext), " : best score = ", score(ciphertext), "\n" #print the original ciphertext
66
- puts ciphertext.gsub(/(.....)/, '\1 ')
67
-
68
- randomize!(key) # randomize the key
69
-
70
- best_score=score(ciphertext); # set the best score to the score of the ciphertext
71
- best_overall=best_score-1; # set the best overall score to the best score -1
72
- num_small_adjusts=0; # set the number of small adjustments to 0
98
+ # This function is the main entry point for this program, it is responsible for
99
+ # implementing the main algorithm that solves the simple substitution cipher.
100
+ # It will not return. The only way to quite out of this function at present is
101
+ # to hit CTRL-C.
102
+ # Parameters:
103
+ # ciphertext = the ciphertext that you are trying to decipher
104
+ # Return Value:
105
+ # none (presently the function never returns)
106
+ def substitution_solver(ciphertext)
73
107
 
74
- loop do # loop forever
75
- best_adj = best_score # set the best adjustment to the current best score
108
+ ciphertext.gsub!(/[^a-zA-Z]/, "").upcase! # get rid of any non-alphabetic characters
76
109
 
77
- for i in 0...26 # loop through all possible "trivial" letter replacements
78
- for j in i...26 # in the key looking for the best swap. This in effect is
79
- test_key = key.dup # the so called "Hill Climbing" part of our program
80
- temp = test_key[(i+65).chr]
81
- test_key[(i+65).chr] = test_key[(j+65).chr]
82
- test_key[(j+65).chr] = temp
83
- sc = score(plaintext(ciphertext, test_key)) # score the change we've made
84
- if sc > best_adj # if it's better than any so far
85
- best_adj=sc # then record the change so we can apply it later if it
86
- best_i = i # turns out to be the best one
87
- best_j = j
88
- end
89
- end
110
+ key = Hash.new # Create a hash that will represent the translation key
111
+
112
+ $dictionary = Hash.new(0) # The dictionary of tetragraph frequencies
113
+ File.open("english.dic") do |f| # Open the saved tetragraph information
114
+ $dictionary = Marshal.load(f) # And load this information into our dictionary
90
115
  end
91
116
 
92
- if best_adj > best_score # if we found an adjustment that improves the best score
93
- temp = key[(best_i+65).chr] # then apply that adjustment to the key
94
- key[(best_i+65).chr] = key[(best_j+65).chr]
95
- key[(best_j+65).chr] = temp
96
- best_score = best_adj
97
- if best_score > best_overall # if that adjustment is the best overall
98
- num_small_adjusts = 0 # then reset the number of small adjusts counter
99
- best_overall = best_score # set this new score as the best overall
100
- print "best overall = ", best_overall, " : best score = ", best_score, " : iteration = #{$iteration}\n"
101
- puts plaintext(ciphertext, key).gsub(/(.....)/, '\1 ') # and print our new found best overall value
117
+ print "best overall = ", score(ciphertext), " : best score = ", score(ciphertext), "\n" #print the original ciphertext
118
+ puts ciphertext.gsub(/(.....)/, '\1 ')
119
+
120
+ randomize!(key) # randomize the key
121
+
122
+ best_score=score(ciphertext); # set the best score to the score of the ciphertext
123
+ best_overall=best_score-1; # set the best overall score to the best score -1
124
+ num_small_adjusts=0; # set the number of small adjustments to 0
125
+
126
+ loop do # loop forever
127
+ best_adj = best_score # set the best adjustment to the current best score
128
+
129
+ for i in 0...26 # loop through all possible "trivial" letter replacements
130
+ for j in i...26 # in the key looking for the best swap. This in effect is
131
+ test_key = key.dup # the so called "Hill Climbing" part of our program
132
+ temp = test_key[(i+65).chr]
133
+ test_key[(i+65).chr] = test_key[(j+65).chr]
134
+ test_key[(j+65).chr] = temp
135
+ sc = score(plaintext(ciphertext, test_key)) # score the change we've made
136
+ if sc > best_adj # if it's better than any so far
137
+ best_adj=sc # then record the change so we can apply it later if it
138
+ best_i = i # turns out to be the best one
139
+ best_j = j
140
+ end
141
+ end
102
142
  end
103
- else # otherwise none of the adjustments raised are score
104
- if num_small_adjusts < 10 # so make a small random adjustment to the key
105
- small_adj!(key) # as long as we haven't already made to many small adjustments
106
- num_small_adjusts += 1 # increment the number of small adjustments
107
- else # otherwise we've made to many small adjustments, we're
108
- randomize!(key) # probably not getting anywhere and need to start looking
109
- num_small_adjusts = 0 # somplace else, randomize the key and start climbing the
110
- end # hill again
111
- best_score=score(plaintext(ciphertext, key)) # set the best score to either the small adjustment value or the new randomized string value depending on what we did above.
112
- end
143
+
144
+ if best_adj > best_score # if we found an adjustment that improves the best score
145
+ temp = key[(best_i+65).chr] # then apply that adjustment to the key
146
+ key[(best_i+65).chr] = key[(best_j+65).chr]
147
+ key[(best_j+65).chr] = temp
148
+ best_score = best_adj
149
+ if best_score > best_overall # if that adjustment is the best overall
150
+ num_small_adjusts = 0 # then reset the number of small adjusts counter
151
+ best_overall = best_score # set this new score as the best overall
152
+ print "best overall = ", best_overall, " : best score = ", best_score, " : iteration = #{$iteration}\n"
153
+ puts plaintext(ciphertext, key).gsub(/(.....)/, '\1 ') # and print our new found best overall value
154
+ end
155
+ else # otherwise none of the adjustments raised are score
156
+ if num_small_adjusts < 10 # so make a small random adjustment to the key
157
+ small_adj!(key) # as long as we haven't already made to many small adjustments
158
+ num_small_adjusts += 1 # increment the number of small adjustments
159
+ else # otherwise we've made to many small adjustments, we're
160
+ randomize!(key) # probably not getting anywhere and need to start looking
161
+ num_small_adjusts = 0 # somplace else, randomize the key and start climbing the
162
+ end # hill again
163
+ best_score=score(plaintext(ciphertext, key)) # set the best score to either the small adjustment value or the new randomized string value depending on what we did above.
164
+ end
165
+ end
166
+ end
167
+
168
+ ciphertext = String.new
169
+
170
+ File::readlines(ARGV[0]).each do |line| # Grab the input from the standard input
171
+ ciphertext << line
113
172
  end
173
+
174
+ substitution_solver(ciphertext) # start the program on it's main loop
metadata CHANGED
@@ -3,12 +3,12 @@ rubygems_version: 0.8.10
3
3
  specification_version: 1
4
4
  name: substitution_solver
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.0
7
- date: 2005-11-09
6
+ version: 0.5.1
7
+ date: 2005-11-17
8
8
  summary: "Program for solving mono-alphabetic simple substitution ciphers, (as in
9
9
  cryptoquotes), without word lengths."
10
10
  require_paths:
11
- - lib
11
+ - "."
12
12
  email: pfharlock@yahoo.com
13
13
  homepage:
14
14
  rubyforge_project:
@@ -16,7 +16,7 @@ description:
16
16
  autorequire:
17
17
  default_executable:
18
18
  bindir: "."
19
- has_rdoc: false
19
+ has_rdoc: true
20
20
  required_ruby_version: !ruby/object:Gem::Version::Requirement
21
21
  requirements:
22
22
  -