What's wrong with my Ruby Cipher? - ruby

Can someone tell me what I am doing wrong with my code. I am attempting to create a onetime pad cipher, which takes a string scrambles it and then puts it back out. But when I put a string in it doesn't work, can someone correct my work, thanks.
My Work:
h = {}
v = 0
('A'..'Z').each do |c|
v+=1
h[c] = v
end
puts "Provide Input:"
input = gets
input.downcase!
if input.include?("a")
n = h["A"] + rand(26)
puts h.index(n)
if input.include?("b")
n = h["B"] + rand(26)
puts h.index(n)
if input.include?("c")
n = h["C"] + rand(26)
puts h.index(n)
etc, etc... (All the way to the end of the alphabet)
end
end
end

I don't see any 'end' trailing each 'if' block. If that's the case, it means your ifs are nested, and you're only checking the next condition when the previous one is met (won't do anything if there's no 'a', won't do much if there's an 'a' but no 'b', etc). Also, any time you have chunks of code that are nearly identical and only vary by a small amount, it's typically a sign that there's a much easier way of doing what you want.

Related

having trouble with .next method making a casear cipher

I'm not worried about what happens if my key will go past Z right now, or capital letters. All I want is my outcome to be something like. text=abc key=2 and it print "cde". Where am I going wrong?
puts "What would you like to cipher?"
text = gets.chomp
puts " what number key would you like?"
key = gets.chomp.to_i
def casear_cipher(text,key)
ciphered_text = []
text.chars.each do |letter|
ciphered_text = letter
ciphered_text = ciphered_text.next
end
end
puts casear_cipher(text,key)
You're not using the key yet, so it will always just do abc -> bcd. If you're really not concerned about "Z" going to "AA", you can try this:
def cipher(text, key)
text.chars.map { |c| (c.ord + key).chr }.join
end
Since 'Z'.next => 'AA' and 'z'.next #=> 'aa', we can use [-1] to select the last letter.
In the code below we perform next! on each character n times using the times method. next! modifies the character whereas next does not.
def casear_cipher(text, n)
text.chars.map do |c| n.times { c.next! }
c[-1]
end.join
end
p casear_cipher('abc',2) #=> "cde"
p casear_cipher('xyz',2) #=> "zab"
p casear_cipher('ZEBRA',2) #=> "BGDTC"
More information about these methods can be found at http://www.ruby-doc.org/core-2.4.1/

How to preserve case of characters when using Caesar Cipher

I have a Caesar Cipher script in Ruby that is working but it returns the string as all upper-case letters instead of preserving the cases of the original string.
I could use capitalize to make it look good enough but I would like a more concrete way of preserving the cases.
Here is the script:
BASE_ORD = 'A'.ord
def caesar_cipher(phrase, key)
cipher = phrase.gsub(/[a-z]/i) do |c|
orig_pos = c.upcase.ord - BASE_ORD
new_pos = (orig_pos + key) % 26
(new_pos + BASE_ORD).chr
end
puts cipher
end
caesar_cipher("What a string!", 5)
Any help or insight would be appreciated.
The simplest solution, given your existing code, is to check whether the character is uppercase or lowercase and set base_ord accordingly. Since the lowercase letters come after the uppercase letters in UTF-8 (as in ASCII), we can just test letter >= 'a', e.g.:
base_ord = (letter >= 'a' ? 'a' : 'A').ord
Here's the whole method with this change (you no longer need the BASE_ORD constant):
def caesar_cipher(phrase, key)
phrase.gsub(/[a-z]/i) do |letter|
base_ord = (letter >= 'a' ? 'a' : 'A').ord
orig_pos = letter.ord - base_ord
new_pos = (orig_pos + key) % 26
(new_pos + base_ord).chr
end
end
puts caesar_cipher("What a string!", 5) # => Bmfy f xywnsl!
Edit
Amadan makes a good point about using String#tr. Here's a somewhat more concise implementation:
ALPHABET = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
# Or if you want to be fancy: ALPHABET = (?a..?z).flat_map {|c| [ c, c.upcase ] }.join
def caesar_cipher(phrase, key)
to_alphabet = ALPHABET.dup
to_alphabet << to_alphabet.slice!(0, key * 2)
phrase.tr(ALPHABET, to_alphabet)
end
puts caesar_cipher("What a string!", 5) # => Bmfy f xywnsl!
As said in comments, tr is easier to use for Caesar Cypher (once you prepare the two alphabets), and should also be much faster:
class CaesarCypher
def initialize(key, alphabet=nil)
#from_alphabet = alphabet || (?a..?z).to_a.join
#to_alphabet = #from_alphabet[key..-1] + #from_alphabet[0...key]
#from_alphabet += #from_alphabet.upcase
#to_alphabet += #to_alphabet.upcase
end
def encode(str)
str.tr(#from_alphabet, #to_alphabet)
end
def encode!(str)
str.tr!(#from_alphabet, #to_alphabet)
end
def decode(str)
str.tr(#to_alphabet, #from_alphabet)
end
def decode(str)
str.tr!(#to_alphabet, #from_alphabet)
end
end
cc = CaesarCypher.new(1)
puts cc.encode("Caesar, huh?")
puts cc.decode("Dbftbs, ivi?")

Creating A Caesar Cipher in Ruby, getting an error

I am attempting to create a Caesar Cipher in Ruby for my computer science class. My friend was able to create part of the code:
def cipher(word, n)
new_word = ""
word.each_char do |i|
n.times do
if(i == "z")
i = "a"
next
elsif(i == "Z")
i = "A"
next
end
i.next!
i == "%" ? i = " " : ""
end
new_word += i
end
puts new_word
end
cipher("phrase", 5)
Where the last line is where you would put the phrase you want to scramble, and the number is how much you want to scramble it by. One of the requirements is that we use gets.chomp to specify a phrase and amount to scramble by without editing the .rb file itself. So I came up with this:
puts "What would you like to scramble?"
word = gets.chomp
puts "How much would you like to scramble that?"
n = gets.chomp
def cipher(word, n)
new_word = ""
word.each_char do |i|
n.times do
if(i == "z")
i = "a"
next
elsif(i == "Z")
i = "A"
next
end
i.next!
i == "%" ? i = " " : ""
end
new_word += i
end
puts new_word
end
cipher(word, n)
And I get the following error outputed when run in Terminal:
some.rb:10:in `block in cipher': undefined method `times' for "5":String (NoMethodError)
from some.rb:9:in `each_char'
from some.rb:9:in `cipher'
from some.rb:26:in `<main>'
If someone could help me figure out what I'm doing wrong, that would help me out a lot.
gets.chomp returns a string
word = gets.chomp
So word is a string, as expected, but then you call gets.chomp again, this time to get number of scrabbles that should be applied to the string. So n is a string as well.
n = gets.chomp
When you call the times method on n it's not defined, because it only makes sense on integers. The solution is to convert n to an integer. This should work:
n = gets.chomp.to_i
Update
Documentation on the to_i method on String instances: http://ruby-doc.org/core-2.0.0/String.html#method-i-to_i
Call .to_i on n.
You need to convert that string you got from the user's input into a number before you can run .times on it. .to_i does this for you.
Example:
http://progzoo.net/wiki/Ruby:Convert_a_String_to_a_Number
Did this a while ago, the requirements were only lowercase ASCII alphabet letters, hope you get the general idea to do it your way:
def encrypt(msg, key)
msg.downcase.split("").each_with_index do |char, i|
next if msg[i] == " "
msg[i] = (msg[i].ord + key) > 122 ? (((msg[i].ord + key) % 123) + 97).chr : (msg[i].ord + key).chr
end
msg
end
def decrypt(msg, key)
msg.downcase.split("").each_with_index do |char, i|
next if msg[i] == " "
msg[i] = (msg[i].ord - key) < 97 ? (123 - (97 - (msg[i].ord - key))).chr : (msg[i].ord - key).chr
end
msg
end
gets.chomp return a string, you must convert it to a number in order to call .times method. Change this line n = gets.chomp by n = gets.chomp.to_i

Display subset of array

Say I want to puts the alphabet. So I can do something like:
alphabet = ('a'..'z')
alphabet.map do |a|
puts a
end
What I want to do now is exclude the vowels.
alphabet = ('a'..'z')
vowels = ['a','e','i','o','u']
alphabet.map do |a|
puts a unless a == vowels
end
I am trying to avoid this:
alphabet = ('a'..'z')
alphabet.map do |a|
puts a unless a == 'a'
puts a unless a == 'e'
puts a unless a == 'i'
puts a unless a == 'o'
puts a unless a == 'u'
end
How do I syntactically implement the second example so that it works properly?
A Range can be expanded into an Array. Then you can subtract another array.
chars = ('a'..'z').to_a - %w( a e i o u )
chars.each do |a|
puts a
end
As a side note, don't use #map unless you really need to. Use #each if you don't care about the returning value.
You don't want equality, you want inclusion:
puts a if vowels.include? a
Also, you're using map (same as collect) which will actually return the results of the puts statements. Unless you actually need that, use each. Or find the letters that match the condition and use that collection to print the results later.
Use the Array#include? method:
puts a unless vowels.include? a
Source: http://rubydoc.info/stdlib/core/1.9.2/Array#include%3F-instance_method
You can even get rid of the loop. This preserves the original alphabet.
alphabet = ('a'..'z')
puts (alphabet.to_a - %w(a e i o u)).join('\r')
Enumerable#grep would work, too:
('a'..'z').grep(/[^aeiou]/) { |a| puts a }
Or simply
puts ('a'..'z').grep(/[^aeiou]/)

Ruby: String reconstruction algo, working only partially

I'm working on a string reconstruction algorithm (a classic in dynamic programming examples, turning space less text into normal spaced text) in Ruby. The code below is pure ruby, you can copy paste and start testing immediately, it's working 80% of the time and tends to break, the larger the dictionary becomes. I've tested it with more then 80k words dictionaries and it works less good, about 70% of the time.
If there's a way to make it work 100% if the word is present in the dictionary, please show me.
Here's the code: (it's well spaced and should be very readable)
# Partially working string reconstruction algo in pure Ruby
# the dictionary
def dict(someWord)
myArray = [" ", "best", "domain", "my", "successes", "image", "resizer", "high", "tech", "crime", "unit", "name", "edge", "times", "find", "a", "bargain", "free", "spirited", "style", "i", "command", "go", "direct", "to", "harness", "the", "force"]
return !!(myArray.index(someWord))
end
# inspired by http://cseweb.ucsd.edu/classes/wi12/cse202-a/lecture6-final.pdf
## Please uncomment the one you wanna use
#
# (all the words used are present in the dictionary above)
#
# working sentences
x = ' ' + "harnesstheforce"
# x = ' ' + "hightechcrimeunit"
#
# non working sentences
# x = ' ' + "findabargain"
# x = ' ' + "icommand"
puts "Trying to reconstruct #{x}"
# useful variables we're going to use in our algo
n = x.length
k = Array.new(n)
s = Array.new(n)
breakpoints = Hash.new
validBreakpoints = Hash.new
begin
# let's fill k
for i in 0..n-1
k[i] = i
end
# the core algo starts here
s[0] = true
for k in 1..n-1
s[k] = false
for j in 1..k
if s[j-1] && dict(x[j..k])
s[k] = true
# using a hash is just a trick to not have duplicates
breakpoints.store(k, true)
end
end
end
# debug
puts "breakpoints: #{breakpoints.inspect} for #{x}"
# let's create a valid break point vector
i=1
while i <= n-1 do
# we choose the longest valid word
breakpoints.keys.sort.each do |k|
if i >= k
next
end
# debug: when the algo breaks, it does so here and goes into an infinite loop
#puts "x[#{i}..#{k}]: #{x[i..k]}"
if dict(x[i..k])
validBreakpoints[i] = k
end
end
if validBreakpoints[i]
i = validBreakpoints[i] + 1
end
end
# debug
puts "validBreakpoints: #{validBreakpoints.inspect} for #{x}"
# we insert the spaces at the places defined by the valid breakpoints
x = x.strip
i = 0
validBreakpoints.each_key do |key|
validBreakpoints[key] = validBreakpoints[key] + i
i += 1
end
validBreakpoints.each_value do |value|
x.insert(value, ' ')
end
puts "Debug: x: #{x}"
# we capture ctrl-c
rescue SignalException
abort
# end of rescue
end
Note that your algorithm fails for strings containing single-character words. This is an off-by-one error. You are ignoring the breakpoints after such words, thus you end up with a word ("abargain") not contained in your dictionary.
Change
if i >= k
next
end
to
if i > k
next
end
or more Ruby-like
next if i > k
Note also that you are running into an endless loop whenever your string contains something which is not a word:
if validBreakpoints[i] # will be false
i = validBreakpoints[i] + 1 # i not incremented, so start over at the same position
end
You better treat this as an error
return '<no parse>' unless validBreakpoints[i] # or throw if you are not in a function
i = validBreakpoints[i] + 1
The problem with "inotifier" is a deficiency of your algorithm. Always choosing the longest word is not good. In this case, the first "valid" breakpoint detected is after the "in" which leaves you the non-word "otifier".

Resources