Reading word into an array [ruby] - ruby

Trying to create a ceaser cipher in Ruby.
The problem I'm facing is that when the program reaches the while loop, it only performs the desired operation on the very last letter of the inputted word. Before I delve further into what I've tried, please find the code:
#!/usr/bin/ruby
#print 65.chr ASCII code for A
#print 97.chr ASCII code for a
a = 0
b = 97
d = []
e = 0
# Just to print the alphabet alongside the ASCII value
# (For sanity checking)
while a <= 25
print b.chr + " "
print b.to_s + "\n"
a = a + 1
b = b + 1
end
puts "\n Please enter a word to translate"
word = gets.strip
# The desired effect is to move the letter along by key value
puts "Please enter a key"
k = gets.chomp.to_i
# In its current state, what happens is only the last char
# is moved along by the key value.
while e <= word.length
word.each_byte do |c|
d[e] = c + k
end
e = e + 1
end
puts d
I'm thinking that the problem lies with the logic for the while loop. The way I am going to attack this is by reading the pre-converted word into an array, as opposed to using the .each_byte object.
I don't know how to do that and the guides/questions I've found don't quite answer the question. If anyone knows how to do this, or knows a better way of solving this- I'd be much appreciative.

you don't need the last while loop
word.each_byte do |c|
d[e] = c + k
e = e + 1
end

Something a bit more verbose:
alphabet = ('a'..'z').to_a
new_word = ''
puts "\n Please enter a word to translate"
word = gets.strip
puts "Please enter a key"
k = gets.chomp.to_i
word.split('').each_with_index do |letter, index|
alphabet_index = alphabet.index(letter)
new_index = alphabet_index + key
new_word[index] = alphabet[new_index]
end
puts "Your translated word is #{new_word}"

Caesar cipher is a simple shift cipher
word.each_byte do |c|
p c + k
end

Managed to get it working, thanks for all the help... Code for anyone interested:
#!/usr/bin/ruby
#print 65.chr A
#print 97.chr a
a = 0
b = 65
y = 97
d = []
e = 0
while a <= 25
print y.chr + " = " + y.to_s + " "
print b.chr + " = " + b.to_s + " " + "\n"
a = a + 1
b = b + 1
y = y + 1
end
puts "\n Please enter a word to translate"
word = gets.strip
puts "Please enter a key"
k = gets.chomp.to_i
word.each_byte do |c|
d[e] = c + k
e = e + 1
end
print "\n"
a = 0
arlen = d.count
while a != arlen
print d[a].chr
a = a + 1
end
print k

Related

customised random string generator in ruby

Below is my code to generate a random string by taking the number of length from the user . I want to enhance this by asking the user to enter number of numbers and special characters to be included and generate a string to match those requirements.
For example:
length: 7
number of numbers: 2
number of special chars: 1
Then my output should be something like:
ab2hg3!
Below is my code:
puts "enter the minimum length of the password"
lengths = gets.to_i
puts "enter the number of numbers to be included"
num = gets.to_i
def gen_pass(lengths)
chars = ('a'..'z').to_a + ('A'..'Z').to_a + (0..10).to_a
Array.new(lengths) {chars.sample}.join
end
puts gen_pass(lengths)
puts gen_pass(lengths)
Try this:
def gen_pass(lengths, number_of_numbers, number_of_special_chars)
chars = ('a'..'z').to_a + ('A'..'Z').to_a + (0..10).to_a
digits = (0..9).to_a
special_chars = "?<>',?[]}{=-)(*&^%$#`~{}".split('')
remaining_letters_count = lengths - number_of_numbers - number_of_special_chars
n = digits.sample(number_of_numbers)
s = special_chars.sample(number_of_special_chars)
c = chars.sample(remaining_letters_count)
str = n << s << c
str.flatten.shuffle.join
end
puts gen_pass(lengths, number_of_numbers, number_of_special_chars)
If this is an exercise you should do it yourself. If it's not, you are certainly doing it wrong.
Anyway, here is my try :
#!/usr/bin/env ruby
def secret(l,n,s)
random_string =
[*('A'..'Z'),*('a'..'z')].sample(l-n-s) +
[*('0'..'9')].sample(n) +
[*('!'..'/')].sample(s)
random_string.shuffle.join
end
p secret(5,2,1)
=> ".4F0r"

How to I make floyd triangle shape using for loop?

I need output of Floyd triangle like:
1
0 1
1 0 1
0 1 0 1
I tried. I didn't get it exactly. Can anyone explain the logic?
This is the code I tried:
k = 0
for i in 1..5
for j in 1..5
if (i%2)==0;
k = (j%2==0) ? 1:0;
else;
k = (j%2==0) ? 0:1;
puts k,'';
end
end
puts
end
The main issue here is that in order to get the "triangle" shape of your output, you need your inner loop to increment from 1 to i instead of 1 to 5.
k = 0
for i in 1..5
for j in 1..i
if (i%2)==0
k = j + 1
else
k = j
end
print "#{k%2} "
end
puts
end
Here's a one line approach:
5.times {|line| puts (line + 1).times.with_object(""){|num, str| (num + line).even? ? (str << " 1 ") : (str << " 0 ") } }
to make it more clear:
lines = 5
lines.times do |line|
str = ""
line = line + 1 # 5.times runs from 0 to 4 and we need 1 to 5
line.times do |num|
# the condition is a bit different because I changes the code a bit
if (line + num).even?
str << " 0 "
else
str << " 1 "
end
end
puts str
end
Alright the following should work, but i hope it's readable. If you need more explanation or have specific questions let me know
i = 1
while i <= 4 do
if i%2 > 0
output = 1
else
output = 0
end
j = 1
while j <= i do
print( "#{output} " )
if output == 1
output = 0
else
output = 1
end
j+=1
end
print( "\n" )
i+=1
end
You can try following code for output you are expecting:
k = 0
for i in 1..4
for j in 1..i // inner loop code runs i times for each outer loop iteration
if (i%2)==0;
k = (j%2==0) ? 1:0;
else;
k = (j%2==0) ? 0:1;
end
print k,' ';
end
puts
end
Click Here to see output.
You can also get idea about for loops through this link.
The prefered ruby way:
layers = 4 # Change to as many layers as you want
layers.times do |i| # i starts from 0
(i + 1).times do |j| # j also starts from 0
print (i + j + 1) & 1, ' '
end
puts
end
The for way:
layers = 4
for i in 0...layers
for j in 0...(i + 1)
print (i + j + 1) & 1, ' '
end
puts
end

How to bypass scope convention in ruby if else statements

This shows an error because ruby scope rules prevent me from accessing outer variables inside an if else block.
puts "Enter Line 1 m and c:"
m1 = gets.to_f
c1 = gets.to_f
puts "Enter Line 2 m and c:"
m2 = gets.to_f
c2 = gets.to_f
if ((m1==m2) and (c1==c2))
puts "infinite solutions"
elsif ((m1==m2) and (c1!=c2))
puts "no solution"
else
x = (c1 - c2)/(m2 - m1)
y = m1*x + c1
puts "(x,y) = (" + x + "," + y+")"
end
Can you please tell me a way to get around this error ?
Update:
actually the error i get is:
undefined local variable or method 'c1'
for main:Object from :7
from C;/Ruby200-x64/bin/irb:12;in ''
Use interpolation to get rid of this.
puts "(x,y) = (#{x}, #{y})"
You were trying to concatenate String object with Float object. That's not possible, so you have to convert those Float to String objects before concatenation.
modified code:
puts "Enter Line 1 m and c:"
m1 = gets.to_f
c1 = gets.to_f
puts "Enter Line 2 m and c:"
m2 = gets.to_f
c2 = gets.to_f
if m1 == m2 and c1 == c2
puts "infinite solutions"
elsif m1 == m2 and c1 != c2
puts "no solution"
else
x = (c1 - c2)/(m2 - m1)
y = m1*x + c1
puts "(x,y) = (#{x}, #{y})"
end
output
[arup#Ruby]$ ruby a.rb
Enter Line 1 m and c:
14
21
Enter Line 2 m and c:
12
44
(x,y) = (11.5, 182.0)
[arup#Ruby]$
It doesn't prevent you from accessing outer variables, the error you see is:
`+': no implicit conversion of Float into String (TypeError)
which is completely different and has nothing to do with variables visibility scopes. What error says is that you can't sum up String and Float(try 'a' + 1.0 in console).
To fix it you should convert variables to strings by yourself with:
puts "(x,y) = (" + x.to_s + "," + y.to_s + ")"
or by using interpolation(which is preferable):
puts "(x,y) = (#{x}, #{y})"

How do I count the frequencies of the letters in user input?

How do I count the frequency of letters that appear in the word "supercaliforniamightly" when the user enters a word like that in Ruby, and print out stars or asterisks to count the number of letters that appear?
Here's my code:
puts "Enter string: "
text= gets.chomp
text.downcase!
words = text.split(//)
frequencies = Hash.new(0)
words.each{|item| frequencies[item] +=1}
frequencies = frequencies.sort_by{ |item, amount| amount}
frequencies.reverse!
frequencies.each do |item, amount|
puts item + " " + amount.to_s
end
The output I want is something like:
Enter a string:
uuuuiiii
u , 4 ****
i , 4 ****
I changed the output a little bit (removed the space before the comma) so that I don't look like uneducated.
puts "Enter string: "
gets.chomp.downcase
.each_char.with_object(Hash.new(0)){|c, h| h[c] += 1}
.sort_by{|_, v| v}
.reverse
.each{|k, v| puts k + ", " + v.to_s + " " + "*" * v}
Output:
Enter string:
uuuuiiii
i, 4 ****
u, 4 ****

Public key encryption demo in Ruby

I wrote up the ruby script below to help my students understand public key encryption. I followed the "pencil and paper" method shown here: http://sergematovic.tripod.com/rsa1.html
This works fine as long as 29 isn't chosen as either p or q. If 29 is picked, it hangs on calculating the secret key. Can anyone tell me why that is?
#!/usr/bin/env ruby -wKU
#initialize
primes, p, q, n, z, k, j, m,e,d = nil
def prime
primes = [2,3,5,7,11,13,17,19,23,29,31]
primes.sample
end
#pick p
p= prime
puts "p: " + p.to_s
#pick q
q=p
while p==q
q = prime
end
puts "q: " + q.to_s
#find n
n=p*q
puts "n: " + n.to_s
#find z
z=(p-1)*(q-1)
puts "z: " + z.to_s
#pick a relative prime of the totient
k=7
puts "k: " + k.to_s
#calculate secret key
j=0
while j*k % z != 1
j+=1
end
puts "j: " + j.to_s
#message
m=16
puts "Message: " + m.to_s
#encrypt
e = m**k % n
puts "Encrypted: " + e.to_s
#decrypt
d = e**j % n
puts "Decrypted: " + d.to_s
When 29 is chosen as p or q, z has 28 as a factor, and thus k = 7 is not a relative prime of the totient as your comment claims!
(This means j*k % z is always a multiple of 7, so your loop never terminates.)

Resources