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!
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!