0

I'm working on a Caesar's cipher problem with a left shift of 1 but having a difficulty getting started. The prompt asks me to look at the String method #ord and the Integer method #chr in the Ruby Documentation. And letter 'a' has to be shifted to 'z'...

Below is my work..

def solve_cipher(string, n)
letters = ['a'..'z']
string.map {|x| letters.include?(x.ord + n).chr : x}.join

Thanks for any advice...!

Roman Kiselenko
  • 43,210
  • 9
  • 91
  • 103
jso1226
  • 59
  • 1
  • 2
  • 8
  • Your question is too broad. Please include the results of your work and what is wrong (what do you actually need help with specifically?). For example, you could state that you send 'ABC' to your function and it outputs '!$%' which is not what you expected - but you're not sure why. – Rots Aug 02 '15 at 21:17
  • 2
    Welcome to Stack Overflow. When asking for help debugging you need to provide sample input, your expected output, and the minimal code necessary to demonstrate the problem you're having in the question itself. Your example method isn't complete, nor is there data we can use. Please help us help you and supply the data and make the method at least syntactically correct. – the Tin Man Aug 02 '15 at 21:39

3 Answers3

0

You need to split your word into the individual characters. Then you need to convert the characters to ints with ord so you can do arithmetic. The arithmetic operation is to add an offset relative to 'a', modulo 26, so that the result maps to a different offset from 'a' which will result in a new character. Change the result back to character with chr, and join the characters back together to form a string.

Here's one possible implementation which accommodates both upper and lower case letters:

def shift_char(c, base, offset)
  (((c.ord - base) + offset) % 26 + base).chr
end

def cipher(s, offset)
  s.chars.map do |c|
    case c
    when 'a'..'z'
      shift_char(c, 'a'.ord, offset)
    when 'A'..'Z'
      shift_char(c, 'A'.ord, offset)
    else
      c
    end
  end.join
end

cipher_text = cipher('Now is the time for all good men...', 13)
p cipher_text       # "Abj vf gur gvzr sbe nyy tbbq zra..."
original_text = cipher(cipher_text, 13)
p original_text     # "Now is the time for all good men..."
pjs
  • 18,696
  • 4
  • 27
  • 56
  • What does the 'c' and 'base' refer to here? – jso1226 Aug 03 '15 at 00:24
  • `c` is an individual character, `base` is the numeric starting point for the range of letters a-z or A-Z, i.e., it's `'a'.ord` in the former case and `'A'.ord` in the latter. For instance, the letter 'e' would be initially calculated as shifted 4 away from 'a'. You can make the offset any value you like, but I used the relatively common value of 13. So adding an offset of 13 to 'e' would now put it 17 away from 'a', which converts it to 'r' in the cipher. The modulo 26 wraps any values past 'z' back to the beginning of the alphabet. – pjs Aug 03 '15 at 01:28
  • Thank you for further explanation. I managed to get a short word shifts but when I put in a long sentence, my command prompt returns 'm<->'? – jso1226 Aug 04 '15 at 20:00
  • When I run your test string `"pb uhdo qdph lv grqdog gxfn"` through my code above, it produces `"my real name is donald duck"`. It's not at all clear to me what you're trying to communicate with your code-filled comment, which *doesn't* work correctly for me. – pjs Aug 04 '15 at 22:13
  • Oh I was just trying to figure out a way to insert my code in a block here. When I use that code, I did not get that... but you did...? So i was trying to figure out what i'm doing wrong... – jso1226 Aug 05 '15 at 00:31
  • Try literally copying and pasting my functions given above into `irb`. Then invoke the `cipher` method by typing `p cipher("pb uhdo qdph lv grqdog gxfn", -3)`. – pjs Aug 05 '15 at 00:50
-1

First of all you should use modulo operator to stay in letters range. Secondly you are trying to use conditional operator in a wrong way - read about ternary operator. Changing characters to numbers in letters array and moving it outside of the function would be an improvement too. Another problem is that String has no method map in Ruby. You have to use chars method, which returns an array of characters in your string.

Modified version of your solve_cipher function:

LETTERS = ('a'.ord..'z'.ord)

def solve_cipher(string, n)
  string.chars.map {|x| LETTERS.include?(x.ord)?((x.ord - LETTERS.min + n) % 26 + LETTERS.min).chr  : x}.join
end

As you can see I take the remainder after division by 26 - the length of LETTERS array - to stay in range of lowercase letters. .

Community
  • 1
  • 1
Walerian Sobczak
  • 817
  • 2
  • 10
  • 23
-1

You probably are looking for something like this:

def solve_cipher(string, n)
  string.split('').map do |x|
    new_index = x.ord + n
    while new_index > 'z'.ord
      new_index = 'a'.ord + new_index - 'z'.ord - 1
    end
    if new_index < 'a'.ord
      new_index = 'z'.ord - ('a'.ord - new_index) + 1
    end
    new_index.chr
  end.join
end
EugZol
  • 6,476
  • 22
  • 41