Algorithm Sundays: Decrypting a ROT-13 Caesar Cipher

Is there anything better than starting your Sunday with a steaming hot mug of coffee and an algorithm challenge to get your brain juices flowing? I don't think so. Writing algorithms is a perfect way for you to stay sharp and be prepared for all kinds of challenges life (or your boss) is throwing at you. Please keep in mind that for most problems there are many ways to solve them and it's difficult to decide for a best solution. The one that runs quickest? The one that takes the least lines of code? The one that can be read easiest by your colleagues? I highly encourage you to take some time and think about the presented problem first. Try to come up with your own solution, before following the steps I took to solve this challenge.

The Caesar Cipher

Remember Julius Caesar? This guy was pretty serious about privacy. So serious, that he invented his own method of text encryption, referred to as Caesar Cipher. His need for secrecy was quite obvious, considering that an intercepted message with war strategies might have caused him a lot of trouble. The method of encrypting a message is easy: Every letter of the message is replaced by another letter, which is shifted forward or backwards by a certain amount of character. Both sender and receiver would know how to shift the letters, making it easy to quickly exchange encrypted messages. As the cipher is replacing letters with other letters, it is counted to the family of Substitution Ciphers. Today, more than 2000 years later, the Caesar Cipher is considered to offer almost no security anymore, as breaking it doesn't require much effort. In the context of the Caesar Cipher, ROT-13 is one of the most commonly used encryption methods. It simply means rotate 13 forward.

The Challenge

Let's get started. Our objective is to decrypt the following line of code, which was encrypted using the ROT-13 method.

jurervfzlpbssrr  

Let's start by building a simple function and passing a string to it.

function rot13(str){

}
rot13("jurervfzlpbssrr");  

We know it has been rotated forward by 13 places. So, let's take a look at the string method string.charCodeAt(). This methods returns a number for the character at a specific position. For letters and numbers we will usually be within 0-127, which represents the ASCII character set. Also, it's important to know that lower and uppercase letters have different numerical values within the ASCII table. So make sure you either stick with lower or uppercase letters. Remember, for this you can use string.toUpperCase() or string.toLowerCase(). If we decide to use all lowercase letters and take a look at the ASCII table, we see that our letters will range from 97 (a) to 122 (z). As they are all numbered consecutively, b will be 98, c 99, d 100 and so on. So let's give this a spin.

function rot13(str){  
  for (var i = 0; i<str.length; i++) {
    console.log(str.charCodeAt(i));
  }
}
rot13("jurervfzlpbssrr");  

Now we're looping over all the characters in the string and receive the charcodes for each of them. Now, if we only added +13 to each of them and converted the numbers back to characters, that should solve our challenge, right? Well, not so fast. Let's look at our second character u. Its number is 117. If we now incremented this number by 13, we'd end up with 130 - a number that is outside of our lowercase alphabet. In the Caesar Cipher, we start back at the beginning of the alphabet after reaching the last letter. In case of u, that would mean we still have 8 steps to go from the beginning of the alphabet, after reaching z (117 - 122 + 13). Let's add some of those elements to our code.

function rot13(str){  
  var res = []; // an array to capture our results
  for (var i = 0; i<str.length; i++) {
    if (str.charCodeAt(i)+13 > 122){
      // check if we have to start over at the beginning
    }
    else {

    }
  }
  return res.join('');
}
rot13("jurervfzlpbssrr");  

Now we can make use of String.fromCharCode(), a static method of the String object. We cannot call this on an individual string, so you always have to literally use String and not a variable.

function rot13(str){  
  var res = [];
  var currentPosition;
  var shiftedPosition;
  for (var i = 0; i<str.length; i++) {
    currentPosition = str.charCodeAt(i);
    shiftedPosition = str.charCodeAt(i) + 13;
    if (shiftedPosition > 122){
      res.push(String.fromCharCode(96+currentPosition-122+13));
      //moving the remaining characters forward from "a"   
    }
    else {
      res.push(String.fromCharCode(shiftedPosition));
      //moving 13 characters forward from the current character
    }
  }
  return res.join('');
}
rot13("jurervfzlpbssrr");  

Since there is a lot happening here at the same time, I've added two helping variables currentPosition and shiftedPosition. We actually don't need these variables and can write our solution a bit shorter

function rot13(str){  
  var res = [];
  for (var i = 0; i<str.length; i++) {
    if (str.charCodeAt(i)+13 > 122){
      res.push(String.fromCharCode(96+str.charCodeAt(i)-122+13));    
    }
    else {
      res.push(String.fromCharCode(str.charCodeAt(i)+13));
    }
  }
  return res.join('');
}
rot13("jurervfzlpbssrr");  

Finally, we can shorten our if statement (it's a matter of taste really)

function rot13(str){  
  var res = [];
  for (var i = 0; i<str.length; i++) {
    if (str.charCodeAt(i)+13 > 122) res.push(String.fromCharCode(96+str.charCodeAt(i)-122+13));    
    else res.push(String.fromCharCode(str.charCodeAt(i)+13));
  }
  return res.join('');
}
rot13("jurervfzlpbssrr");  

There we go. How does your algorithm look like?

A few more strings for testing: "nabguregrfgzrffntr", "guvfvfgbbrnfl", "rawbllbhefhaqnl"

Further reading about the methods used:
string.charCodeAt() on w3schools and MDN.
String.fromCharCode() on w3schools and MDN.

Further down the rabbit hole