Bonfire: Caesar’s Cipher Solution

Caesar's Cipher

I had to take a little break and do a pen review in between some projects, but I’m back and ready to polish off these beginner’s algorithms. Today, we’re tackling the Caesar’s Cipher Bonfire and the last of the beginner challenges that Free Code Camp has for us. Let’s take a look at what they give us right out of the gate:

function rot13(str) { // LBH QVQ VG!
 
  return str;
}

// Change the inputs below to test
rot13("SERR PBQR PNZC");

I don’t even know what I’m looking at here. So before we even try to look into solving this, let’s go over what exactly a Caesar’s Cipher is.

Ciphers, It’s More Than a Cool Word

Way back in time when Julius Caesar was still around, he’d use a cipher to send coded messages to his generals. They have apparently named this cipher after him. Caesar’s Cipher is also know as a Shift Cipher, where the letters are shifted based upon a number. So if you had a coded message like “fvmrk tmdde”, where the key is 4. Then to decode the message, you would have to move the letters back by four to decipher it. In this case, “fvmrk tmdde” deciphers into “bring pizza”, where the letter F, walked back four times, in the English alphabet, is the letter B. And so on for the rest of the characters until we get our secret message. By the way, bringing pizza is a very considerate thing to do when you’re going to a party.

Now, a Caesar’s Cipher might be pretty cool if we were still in grade school and secretly passing notes to each other that we don’t want the teacher to understand, but this type of rudimentary cipher is no longer very useful on a security standpoint. Still, it makes for a challenging and, probably, engaging Bonfire.

Free Code Camp’s Bonfire wants us to decode the above messages, and it gives us a big hint by mentioning the ROT13 Cipher, which shifts letters by 13 places. I’m going to assume that ROT stands for Rotate or some similar word. So we already know our key in this instance, we need to shift these letters around by 13 places each in order to decode the secret message and get our club rings. Let’s get to it then!

Approaching the Problem

  1. The Bonfire has already relieved us of having to do anything roundabout by telling us all the characters will be uppercase, no numbers or special characters we have to deal with. So at least we don’t have to fuss with regex.
  2. The Bonfire has also given us a big hint and told us basically that the key is 13. This eliminates the need for us to search for the key, and it ultimately saves us a ton of time and coding too.
  3. I need to determine if we’re walking forward or backward by 13 places. I am assuming that it is backward. What I did was write out the code, then test it going forward to see if I would get gibberish or not, if I did, I’d walk it back 13 instead. Not exactly the best way to figure out which way a key is going but, it works for what we’re doing here.
  4. Given that we need an alphabet or a coding system to work on, the most straightforward solution would be to create an array containing all the characters in the alphabet, but it’s hardly the most efficient. We’re going to do this using the ASCII table instead to avoid having to fall back on unicode or manually typing up an alphabet.
  5. We know that each character needs to be walked back by 13 characters to decipher things so we’ll have to create a loop that does this for each of the characters in the cipher.

This Caesar’s Cipher Bonfire is actually more complicated than it looks in my opinion and I had to really heavily reference an external source: Self-Taught JS, and it’s a pretty good way to end this Beginner’s Bonfire series before throwing us screaming into our next challenge. I will say it was definitely a mental exercise for a purely Front-End Dev. So I guess I’ll start at the top and go easy.

The For Loop Framework

I already know that I’m going to be working with an ASCII table, but there’s a couple of things I want to do first before I dive into that mess. I know I want to fill an array with the deciphered message. So the first thing I’m going to do is declare an empty array called deciphered to fill up later on. Next, I create a simple for loop that will step through each character that’s fed into the rot13() function. Here’s what I ended up:

function rot13(str) { // LBH QVQ VG!

    var deciphered = [];
    for (var i = 0; i < str.length; i++) {

    }

    return str;

}

// Change the inputs below to test
rot13("SERR PBQR PNZC");

Really easy and straight forward so far. Nothing should be scary to us at the moment, so let’s go on to the next step. I’m going to be looking into three things we haven’t seen yet when dealing with these Bonfires, they are charCodeAt(), fromCharCode() & the ASCII Character index. I’m not going to go into anything too deep with these, but they need to be understood before we can venture forth. Let’s start with the ASCII Table.

ASCII Characters

I’m sure you’ve heard of ASCII, and those of us who were poodling around the internet in the 90s have likely created some ASCII “art” out of these characters in text editors and forum posts. I remember ROFLCopter quote fondly. Also Lollerskates. Anyway…

ASCII stands for the American Standard Code for Information Interchange. What exactly is it anyway? ASCII is one of the most popular formats for text files on computers. This is a format where each alphanumeric character could be represented by a binary number (a series of 0s and 1s, typically seven of them represent an ASCII character). There were a total of 128 characters possible in ASCII and our trusty uppercase letters that we’ll be using for this Bonfire are among them. Wikipedia has a very thorough article about ASCII along with a helpful chart.

The chart is split into a number of columns, each representing a character. You have the binary numbering which is typically seven characters in 0s and 1s. You have an Octal number, a Decimal number, a Hex and finally, the Glyph which represents the characters we’re most familiar with. For this Bonfire, we’ll be using the Decimal number to reference the Glyphs, or capital letters.

Without making you do the math, our capital A is 65 and capital Z is 90. All the characters in between fall in order between those two characters, for example, Q is 81. So we now know the range we can use to evaluate and decipher our alphabet: 65 to 90.

Exploring charCodeAt()

charCodeAt() is a method that returns an integer between 0 and 65535 which corresponds to UTF-16 encoding. Without getting too deep into it, what we need to know is that the first 128 returns correspond to the 128 ASCII glyphs’ decimal number. This means we can use charCodeAt() to determine what number each of the characters in our coded message falls on and since we now have a number instead of a character, we can use that to walk back or forth to decode the cipher.

Here’s an example of charCodeAt() in action:

var sentence = "A great gray dane";
console.log(sentence.charCodeAt(4));

logs: 101

Oh boy, 101. If we look that up in our handy ASCII chart, 101 is the lowercase letter “e”, and that just so happens to be the e in our string: “a great gray dane”. Here’s another example of charCodeAt() in action, this time we’re going to walk it forward by 5 characters:

var sentence = "A great gray dane";
console.log(sentence.charCodeAt(4)+5);

logs: 105

Yeah, we just did basic arithmetic, but 105 is the lowercase character for “i”, and if it can walk us forward by 5, it can help us with our Caesar’s Cipher Bonfire by allowing us to walk forward 13 characters. Here’s charCodeAt() at work again, this time with an entire string of characters. So we’re converting our little sentence into an array containing ASCII Decimals:

var sentence = "A great gray dane";
var convert = [];
for (var i = 0; i < sentence.length; i++) {
convert.push(sentence.charCodeAt(i));
console.log(convert);
}

logs: [65, 32, 103, 114, 101, 97, 116, 32, 103, 114, 97, 121, 32, 100, 97, 110, 101]

So that’s all set, right? We got an understanding of ASCII and charCodeAt() will let us move around in the ASCII table. Theoretically, we can use these two concepts to solve our problem and move on with our lives. Not so fast though, there’s still the problem of converting back to characters from the decimals in our ASCII table. This is where fromCharCode() comes in handy.

Looking Into fromCharCode()

fromCharCode() takes a unicode number and converts it back to a character or glyph, it operates on the same order and numbers that correspond to the 128 characters in ASCII. This is exactly what we’re looking for because up until now, we’ve been converting our letters into numbers.

And now when we convert those decimals back into characters using our glyphs, we want to use fromCharCode(), which looks like this:

var sentence = "A great gray dane";
var convert = [];
var reconvert = [];

// Loops through sentence and changes each character into a decimal ASCII value.
for (var i = 0; i < sentence.length; i++) {
convert.push(sentence.charCodeAt(i));
}
console.log(convert);

// Loops through convert[] and changes its decimal ASCII places into characters using fromCharCode().
for (var j = 0; j < convert.length; j++) {
reconvert.push(String.fromCharCode(convert[j]));
}

console.log(reconvert);

logs: [65, 32, 103, 114, 101, 97, 116, 32, 103, 114, 97, 121, 32, 100, 97, 110, 101]
[“A”, ” “, “g”, “r”, “e”, “a”, “t”, ” “, “g”, “r”, “a”, “y”, ” “, “d”, “a”, “n”, “e”]

Awesome, converted and reconverted again. Now, we can go on to clean that array up and turn it back into a string using join(), something we’ve looked at before in a previous Bonfire (Title Case a Sentence) to get that array back into a more readable string, but that’s the easier part. Now that we know about these three parts, let’s construct our solution for the Caesar’s Cipher Bonfire.

The First Conditional and Loop

Let’s take what we had before and start working out our first loop. In case you missed it, we’re looking at only converting and deciphering things using the capital letters portion of the ASCII table. Capital A is 65 and Capital Z is 90. So I know that whatever I check will have to fall within that range of 65 – 90. I’m going to start pushing values to my empty deciphered array immediately as I check and roll back the letters so what I want is an if statement nested inside of my for loop. Here’s what that’s going to look like:

function rot13(str) { // LBH QVQ VG!

    var deciphered = [];
    for (var i = 0; i < str.length; i++) { if ((str.charCodeAt(i) >= 65) && (str.charCodeAt(i) <= 90)) {
                deciphered.push(String.fromCharCode(90 + str.charCodeAt(i) - 65 - 12));
            } else {
                deciphered.push(String.fromCharCode(str.charCodeAt(i)));
            }
    }

    return deciphered.join('');

}

// Change the inputs below to test
rot13("SERR PBQR PNZC");

Now, this looks all fine and dandy from first glance, but if we really take a look at it, it’s missing a couple of critical pieces. I’m going to go ahead and explain what it’s doing now though, even though it isn’t a fully working solution yet. What we’ve got up there is an if statement that checks str (the encoded message) to make sure that whatever we do to it, it always falls within our range of 65 and 90. When it verifies that, it runs a push() method on our empty deciphered array to ensure that each character it deciphers by rolling things back that it remains in that range by wrapping the decimals around. So for example, if rolling back a character takes it down to 64, the code will force it to wrap around to 90 instead of letting it fall to the ASCII character at the 64 decimal position. Otherwise, we just push the character code into our array just to be on the safe side. This chunk you see above is all for wrapping safety, and it was all written to make sure we never fall outside of our desired range of 65 – 90.

If you ran the above code, you’ll notice only a few select characters get decoded. These are mostly the characters that end up wrapping around. So at least we got those dealt with. Let’s finish this up and start rolling back the characters that don’t wrap around.

The Caesar’s Cipher Solution

Having built in the range safety above, I’m going to actually push characters now using the rot13 key. We want to nest the above if conditional in another if conditional that will check and roll back the characters by 13 places. Here’s what that will look like:

function rot13(str) { // LBH QVQ VG!

    var deciphered = [];
    for (var i = 0; i < str.length; i++) {

        if (str.charCodeAt(i) - 13 < 65) { if ((str.charCodeAt(i) >= 65) && (str.charCodeAt(i) <= 90)) {
                deciphered.push(String.fromCharCode(90 + str.charCodeAt(i) - 65 - 12));
            } else {
                deciphered.push(String.fromCharCode(str.charCodeAt(i)));

            }
        } else {
            deciphered.push(String.fromCharCode(str.charCodeAt(i) - 13));
        }

    }

    return deciphered.join('');

}

// Change the inputs below to test
rot13("SERR PBQR PNZC");

In the above code, we have an if statement added above our previous conditional. That if statement checks whatever character we want to roll back to see if it will be less than 65. If that evaluates to true (where a character is less than 65), it falls into the if conditional inside that checks to make sure the character stays in the 65 – 90 range, if it does, we initial the code roll the character back while forcing it to remain within range. Now if our exterior if conditional returns false, it falls into the external else statement that will simply roll our characters back by 13 places. Each of these evaluations and rollbacks are done within a push() method that adds the resulting values to the deciphered array.

By the end of the function, we return a deciphered array connected to a join() method so it results in a tidy looking string. Put that in and evaluate it and it should pass the Caesar’s Cipher Bonfire. Phew, that was a long one. This is far from the most elegant solution to this Bonfire, so feel free to share your solutions in the comments!

Glad to have gotten that one done. Between how busy I’ve been and how hard this one was for me, I’ll be glad to be moving on to the next Javascript challenge. This series isn’t going to end here. After all, we still have the Intermediate and Advanced Algorithms and Bonfires to work through. But I do plan on going back and covering some more basic Javascript and programming basics while simultaneously addressing the Intermediate Bonfires. So I hope to see you there!

Resources

Self Taught JS, Rot13 Caesar Cipher
Invaluable resource that heavily inspired the solution I came up with for this Bonfire.

Coding Tutorials 360, Caesar’s Cipher
A different approach to solving the Caesar’s Cipher, using a written out alphabet instead of messing with ASCII.

Unicode Solution for Caesar’s Cipher
A really pretty looking (can code be pretty?) solution using Unicode.

Allure Solutions, Caesar’s Cipher
A very similar solution to mine, written a little more cleanly. There are some great alternatives and modifications in the comments to check out too.


18 comments on “Bonfire: Caesar’s Cipher Solution

  1. Andre on

    Yes Yah Yippee!
    Thank you very much for your awesome help! Throughout some of these Bonfire challenges, I was able to rely on your assistance and guidance. There are some sites or posts that just give you the answer. That is no help at all when one wants to understand and figure out the problem to learn from it.
    Your posts really helped and taught me a lot!

    Thanks again!

    Reply
  2. JB on

    Your explanations are so helpful! Really appreciate how you break down the problem and explain each step. Has helped me tremendously! Thank you, thank you, thank you 🙂

    Reply
  3. Jamie on

    Thank you so much for this page!!! I would not have gotten through all the algorithm challenges without you, total life saver, plus your explanations made everything so much easier to understand then most of the pages FCC links to. Thank You!!!!

    Reply
  4. Adrienne Bing on

    This was such a lifesaver! You explained the code well and I finally figured out what I was doing wrong. Thank you so much for posting, now I can move on not only with a solution but an understanding of how to get there.

    Reply
  5. Hugo on

    Finally, I’m finished these algorithm challenge!
    I would not have succeeded without your help, I was like “Where do I start ??!” and the part “Approaching the problem” was very helpful. I finally had an idea where to start. It’s the hardest part for me
    Everything is very well explained, I learn a lot, you’re a good teacher 🙂

    So I just wanted to say thanks and keep it up !

    ps: Sorry if my english is bad

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *