RosettaCodeData/Task/ADFGVX-cipher/Crystal/adfgvx-cipher.cr
2026-04-30 12:34:36 -04:00

62 lines
1.6 KiB
Crystal

def make_square
# it's a square at heart...
(('A'..'Z').to_a + ('0'..'9').to_a).shuffle!
end
def make_key (size)
words = File.open("unixdict.txt") do |f|
f.each_line.select {|w| w.size == size && w.chars.to_set.size == size }.to_a
end
raise "no suitable key found" if words.empty?
words.sample
end
def adfgvx_encrypt (message, square, key)
h = "ADFGVX".chars
message.chars.flat_map {|ch|
row, col = square.index!(ch).divmod(6)
[ h[row], h[col] ]
}
.in_groups_of(key.size)
.transpose
.zip(key.chars)
.sort_by {|_, key| key }
.map {|col, _| col.join }
.join(" ")
end
def adfgvx_decrypt (message, square, key)
h = "ADFGVX".chars
sorted_key = key.chars.sort!
cols = message.split.map &.chars
max_size = cols.max_of &.size
cols.map {|col| col + [nil]*(max_size-col.size) }
.zip(sorted_key)
.sort_by {|_, ch| key.index! ch }
.map {|col, _| col }
.transpose
.flatten.compact
.in_slices_of(2)
.map {|(row, col)| square[ h.index!(row)*6 + h.index!(col) ] }
.join
end
def show (title, message, square, key)
encrypted = adfgvx_encrypt message, square, key
decrypted = adfgvx_decrypt encrypted, square, key
puts title
puts " - Alphabet: #{square.join}"
puts " - Key: #{key}"
puts " - Message: #{message}"
puts " - Encrypted: #{encrypted}"
puts " - Decrypted: #{decrypted}"
puts
end
message = "ATTACKAT1200AM"
wiki_square = "NA1C3H8TB2OME5WRPD4F6G7I9J0KLQSUVXYZ".chars
wiki_key = "PRIVACY"
show "Wikipedia example:", message, wiki_square, wiki_key
show "Random square and key:", message, make_square, make_key(9)