In cryptography, a Caesar cipher Ruby - ruby

In cryptography, there is a Caesar cipher. I am trying to build one in Ruby, but I don't know how to use capital letters in my range ('a'..'z').to_a.join. How do I use capital letters?
class Caesar
def initialize(shift)
alphabet = ('a'..'z').to_a.join
i = shift % alphabet.size
#decrypt = alphabet
#encrypt = alphabet[i..-1] + alphabet[0...i]
end
def encrypt(string)
string.tr(#decrypt, #encrypt)
end
def decrypt(string)
string.tr(#encrypt, #decrypt)
end
end
cipher_1 = Caesar.new(1)
s = 'A man, a plan, a canal: Panama!'
puts s
s_encoded = cipher_1.encrypt(s)
puts s_encoded
pudaats = cipher_1.decrypt(s_encoded)
puts pudaats
OUTput
A man, a plan, a canal: Panama!
A nbo, b qmbo, b dbobm: Pbobnb!
A man, a plan, a canal: Panama!
But i need Out Put
A man, a plan, a canal: Panama!
B nbo, b qmbo, b dbobm: Qbobnb!
A man, a plan, a canal: Panama!

The issue here lies within what you've defined as the alphabet.
a..z # ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Notice here that there's no capital letters. The only letters that are being transposed are the lowercase ones. Therefore you will need to change your range to include uppercase characters as well:
alphabet = (("A".."Z").to_a + ("a".."z").to_a).join
Then you will get the correct result.

LOWER_CASE = ('a'.ord .. 'z'.ord)
UPPER_CASE = ('A'.ord .. 'Z'.ord)
def shift(c, by)
byte = c.ord
if LOWER_CASE.include?(byte) then
((((byte - LOWER_CASE.min) + by) % 26) + LOWER_CASE.min).chr
elsif UPPER_CASE.include?(byte) then
((((byte - UPPER_CASE.min) + by) % 26) + UPPER_CASE.min).chr
else
c
end

Related

Caesar Cipher - Ruby

I have to make a function that receives a phrase and encrypts it. The cipher is to each letter in alphabet the encrypted letter is 3 letter ahead.
Example
Alphabet: A B C D E F G ... X Y Z
Ciphered: D E F G H I J ... A B C
If this is my alphabet in Ruby:
a = ['a','b','c','d','e']
I need to map it to:
a = ['c','d','e','a','b']
I've tried iterate twice the array and remove some indexes but I know I'm missing something.
UPDATE--------------------------------------------------------------------
I've managed to solve the six tests where I receive a phrase and have to encrypts as the test require.
Received phrase: prefiro perder a guerra e ganhar a paz
Phrase expected: suhilur#shughu#d#jxhuud#h#jdqkdu#d#sd}
I realize that to cypher the phrase I should change the letters positions 3 positions ahead in the ascii table.
Example: The letter 'a' should be encrypted as 'd', The letter 'z' should be encrypted as '}' and also the 'space' 3 positions ahead in the ascii table is '#'.
Here follows the code I used to solve this:
def cipher(text)
key = 3
cipher_text = text.chars.map {|x| x.ord}
.map {|x| x+key}
cipher_text.map { |x| x.chr }.join
end
def decipher(text)
key = 3
decipher_text = text.chars.map {|x| x.ord}
.map {|x| x-key}
decipher_text.map { |x| x.chr }.join
end
For encryption mentioned in the comments use String.tr method
I have to make a function that receives a phrase and encrypts it. The
cipher is to each letter in alphabet the encrypted letter is 3 letter
ahead.
phrase = "abcd st xyz"
encrypted = phrase.tr("A-Za-z ", "D-ZA-Cd-za-c#")
# => "defg#vw#abc"
Update
Please notice that the letter 'z' (at the end of the phrase) means
'}'
You can map xyz character to {|} explicitly
phrase = "prefiro perder a guerra e ganhar a paz"
encrypted = phrase.tr("A-Wa-wXYZxyz ", "D-WA-Cd-wa-c{|}{|}#")
# => "suhilur#shughu#d#jxhuud#h#jdqkdu#d#sd}"
Not sure I understand your question, but the data looks like you rotate the elements in the array. In Ruby you have a special method for that.
a = %w[a b c d] #=> ["a", "b", "c", "d"]
a.rotate #=> ["b", "c", "d", "a"]
a #=> ["a", "b", "c", "d"]
a.rotate(2) #=> ["c", "d", "a", "b"]
a.rotate(-3) #=> ["b", "c", "d", "a"]
Given an alphabet:
alphabet = ('A'..'Z').to_a
#=> ["A", "B", "C", "D", "E", ..., "V", "W", "X", "Y", "Z"]
You can create the ciphered one by calling rotate:
ciphered = alphabet.rotate(3)
#=> ["D", "E", "F", "G", "H", ..., "Y", "Z", "A", "B", "C"]
And create a mapping from one to the other:
to_cipher = alphabet.zip(ciphered).to_h
#=> {"A"=>"D", "B"=>"E", "C"=>"F", ..., "X"=>"A", "Y"=>"B", "Z"=>"C"}
Now, to encrypt a given string, we have to run each character through that hash:
'HELLO WORLD!'.each_char.map { |char| to_cipher[char] }.join
#=> "KHOORZRUOG"
Well, almost. That also removed the space and exclamation mark. We can fix this by providing a fallback for characters that don't occur in the hash:
'HELLO WORLD!'.each_char.map { |char| to_cipher.fetch(char, char) }.join
#=> "KHOOR ZRUOG!"
Or, with regular expressions using gsub:
'HELLO WORLD!'.gsub(Regexp.union(to_cipher.keys), to_cipher)
#=> "KHOOR ZRUOG!"

Algorithm for generating abstract keys in Ruby

I have an array with each letter
alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
input ="cg?"
Then i'm creating sub-keys
sub_keys = (2..letters.length).flat_map do |n|
letters.combination(n).map(&:join).map{|n| n = n.length.to_s + n.chars.sort.join }
end.uniq
Which gives me
sub_keys = ["2?c, 2?g, 2cg, 3cg?"]
Now i want to transform my sub_keys to get expected output which is
keys = [2ac, 2bc, 2cc, 2c, 2ag, 2bg, 2cg, 2dg, 2eg, 2fg, 2gg, 2g, 3acg, 3bcg, 3ccg, 3cdg, 3ceg, 3cfg, 3cgg, 3cg]
As you can see some keys like: 2c, 2g, 3cg stand out from the rest. In my code 2c acts as 2cd, 2ce, 2cf etc.. This is the output i want and my code for it is here:
keys_with_blanks = sub_keys.select{ |k| k.include? "?" }.map{ |k| k.gsub("?","") }
keys = keys_with_blanks.map do |k|
current_letters = choose_letters(all_letters, k)
k = current_letters.map{ |l| l.gsub(l,l+k) }
end.flatten.map{ |k| k.chars.sort.join }.uniq
keys += keys_with_blanks
For making less keys as the input grows, i decided to choose only the letters i need from the alphabet in each iteration instead of all of them, hence the line current_letters = choose_letters(all_letters, k)
choose_letters:
def choose_letters(letters, key)
letters = letters.select{ |l| l <= key.chars.last }
end
Let's say if i have a key key = "2c" then choose_letters will return ["a","b","c"]
This way i only make the keys i need. Up to about 13 character input its quite fast. The problem start when input is 14 or 15 characters long. Then it takes 3 or 4 seconds to make all the keys.
THIS IS THE SLOW PART
keys = keys_with_blanks.map do |k|
current_letters = choose_letters(all_letters, k)
k = current_letters.map{ |l| l.gsub(l,l+k) }
end.flatten.map{ |k| k.chars.sort.join }.uniq
It takes about 3-4 seconds for input = "abcdefghijklmn?"
QUESTION
How can the code be refactored to generate keys faster? My latest upgrade was to impletent choose_letters method, so keys like "2cd", "2ce", "2cf" and so on wouldn't generate, because they work exactly like "2c". Now it generates only the keys i need, so it's a little faster, but still.. Can it be faster?

Replacing every letter in a given string with the letter following in the alphabet [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have to replace every letter in a string with the letter following it in the alphabet (i.e. c becomes d, z becomes a), capitalize every vowel (a, e, i, o, u), and return the modified string. I'm trying to find solutions without calling any functions like sort or find.
I have this:
def LetterChanges(str)
Changed_Letter = ""
alphabet = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]
for i in 0..str.length do
if str[i] ==
str[i] = alphabet[i] + 1
return str
end
but I am lost. Any help is appreciated.
You are being asked to "map" each letter of the alphabet to another letter, so you will want to use the method Enumerable#map.
VOWELS = "aeiou"
letters = ('a'..'z').to_a
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
letters.map do |c|
<code referencing c>
end
#=> ['b', 'c', 'd', 'E', 'f',..., 'z', 'A]
Now let's fill in the code, using the methods:
String#succ, which, given a character, returns the character with the next-higher ASCII value. For example, "b".ord #=> 98, so "b".succ #=> "c", since "c".ord #=> 99. Since "z".succ #=> 'aa', we need to treat "z" as a special case. String#succ is the same as String#next.
String#include?, which, given a string, returns true or false depending on whether include?'s argument (a string) is included in the receiver. For example, "cat".include?("at") #=> true; "cat".include?("a") #=> true; "cat".include?("z") #=> false. Note that VOWELS, since it begins with a capital letter, is a constant.
String#upcase, which converts all lowercase letters in a given string to upper case (and leaves all other characters unchanged).
letters.map do |c|
if c == 'z'
'A'
else
s = c.succ
if VOWELS.include?(s)
s.upcase
else
s
end
end
end
#=> ["b", "c", "d", "E", "f", "g", "h", "I", "j", "k", "l", "m", "n",
# "O", "p", "q", "r", "s", "t", "U", "v", "w", "x", "y", "z", "A"]
You could instead write this using a case statement and Ruby's ternary operator:
letters.map do |c|
case c
when 'z'
'A'
else
s = c.succ
VOWELS.include?(s) ? s.upcase : s
end
end
or you could make use of the methods String#ord and Integer#chr:
letters.map do |c|
s = ('a'.ord + ((c.ord-'a'.ord+1) % 26)).chr
VOWELS.include?(s) ? s.upcase : s
end
end
If, for example, c = 'r'
('a'.ord + ((c.ord-'a'.ord+1) % 26).chr
#=> (97 + ((114-97+1) % 26).chr
#=> (97 + 18 % 26).chr
#=> (97 + 18).chr
#=> 115.chr
#=> 's'
If, however, c = 'z'
('a'.ord + ((c.ord-'a'.ord+1) % 26).chr
#=> (97 + ((122-97+1) % 26).chr
#=> (97 + 26 % 26).chr
#=> (97 + 0).chr
#=> 97.chr
#=> 'a'
One more way. (You can figure out why this works.)
letters.map do |c|
s = c.succ[0]
VOWELS.include?(s) ? s.upcase : s
end
You might instead wish to create a hash.
letter_mapping = {}
letters.each do |c|
s = c.succ[0]
letter_mapping[c] = VOWELS.include?(s) ? s.upcase : s
end
letter_mapping
#=> { "a"=>"b", "b"=>"c", "c"=>"d", "d"=>"E", "e"=>"f", "f"=>"g", "g"=>"h",
# "h"=>"I", "i"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"O",
# "o"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"U", "u"=>"v",
# "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"A"}
so, for example, letter_mapping['r'] #=> "s".
In time you will find that the Ruby way of writing this is:
letters.each_with_object({}) do |c, letter_mapping|
s = c.succ[0]
letter_mapping[c] = VOWELS.include?(s) ? s.upcase : s
end
#=> { "a"=>"b", ... "z"=>"A"}
One last thing. Enumerable#map is an instance method for every class that includes the Enumerable module. One such class is Array:
Array.included_modules
#=> [Enumerable, Kernel]
Array.instance_methods.include?(:map)
#=> true
Array has use of all of the module Enumerable's methods, just as though they had been defined in Array. That's why map works when the receiver is an array.
Another class that includes Enumerable is Range:
Range.included_modules
#=> [Enumerable, Kernel]
Range.instance_methods.include?(:map)
#=> true
Therefore, instead of writing:
letters = ('a'..'z').to_a
we could (should) write:
letters = ('a'..'z')
and all the above code would work just fine.
You can try this, it will replace a letter with its following letter also it will capitalize vowels.
def letter_changes(str)
alphabets = ('a'..'z').to_a
vowels = ["a","e","i","o","u"]
for i in 0..(str.length-1) do
index = (alphabets.index(str[i]) == (alphabets.size - 1) ? 0 : (alphabets.index(str[i]) + 1))
str[i] = alphabets[index]
str[i] = str[i].upcase if vowels.include?(str[i])
end
puts str
end
## call function
letter_changes("cadcarz")
## OUTPUT
dbEdbsA

How to make the array of the alphabet to rotate from "z" to "a" (ruby)

I am doing a Caesar cipher. I thought that the unless statement will work but it doesn't with or without then. Then I changed the unless with if and put ; in the place of then and it reads : undefined method `>' for nil:NilClass.
def caesar_cipher(input, key)
input.each do |x|
numbers = x.ord + key.to_i unless (numbers > 122) then numbers = x.ord + key - 26
letters = numbers.chr
print letters
end
end
puts "Write the words you want to be ciphered: "
input = gets.chomp.split(//)
puts "Write the key (1 - 26): "
key = gets.chomp
caesar_cipher(input,key)
Here are a couple of Ruby-like ways to write that:
#1
def caesar_cipher(input, key)
letters = ('a'..'z').to_a
input.each_char.map { |c| letters.include?(c) ?
letters[(letters.index(c)+key) % 26] : c }.join
end
caesar_cipher("this is your brown dog", 2)
#=> "vjku ku aqwt dtqyp fqi"
#2
def caesar_cipher(input, key)
letters = ('a'..'z').to_a
h = letters.zip(letters.rotate(key)).to_h
h.default_proc = ->(_,k) { k }
input.gsub(/./,h)
end
caesar_cipher("this is your brown dog", 2)
#=> "vjku ku aqwt dtqyp fqi"
The hash h constructed in #2 equals:
h = letters.zip(letters.rotate(key)).to_h
#=> {"a"=>"c", "b"=>"d", "c"=>"e", "d"=>"f", "e"=>"g", "f"=>"h",
# ...
# "u"=>"w", "v"=>"x", "w"=>"y", "x"=>"z", "y"=>"a", "z"=>"b"}
h.default_proc = ->(_,k) { k } causes
h[c] #=> c
if c is not a lowercase letter (e.g., a space, capital letter, number, punctuation, etc.)
If you write a branch with condition (if or unless) at the end of a line, after an initial statement, there are two things that apply and affect you:
The condition is assessed before the statement on its left. In your case that means numbers has not been assigned yet so it is nil.
The branch decision is whether or not to run the initial statement, you do not branch to the statement after the then.
You can solve this simply by converting your condition to an if and moving it to a separate line:
def caesar_cipher(input, key)
input.each do |x|
numbers = x.ord + key.to_i
if (numbers > 122)
numbers = x.ord + key - 26
end
letters = numbers.chr
print letters
end
end
There are arguably better ways of coding this cipher in Ruby, but this should solve your immediate problem.
There is a more elegant way to loop repeating sequences in ruby. Meet Enumerable#cycle.
('a'..'z').cycle.take(50)
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
# "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
# "u", "v", "w", "x", "y", "z", "a", "b", "c", "d",
# "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
# "o", "p", "q", "r", "s", "t", "u", "v", "w", "x"]
Therefore, translating a single letter given a key can be written as:
('a'..'z').cycle.take(letter.ord + key.to_i - 'a'.ord.pred).last
And the entire method can look prettier:
def caesar_cipher(phrase, key)
phrase.each_char.map do |letter|
('a'..'z').cycle.take(letter.ord + key.to_i - 'a'.ord.pred).last
end.join
end
puts caesar_cipher('abcxyz', 3) # => defabc
Note that this is slower than the alternative, but it also has the benefit that it's easier to read and the key can be any number.

Generate array of all letters and digits

Using ruby, is it possible to make an array of each letter in the alphabet and 0-9 easily?
[*('a'..'z'), *('0'..'9')] # doesn't work in Ruby 1.8
or
('a'..'z').to_a + ('0'..'9').to_a
or
(0...36).map{ |i| i.to_s 36 }
(the Integer#to_s method converts a number to a string representing it in a desired numeral system)
for letters or numbers you can form ranges and iterate over them. try this to get a general idea:
("a".."z").each { |letter| p letter }
to get an array out of it, just try the following:
("a".."z").to_a
You can also do it this way:
'a'.upto('z').to_a + 0.upto(9).to_a
Try this:
alphabet_array = [*'a'..'z', *'A'..'Z', *'0'..'9']
Or as string:
alphabet_string = alphabet_array.join
myarr = [*?a..?z] #generates an array of strings for each letter a to z
myarr = [*?a..?z] + [*?0..?9] # array of strings a-z and 0-9
letters = *('a'..'z')
=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
You can just do this:
("0".."Z").map { |i| i }

Resources