Creating a cipher using a hash (beginner)

I’m trying to write a program which will take a string, run a simple
Caesar cipher over it, and return the encrypted message. Here’s my
code:


puts “Please input the sentence you would like encrypted.”
message = gets.chomp.downcase

puts “Please input the key for the substitution cipher.”
cipher = gets.chomp.to_i

original_alphabet = [(‘a’…‘z’)]
alphabet = [
(‘a’…‘z’)]

moved_alphabet = cipher.times do
alphabet.rotate!
end

hash = {}
original_alphabet.each_with_index {|k,i|hash[k] = alphabet[i]}

print hash

hash.each { |k, v| message.gsub!(k, v) }

print message


When I print the hash, I see that it was populated correctly.
But when I run the #gsub on the message using that hash, it doesn’t
replace the matching keys in the string with their corresponding values.

Using “Hello” for input string.
cipher = 1 --> “aaaaa”
cipher = 2 --> “babba”
cipher = 10 -> “bdaad”
cipher = 13 -> “hellb”
cipher = 14 -> “jgnnc”
cipher = 25 -> “gdkkn”
cipher = 26 -> “hello”

What I don’t understand is why ciphers <= 13 are returning gibberish,
and why only ciphers > 13 actually do the cipher job accurately. I can
only assume I’m using the #gsub line inappropriately, but I don’t know
what the problem is.

But then again, if I run the same #gsub line with a different hash, it
works, so the problem is perhaps in how that line is dealing with the
#times loop?


message = “Hello”.downcase

hash = {}
hash[‘h’] = ‘j’
hash[‘e’] = ‘g’
hash[‘l’] = ‘n’
hash[‘o’] = ‘q’

puts hash

hash.each { |k, v| message.gsub!(k, v) }

puts message #–> ‘jgnnq’


This code returns an encrypted message following the key/value pairs set
up in the hash, while the code in my first post does not.

I have more questions than answers.

Why create moved_alphabet, and not use it?

Why hash.each which I expect would result in 26 substitutions, when you
only need one substitution based on the code/cypher?

=========================================
cipher = ARGV.shift.to_i
message= ARGV.join(’ ')

original_alphabet = (‘a’…‘z’).to_a
alphabet = (1…cipher).inject(original_alphabet) {|a,_| a.rotate}

henc=original_alphabet.zip(alphabet).each_with_object({}) {|(a,b),h|
h[a] = b}
hdec=alphabet.zip(original_alphabet).each_with_object({}) {|(a,b),h|
h[a] = b}

encoded=message.scan(/./).map {|v| henc[v]||v }.join(’’)
decoded=encoded.scan(/./).map {|v| hdec[v]||v }.join(’’)

p encoded,decoded,message

ruby essai.rb 13 abcd mnopq rstuvwxyz
“nopq zabcd efghijklm”
“abcd mnopq rstuvwxyz”
“abcd mnopq rstuvwxyz”

Regis d’Aubarede wrote in post #1149314:

This one is pretty and 3 times faster :

=========================================
cipher = ARGV.shift.to_i
message= ARGV.join(’ ')

original_alphabet = (‘a’…‘z’).to_a
cipher%=original_alphabet.size

replicates=(1…255).each_with_object({}) do |v,h|
k=v.chr
h[k]=k unless original_alphabet.index(k)
end

kv=original_alphabet.zip(original_alphabet.rotate(cipher))

henc=Hash[kv].merge(replicates)
hdec=Hash[henc.to_a.transpose.reverse.transpose]

def codec(message,hcodec)
hcodec.values_at(*message.chars).join
end

encoded=codec(message,henc)
decoded=codec(encoded,hdec)

p message,encoded,decoded

ruby essai.rb 13 “abc,mnopq : xyz”
“abc,mnopq : xyz”
“nop,zabcd : klm”
“abc,mnopq : xyz”

Roger B. Atkins wrote in post #1149311:

I have more questions than answers.

Why create moved_alphabet, and not use it?

I thought somebody would notice that, but wasn’t sure how that fit into
my question. You see, when I cut moved_alphabet out of the code, the
cipher stopped working (the encoded message wouldn’t differ from the
input). So I left it in there. Don’t ask me why, that’s all I got.

It bothers me too, but
original_alphabet.each_with_index {|k,i|hash[k] = alphabet[i]}
should be putting out a=>, b=>b, c=>c etc. since moved_alphabet is what
is doing the rotating, and both original_alphabet and alphabet are
defined the same. (And this is exactly what it does when I cut
moved_alphabet out.)

When I print the hash, I get two alphabets, one altered, as keys and
values. when I run #gsub using that hash, it only uses that hash
accurately for the second half of the alphabet.

And thanks for the alternate methods (I think I understand the first
one; the second one less so), I’m sure there are better ways to do this
but what I really wanted to know was what was wrong with the code I did
write (other than being sub-optimal).

I’m using Geany to write and execute the code, and using the latest
version of Ruby, and I just don’t understand at all why this thing is
spitting out what it does.

Kevin Ringeisen wrote in post #1149401:

Roger B. Atkins wrote in post #1149311:

when I cut moved_alphabet out of the code, the
cipher stopped working (the encoded message wouldn’t differ from the
input). So I left it in there. Don’t ask me why, that’s all I got.

your code contain:
moved_alphabet = cipher.times do
alphabet.rotate!
end

this do 2 things :

  • rotate alphabate cipher time
  • initialize moved_alphabet cipher value (!)

You can do

moved_alphabet = alphabet.clone
cipher.time { moved_alphabet.rotate! }

I run #gsub using that hash

hash.each { |k, v| message.gsub!(k, v) }
not sure your algorithm is right : if hash contain
{“z”=>“a”,“a”=>“b”,…}

‘z’ value will become ‘b’ …

so, by iterating on each char of message, you have no conflict:

message.chars.map { |c| hash[c] }

but if c is not in hash, you want conserve original value, so :

message.chars.map { |c| hash[c]||c }

and I just don’t understand at all why this thing is
spitting out what it does.

You should use irb.exe for test your code, line by line…
Use little data, by example, test with ‘a’…‘d’ in place of ‘a’…‘z’

courage :slight_smile: