rubyzip 0.5.12 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubyzip might be problematic. Click here for more details.
- data/ChangeLog +151 -17
- data/NEWS +13 -0
- data/README +2 -0
- data/Rakefile +2 -3
- data/TODO +2 -3
- data/lib/download_quizzes.rb +119 -0
- data/lib/quiz1/t/solutions/Bill Guindon/solitaire.rb +205 -0
- data/lib/quiz1/t/solutions/Carlos/solitaire.rb +111 -0
- data/lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb +111 -0
- data/lib/quiz1/t/solutions/Florian Gross/solitaire.rb +301 -0
- data/lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb +268 -0
- data/lib/quiz1/t/solutions/James Edward Gray II/solitaire.rb +132 -0
- data/lib/quiz1/t/solutions/Jamis Buck/bin/main.rb +13 -0
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb +230 -0
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cli.rb +24 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_deck.rb +30 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_key-stream.rb +19 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_keying-algorithms.rb +31 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_solitaire-cipher.rb +66 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_unkeyed-algorithm.rb +17 -0
- data/lib/quiz1/t/solutions/Jamis Buck/test/tests.rb +2 -0
- data/lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb +204 -0
- data/lib/quiz1/t/solutions/Jim Menard/test.rb +47 -0
- data/lib/quiz1/t/solutions/Moses Hohman/cipher.rb +97 -0
- data/lib/quiz1/t/solutions/Moses Hohman/deck.rb +140 -0
- data/lib/quiz1/t/solutions/Moses Hohman/solitaire.rb +14 -0
- data/lib/quiz1/t/solutions/Moses Hohman/test_cipher.rb +68 -0
- data/lib/quiz1/t/solutions/Moses Hohman/test_deck.rb +146 -0
- data/lib/quiz1/t/solutions/Moses Hohman/test_util.rb +38 -0
- data/lib/quiz1/t/solutions/Moses Hohman/testsuite.rb +5 -0
- data/lib/quiz1/t/solutions/Moses Hohman/util.rb +27 -0
- data/lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb +151 -0
- data/lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb +198 -0
- data/lib/zip/ioextras.rb +17 -15
- data/lib/zip/zip.rb +394 -112
- data/lib/zip/zipfilesystem.rb +2 -2
- data/test/gentestfiles.rb +3 -1
- data/test/zipfilesystemtest.rb +2 -2
- data/test/ziptest.rb +34 -29
- metadata +35 -12
- data/samples/zipdialogui.rb +0 -80
- data/test/data/file2.txt.other +0 -0
- data/test/zlibtest.rb +0 -26
@@ -0,0 +1,268 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Solitaire Cipher
|
3
|
+
# Ruby-lang quiz #1
|
4
|
+
# Solution by Glenn M. Lewis - 9/27/04
|
5
|
+
|
6
|
+
$debug = nil
|
7
|
+
|
8
|
+
class Card
|
9
|
+
attr_reader :value, :face_value, :suit
|
10
|
+
|
11
|
+
def initialize(face_value, suit)
|
12
|
+
@face_value = face_value
|
13
|
+
@suit = suit
|
14
|
+
if suit then
|
15
|
+
@value = calc_value(face_value, suit)
|
16
|
+
return
|
17
|
+
end
|
18
|
+
case face_value
|
19
|
+
when "AJoker"
|
20
|
+
@value = 53
|
21
|
+
when "BJoker"
|
22
|
+
@value = 53
|
23
|
+
else
|
24
|
+
puts "ERROR: Unknown joker: #{joker}, should be 'AJoker' or 'BJoker'"
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def calc_value(face_value, suit)
|
30
|
+
val = 0
|
31
|
+
case suit
|
32
|
+
when "S" then val += 39
|
33
|
+
when "H" then val += 26
|
34
|
+
when "D" then val += 13
|
35
|
+
when "C"
|
36
|
+
else
|
37
|
+
puts "ERROR: Unknown suit: #{suit}, should be C,D,H,S"
|
38
|
+
end
|
39
|
+
case face_value
|
40
|
+
when 2..10 then val += face_value
|
41
|
+
when "A" then val += 1
|
42
|
+
when "J" then val += 11
|
43
|
+
when "Q" then val += 12
|
44
|
+
when "K" then val += 13
|
45
|
+
else
|
46
|
+
puts "ERROR: Unknown card face value: #{face_value}, should be A,2-10,J,Q,K"
|
47
|
+
end
|
48
|
+
return val
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Deck < Array
|
53
|
+
def initialize
|
54
|
+
["C", "D", "H", "S"].each do |suit|
|
55
|
+
["A", 2, 3, 4, 5, 6, 7, 8, 9, 10, "J", "Q", "K"].each do |face_value|
|
56
|
+
self.push(Card.new(face_value, suit))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
self.push(Card.new("AJoker", nil))
|
60
|
+
self.push(Card.new("BJoker", nil))
|
61
|
+
@deck_size = self.size
|
62
|
+
end
|
63
|
+
|
64
|
+
def dump
|
65
|
+
self.each do |c|
|
66
|
+
if (c.value == 53)
|
67
|
+
print c.face_value
|
68
|
+
else
|
69
|
+
print c.value
|
70
|
+
end
|
71
|
+
print " "
|
72
|
+
end
|
73
|
+
print "\n\n"
|
74
|
+
if (@deck_size != self.size) then
|
75
|
+
puts "ERROR! Deck size changed to #{self.size}"
|
76
|
+
exit
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def find_joker(j)
|
81
|
+
self.each_index do |i|
|
82
|
+
if (self[i].face_value == j)
|
83
|
+
return i
|
84
|
+
end
|
85
|
+
end
|
86
|
+
puts "ERROR: Could not find joker '#{j}' in deck."
|
87
|
+
end
|
88
|
+
|
89
|
+
def move_card_down(pos, num)
|
90
|
+
print "before move_card_down(#{pos}, #{num}): " if $debug
|
91
|
+
self.dump if $debug
|
92
|
+
dest = pos + num
|
93
|
+
dest -= (self.size-1) if (dest >= self.size)
|
94
|
+
card = self.delete_at(pos)
|
95
|
+
temp = self.dup
|
96
|
+
self.clear
|
97
|
+
temp.slice(0, dest).each {|x| self.push(x) }
|
98
|
+
self << card
|
99
|
+
temp.slice(dest..(-1)).each {|x| self.push(x) }
|
100
|
+
print "after move_card_down(#{pos}, #{num}): " if $debug
|
101
|
+
self.dump if $debug
|
102
|
+
end
|
103
|
+
|
104
|
+
def triple_cut_split(a, b)
|
105
|
+
a,b=b,a if (a > b)
|
106
|
+
print "before triple_cut_split(#{a}, #{b}): " if $debug
|
107
|
+
self.dump if $debug
|
108
|
+
temp = self.dup
|
109
|
+
self.clear
|
110
|
+
temp.slice((b+1)..-1).each {|x| self.push(x) }
|
111
|
+
temp.slice(a..b).each {|x| self.push(x) }
|
112
|
+
temp.slice(0..(a-1)).each {|x| self.push(x) }
|
113
|
+
print "after triple_cut_split(#{a}, #{b}): " if $debug
|
114
|
+
self.dump if $debug
|
115
|
+
end
|
116
|
+
|
117
|
+
def count_cut
|
118
|
+
print "before count_cut: " if $debug
|
119
|
+
self.dump if $debug
|
120
|
+
temp = self.dup
|
121
|
+
self.clear
|
122
|
+
num = temp[-1].value
|
123
|
+
temp.slice(num..-2).each {|x| self.push(x) }
|
124
|
+
temp.slice(0..(num-1)).each {|x| self.push(x) }
|
125
|
+
self.push(temp[-1])
|
126
|
+
print "after count_cut: " if $debug
|
127
|
+
self.dump if $debug
|
128
|
+
end
|
129
|
+
|
130
|
+
def output_letter
|
131
|
+
num = self[0].value
|
132
|
+
card = self[num]
|
133
|
+
return nil if (card.value == 53)
|
134
|
+
num = (card.value > 26 ? card.value-26 : card.value)
|
135
|
+
char = (num-1 + "A"[0]).chr
|
136
|
+
puts "card.value=#{card.value}, char=#{char}" if $debug
|
137
|
+
return char
|
138
|
+
end
|
139
|
+
|
140
|
+
def keystream_message(msg)
|
141
|
+
# result = "DWJXHYRFDGTMSHPUURXJ"
|
142
|
+
result = ""
|
143
|
+
while (result.length < msg.length) do
|
144
|
+
# Step 2 - Move the A Joker down one card
|
145
|
+
pos = find_joker("AJoker")
|
146
|
+
move_card_down(pos, 1)
|
147
|
+
# Step 3 - Move the B Joker down two cards
|
148
|
+
pos = find_joker("BJoker")
|
149
|
+
move_card_down(pos, 2)
|
150
|
+
# Step 4 - Triple cut split around two jokers
|
151
|
+
apos = find_joker("AJoker")
|
152
|
+
bpos = find_joker("BJoker")
|
153
|
+
triple_cut_split(apos, bpos)
|
154
|
+
# Step 5 - Count cut
|
155
|
+
count_cut
|
156
|
+
# Step 6 - Output letter - might be nil
|
157
|
+
letter = output_letter
|
158
|
+
result << letter if letter
|
159
|
+
end
|
160
|
+
return result
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
message = ARGV[0].dup
|
165
|
+
|
166
|
+
encrypted = true
|
167
|
+
encrypted = false if (message =~ /[a-z]/)
|
168
|
+
words = message.split(/\s+/)
|
169
|
+
words.each do |word|
|
170
|
+
encrypted = false if (word.length != 5)
|
171
|
+
encrypted = false if (word =~ /[^A-Z]/)
|
172
|
+
end
|
173
|
+
|
174
|
+
def message2nums(msg)
|
175
|
+
result = []
|
176
|
+
msg.each_byte do |c|
|
177
|
+
result.push(c+1-"A"[0])
|
178
|
+
end
|
179
|
+
return result
|
180
|
+
end
|
181
|
+
|
182
|
+
def nums2message(nums)
|
183
|
+
result = ""
|
184
|
+
nums.each do |val|
|
185
|
+
result << (val-1+"A"[0]).chr
|
186
|
+
end
|
187
|
+
return result
|
188
|
+
end
|
189
|
+
|
190
|
+
deck = Deck.new
|
191
|
+
|
192
|
+
if encrypted then
|
193
|
+
puts "Encrypted message: '#{message}'"
|
194
|
+
message.gsub!(/[^A-Z]/, '')
|
195
|
+
|
196
|
+
# Step 1
|
197
|
+
keystream_message = deck.keystream_message(message)
|
198
|
+
# puts "keystream_message = #{keystream_message}"
|
199
|
+
|
200
|
+
# Step 2
|
201
|
+
num_message = message2nums(message)
|
202
|
+
# puts "num_message = "
|
203
|
+
# p num_message
|
204
|
+
|
205
|
+
# Step 3
|
206
|
+
num_keystream = message2nums(keystream_message)
|
207
|
+
# puts "num_keystream = "
|
208
|
+
# p num_keystream
|
209
|
+
|
210
|
+
# Step 4
|
211
|
+
num_result = []
|
212
|
+
num_message.each_index do |index|
|
213
|
+
num_result[index] = num_message[index] - num_keystream[index]
|
214
|
+
num_result[index] += 26 if (num_result[index] < 1)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Step 6
|
218
|
+
result = nums2message(num_result)
|
219
|
+
print "Unencrypted message: "
|
220
|
+
count = 0
|
221
|
+
result.each_byte do |c|
|
222
|
+
print c.chr
|
223
|
+
count += 1
|
224
|
+
print " " if ((count % 5) == 0)
|
225
|
+
end
|
226
|
+
print "\n"
|
227
|
+
|
228
|
+
else
|
229
|
+
puts "Unencrypted message: '#{message}'"
|
230
|
+
|
231
|
+
# Step 1
|
232
|
+
message.upcase!
|
233
|
+
message.gsub!(/[^A-Z]/, '')
|
234
|
+
message << "X" * ((message.length % 5)==0 ? 0 : (5-(message.length % 5)))
|
235
|
+
# puts "message: #{message}"
|
236
|
+
|
237
|
+
# Step 2
|
238
|
+
keystream_message = deck.keystream_message(message)
|
239
|
+
# puts "keystream_message = #{keystream_message}"
|
240
|
+
|
241
|
+
# Step 3
|
242
|
+
num_message = message2nums(message)
|
243
|
+
# puts "num_message = "
|
244
|
+
# p num_message
|
245
|
+
|
246
|
+
# Step 4
|
247
|
+
num_keystream = message2nums(keystream_message)
|
248
|
+
# puts "num_keystream = "
|
249
|
+
# p num_keystream
|
250
|
+
|
251
|
+
# Step 5
|
252
|
+
num_result = []
|
253
|
+
num_message.each_index do |index|
|
254
|
+
num_result[index] = num_message[index] + num_keystream[index]
|
255
|
+
num_result[index] -= 26 if (num_result[index] > 26)
|
256
|
+
end
|
257
|
+
|
258
|
+
# Step 6
|
259
|
+
result = nums2message(num_result)
|
260
|
+
print "Encrypted message: "
|
261
|
+
count = 0
|
262
|
+
result.each_byte do |c|
|
263
|
+
print c.chr
|
264
|
+
count += 1
|
265
|
+
print " " if ((count % 5) == 0)
|
266
|
+
end
|
267
|
+
print "\n"
|
268
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
#!/usr/bin/ruby -w
|
2
|
+
|
3
|
+
$deck = (1..52).to_a + ["A", "B"] # Unkeyed deck - Keystream Step 1
|
4
|
+
|
5
|
+
def encrypt(message)
|
6
|
+
# Step 1
|
7
|
+
message = message.upcase.tr("^A-Z", "")
|
8
|
+
i = 5
|
9
|
+
while i < message.size
|
10
|
+
message[i, 0] = " "
|
11
|
+
i += 6
|
12
|
+
end
|
13
|
+
message += "X" while message.rindex(" ") != message.size - 6
|
14
|
+
|
15
|
+
# Step 2
|
16
|
+
key_stream = generate(message.count("^ "))
|
17
|
+
|
18
|
+
# Step 3
|
19
|
+
values = message.split("").map { |letter| letter[0] - ?A + 1 }
|
20
|
+
|
21
|
+
# Step 4
|
22
|
+
key_values = key_stream.split("").map { |letter| letter[0] - ?A + 1 }
|
23
|
+
|
24
|
+
# Step 5
|
25
|
+
values.each_with_index do |value, index|
|
26
|
+
next if value < 0
|
27
|
+
values[index] = value + key_values[index]
|
28
|
+
values[index] -= 26 if values[index] > 26
|
29
|
+
end
|
30
|
+
|
31
|
+
# Step 6
|
32
|
+
message = (values.map { |number| (number - 1 + ?A).chr }).join("")
|
33
|
+
|
34
|
+
return message
|
35
|
+
end
|
36
|
+
|
37
|
+
def decrypt(message)
|
38
|
+
# Step 1
|
39
|
+
key_stream = generate(message.size)
|
40
|
+
|
41
|
+
# Step 2
|
42
|
+
values = message.split("").map { |letter| letter[0] - ?A + 1 }
|
43
|
+
|
44
|
+
# Step 3
|
45
|
+
key_values = key_stream.split("").map { |letter| letter[0] - ?A + 1 }
|
46
|
+
|
47
|
+
# Step 4
|
48
|
+
values.each_with_index do |value, index|
|
49
|
+
next if value < 0
|
50
|
+
if value <= key_values[index]
|
51
|
+
values[index] = value + 26 - key_values[index]
|
52
|
+
else
|
53
|
+
values[index] = value - key_values[index]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Step 5
|
58
|
+
message = (values.map { |number| (number - 1 + ?A).chr }).join("")
|
59
|
+
|
60
|
+
return message
|
61
|
+
end
|
62
|
+
|
63
|
+
def generate(count) # Keystream Steps
|
64
|
+
key_stream = [ ]
|
65
|
+
|
66
|
+
until key_stream.size == count
|
67
|
+
# Step 2
|
68
|
+
a = $deck.index("A")
|
69
|
+
if a == 53
|
70
|
+
$deck.insert(1, $deck.pop)
|
71
|
+
else
|
72
|
+
$deck.insert(a + 1, $deck.delete_at(a))
|
73
|
+
end
|
74
|
+
|
75
|
+
# Step 3
|
76
|
+
b = $deck.index("B")
|
77
|
+
if b == 53
|
78
|
+
$deck.insert(2, $deck.pop)
|
79
|
+
elsif b == 52
|
80
|
+
$deck.insert(1, $deck.delete_at(b))
|
81
|
+
else
|
82
|
+
$deck.insert(b + 2, $deck.delete_at(b))
|
83
|
+
end
|
84
|
+
|
85
|
+
# Step 4
|
86
|
+
a = $deck.index("A")
|
87
|
+
b = $deck.index("B")
|
88
|
+
top = [a, b].min
|
89
|
+
bottom = [a, b].max
|
90
|
+
$deck = $deck.values_at((bottom + 1)..53, top..bottom, 0...top)
|
91
|
+
|
92
|
+
# Step 5
|
93
|
+
if $deck[53].kind_of? Integer
|
94
|
+
$deck = $deck.values_at($deck[53]..52, 0...$deck[53], 53)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Step 5
|
98
|
+
if $deck[0].kind_of? Integer
|
99
|
+
if $deck[$deck[0]].kind_of? Integer
|
100
|
+
key_stream.push($deck[$deck[0]])
|
101
|
+
end
|
102
|
+
else
|
103
|
+
if $deck[53].kind_of? Integer
|
104
|
+
key_stream.push($deck[53])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end # Step 7
|
108
|
+
|
109
|
+
key_stream.map! do |number|
|
110
|
+
if number > 26
|
111
|
+
(number - 26 - 1 + ?A).chr
|
112
|
+
else
|
113
|
+
(number - 1 + ?A).chr
|
114
|
+
end
|
115
|
+
end
|
116
|
+
key_stream = key_stream.join("")
|
117
|
+
i = 5
|
118
|
+
while i < key_stream.size
|
119
|
+
key_stream[i, 0] = " "
|
120
|
+
i += 6
|
121
|
+
end
|
122
|
+
return key_stream
|
123
|
+
end
|
124
|
+
|
125
|
+
# Mind reading interface
|
126
|
+
if ARGV.size == 1 and ARGV[0] =~ /^(?:[A-Z]{5} )*[A-Z]{5}$/
|
127
|
+
puts decrypt(ARGV[0])
|
128
|
+
elsif ARGV.size == 1
|
129
|
+
puts encrypt(ARGV[0])
|
130
|
+
else
|
131
|
+
puts "Usage: solitaire.rb MESSAGE"
|
132
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'copland'
|
4
|
+
|
5
|
+
libdir = File.join( File.dirname( __FILE__ ), "..", "lib" )
|
6
|
+
$: << libdir
|
7
|
+
|
8
|
+
registry = Copland::Registry.build libdir, :log_device => STDOUT
|
9
|
+
|
10
|
+
cli = registry.service( "solitaire.cipher.CLI" )
|
11
|
+
cli.run
|
12
|
+
|
13
|
+
registry.shutdown
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class Deck
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@deck = (1..52).to_a + [ "A", "B" ]
|
7
|
+
@length = @deck.length
|
8
|
+
end
|
9
|
+
|
10
|
+
def cipher_shuffle!
|
11
|
+
# move joker A down one card, circularly
|
12
|
+
reposition_card( "A", 1 )
|
13
|
+
|
14
|
+
# move joker B down two cards, circularly
|
15
|
+
reposition_card( "B", 2 )
|
16
|
+
|
17
|
+
joker_A = @deck.index( "A" )
|
18
|
+
joker_B = @deck.index( "B" )
|
19
|
+
|
20
|
+
# move all cards above the top-most joker, below the bottom-most joker, and
|
21
|
+
# all cards below the bottom-most joker, above the top-most joker.
|
22
|
+
top = ( joker_A < joker_B ? joker_A : joker_B )
|
23
|
+
bottom = ( joker_A > joker_B ? joker_A : joker_B )
|
24
|
+
@deck = @deck[bottom+1..-1] + @deck[top..bottom] + @deck[0,top]
|
25
|
+
|
26
|
+
# take value of the bottom-most card, and cut that many cards off the
|
27
|
+
# top, inserting them just before the bottom-most card.
|
28
|
+
cut = @deck.last
|
29
|
+
@deck = @deck[cut..-2] + @deck[0,cut] + [ @deck.last ]
|
30
|
+
end
|
31
|
+
|
32
|
+
def cipher_letter
|
33
|
+
count = @deck.first
|
34
|
+
count = 53 if count.is_a?( String )
|
35
|
+
result = @deck[ count ]
|
36
|
+
return nil unless result.is_a? Fixnum
|
37
|
+
result -= 26 while result > 26
|
38
|
+
return (result+64).chr
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_a
|
42
|
+
@deck.dup
|
43
|
+
end
|
44
|
+
|
45
|
+
def cards=( deck )
|
46
|
+
@deck = deck
|
47
|
+
@length = @deck.length
|
48
|
+
raise "the deck must contain an 'A' joker" unless @deck.include?("A")
|
49
|
+
raise "the deck must contain a 'B' joker" unless @deck.include?("B")
|
50
|
+
end
|
51
|
+
|
52
|
+
def reposition_card( card, delta )
|
53
|
+
pos = @deck.index card
|
54
|
+
@deck.delete_at pos
|
55
|
+
new_pos = pos + delta
|
56
|
+
new_pos = 1 + new_pos % @length if new_pos >= @length
|
57
|
+
@deck.insert new_pos, card
|
58
|
+
new_pos
|
59
|
+
end
|
60
|
+
private :reposition_card
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
class KeyingAlgorithms
|
65
|
+
|
66
|
+
attr_writer :algorithms
|
67
|
+
attr_writer :registry
|
68
|
+
|
69
|
+
def get( name )
|
70
|
+
svc_name = @algorithms[ name ]
|
71
|
+
raise "No such algorithm #{name.inspect}" if svc_name.nil?
|
72
|
+
|
73
|
+
return @registry.service( svc_name )
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
class UnkeyedAlgorithm
|
79
|
+
|
80
|
+
def new_deck
|
81
|
+
Deck.new
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class KeyStream
|
87
|
+
|
88
|
+
attr_writer :deck
|
89
|
+
|
90
|
+
def next
|
91
|
+
loop do
|
92
|
+
@deck.cipher_shuffle!
|
93
|
+
letter = @deck.cipher_letter
|
94
|
+
return letter if letter
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class SolitaireCipher
|
101
|
+
|
102
|
+
attr_writer :algorithms
|
103
|
+
attr_writer :stream
|
104
|
+
|
105
|
+
def initialize( default_algorithm )
|
106
|
+
@algorithm = default_algorithm
|
107
|
+
end
|
108
|
+
|
109
|
+
def use_algorithm( keying_algorithm )
|
110
|
+
@algorithm = @algorithms.get( keying_algorithm )
|
111
|
+
end
|
112
|
+
|
113
|
+
def encrypt( message )
|
114
|
+
reset
|
115
|
+
|
116
|
+
chars = message.split(//).map { |c| c.upcase }. reject { |c| c !~ /[A-Z]/ }
|
117
|
+
chars.concat ["X"] * ( 5 - chars.length % 5 ) if chars.length % 5 > 0
|
118
|
+
chars.map! { |c| c[0] - 64 }
|
119
|
+
key = generate_key( chars.length )
|
120
|
+
code = chars.zip( key ).map { |c,k| ( c + k > 26 ? c + k - 26 : c + k ) }.map { |c| (c+64).chr }
|
121
|
+
|
122
|
+
msg = ""
|
123
|
+
(code.length/5).times do
|
124
|
+
msg << " " if msg.length > 0
|
125
|
+
5.times { msg << code.shift }
|
126
|
+
end
|
127
|
+
|
128
|
+
return msg
|
129
|
+
end
|
130
|
+
|
131
|
+
def decrypt( message )
|
132
|
+
raise "bad decrypt message: #{message.inspect}" if message =~ /[^A-Z ]/
|
133
|
+
|
134
|
+
reset
|
135
|
+
chars = message.split(//).reject { |c| c == " " }.map { |c| c[0] - 64 }
|
136
|
+
key = generate_key( chars.length )
|
137
|
+
chars.zip( key ).map { |c,k| ( k >= c ? c + 26 - k : c - k ) }.map { |c| (c+64).chr }.join
|
138
|
+
end
|
139
|
+
|
140
|
+
def generate_key( length )
|
141
|
+
key = []
|
142
|
+
length.times { key << @stream.next }
|
143
|
+
key.map { |c| c[0] - 64 }
|
144
|
+
end
|
145
|
+
private :generate_key
|
146
|
+
|
147
|
+
def reset
|
148
|
+
@stream.deck = @algorithm.new_deck
|
149
|
+
end
|
150
|
+
private :reset
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
class Options
|
155
|
+
|
156
|
+
attr_reader :strings
|
157
|
+
attr_reader :keying_algorithm
|
158
|
+
|
159
|
+
def initialize( argv = ARGV )
|
160
|
+
@named_options = Hash.new
|
161
|
+
@run_app = true
|
162
|
+
@keying_algorithm = "unkeyed"
|
163
|
+
|
164
|
+
OptionParser.new do |opts|
|
165
|
+
opts.banner = "Usage: #{$0} [options] [strings]"
|
166
|
+
opts.separator ""
|
167
|
+
|
168
|
+
opts.on( "-o", "--option NAME=VALUE",
|
169
|
+
"Specify a named value, for use by a component of the cipher."
|
170
|
+
) do |pair|
|
171
|
+
name, value = pair.split( / *= */, 2 )
|
172
|
+
@named_options[ name ] = value
|
173
|
+
end
|
174
|
+
|
175
|
+
opts.on( "-k", "--key NAME", "Specify the keying algorithm to use" ) do |value|
|
176
|
+
@keying_algorithm = value
|
177
|
+
end
|
178
|
+
|
179
|
+
opts.separator ""
|
180
|
+
|
181
|
+
opts.on_tail( "-h", "--help", "This help text" ) do
|
182
|
+
puts opts
|
183
|
+
@run_app = false
|
184
|
+
end
|
185
|
+
|
186
|
+
opts.parse!( argv )
|
187
|
+
end
|
188
|
+
|
189
|
+
@strings = argv
|
190
|
+
end
|
191
|
+
|
192
|
+
def []( value )
|
193
|
+
@named_options[ value ]
|
194
|
+
end
|
195
|
+
|
196
|
+
def run_app?
|
197
|
+
@run_app
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
|
203
|
+
class BackwardsAlgorithm
|
204
|
+
|
205
|
+
def new_deck
|
206
|
+
deck = Deck.new
|
207
|
+
deck.cards = deck.to_a.reverse
|
208
|
+
deck
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
class ShuffleAlgorithm
|
214
|
+
|
215
|
+
attr_writer :options
|
216
|
+
|
217
|
+
def new_deck
|
218
|
+
deck = Deck.new
|
219
|
+
cards = deck.to_a
|
220
|
+
|
221
|
+
seed = ( @options[ "seed" ] || 0 ).to_i
|
222
|
+
srand seed
|
223
|
+
|
224
|
+
7.times { cards.sort! { rand(3)-1 } }
|
225
|
+
deck.cards = cards
|
226
|
+
|
227
|
+
return deck
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|