Skip to content

Instantly share code, notes, and snippets.

@martin-mok
Last active July 5, 2023 16:56
Show Gist options
  • Save martin-mok/73f8d19393b34794233588840ab8653c to your computer and use it in GitHub Desktop.
Save martin-mok/73f8d19393b34794233588840ab8653c to your computer and use it in GitHub Desktop.
freecodecamp: Caesars Cipher

Description

One of the simplest and most widely known ciphers is a Caesar cipher, also known as a shift cipher. In a shift cipher the meanings of the letters are shifted by some set amount. A common modern use is the ROT13 cipher, where the values of the letters are shifted by 13 places. Thus 'A' ↔ 'N', 'B' ↔ 'O' and so on. Write a function which takes a ROT13 encoded string as input and returns a decoded string. All letters will be uppercase. Do not transform any non-alphabetic character (i.e. spaces, punctuation), but do pass them on.

Tests

tests:
  - text: <code>rot13("SERR PBQR PNZC")</code> should decode to <code>FREE CODE CAMP</code>
    testString: assert(rot13("SERR PBQR PNZC") === "FREE CODE CAMP");
  - text: <code>rot13("SERR CVMMN!")</code> should decode to <code>FREE PIZZA!</code>
    testString: assert(rot13("SERR CVMMN!") === "FREE PIZZA!");
  - text: <code>rot13("SERR YBIR?")</code> should decode to <code>FREE LOVE?</code>
    testString: assert(rot13("SERR YBIR?") === "FREE LOVE?");
  - text: <code>rot13("GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT.")</code> should decode to <code>THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.</code>
    testString: assert(rot13("GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT.") === "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.");

Solution

Offical soln:

var lookup = {
  'A': 'N','B': 'O','C': 'P','D': 'Q',
  'E': 'R','F': 'S','G': 'T','H': 'U',
  'I': 'V','J': 'W','K': 'X','L': 'Y',
  'M': 'Z','N': 'A','O': 'B','P': 'C',
  'Q': 'D','R': 'E','S': 'F','T': 'G',
  'U': 'H','V': 'I','W': 'J','X': 'K',
  'Y': 'L','Z': 'M'
};

function rot13(encodedStr) {
  var codeArr = encodedStr.split("");  // String to Array
  var decodedArr = []; // Your Result goes here
  // Only change code below this line

  decodedArr = codeArr.map(function(letter) {
    if(lookup.hasOwnProperty(letter)) {
      letter = lookup[letter];
    }
    return letter;
  });

  // Only change code above this line
  return decodedArr.join(""); // Array to String
}

My soln:

function rot13(str) { // LBH QVQ VG!
  const Acode='A'.charCodeAt();
  const Ncode='N'.charCodeAt();
  const Zcode='Z'.charCodeAt();
  return [...str].map(
    function(e){
      const code=e.charCodeAt();
      if(Acode<=code && code<=Zcode){
        if(code<Ncode){
          return String.fromCharCode(code+13);
        } else {
          return String.fromCharCode(code-13);
        }
      } else {
        return e;
      }
    }
  ).join("");
}

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

freeCodeCamp Challenge Guide: Caesars Cipher:
Soln 2:

// Solution with Regular expression and Array of ASCII character codes
function rot13(str) {
  var rotCharArray = [];
  var regEx = /[A-Z]/;
  str = str.split("");
  for (var x in str) {
    if (regEx.test(str[x])) {
      // A more general approach
      // possible because of modular arithmetic
      // and cyclic nature of rot13 transform
      rotCharArray.push(((str[x].charCodeAt() - 65 + 13) % 26) + 65);
    } else {
      rotCharArray.push(str[x].charCodeAt());
    }
  }
  str = String.fromCharCode.apply(String, rotCharArray);
  return str;
}
Algorithm Explanation:

    ALPHA	KEY	BASE 	 	 	 ROTATED	ROT13
    -------------------------------------------------------------
    [A]     65  <=>   0 + 13  =>  13 % 26  <=>  13 + 65 = 78 [N]
    [B]     66  <=>   1 + 13  =>  14 % 26  <=>  14 + 65 = 79 [O]
    [C]     67  <=>   2 + 13  =>  15 % 26  <=>  15 + 65 = 80 [P]
    [D]     68  <=>   3 + 13  =>  16 % 26  <=>  16 + 65 = 81 [Q]
    [E]     69  <=>   4 + 13  =>  17 % 26  <=>  17 + 65 = 82 [R]
    [F]     70  <=>   5 + 13  =>  18 % 26  <=>  18 + 65 = 83 [S]
    [G]     71  <=>   6 + 13  =>  19 % 26  <=>  19 + 65 = 84 [T]
    [H]     72  <=>   7 + 13  =>  20 % 26  <=>  20 + 65 = 85 [U]
    [I]     73  <=>   8 + 13  =>  21 % 26  <=>  21 + 65 = 86 [V]
    [J]     74  <=>   9 + 13  =>  22 % 26  <=>  22 + 65 = 87 [W]
    [K]     75  <=>  10 + 13  =>  23 % 26  <=>  23 + 65 = 88 [X]
    [L]     76  <=>  11 + 13  =>  24 % 26  <=>  24 + 65 = 89 [Y]
    [M]     77  <=>  12 + 13  =>  25 % 26  <=>  25 + 65 = 90 [Z]
    [N]     78  <=>  13 + 13  =>  26 % 26  <=>   0 + 65 = 65 [A]
    [O]     79  <=>  14 + 13  =>  27 % 26  <=>   1 + 65 = 66 [B]
    [P]     80  <=>  15 + 13  =>  28 % 26  <=>   2 + 65 = 67 [C]
    [Q]     81  <=>  16 + 13  =>  29 % 26  <=>   3 + 65 = 68 [D]
    [R]     82  <=>  17 + 13  =>  30 % 26  <=>   4 + 65 = 69 [E]
    [S]     83  <=>  18 + 13  =>  31 % 26  <=>   5 + 65 = 70 [F]
    [T]     84  <=>  19 + 13  =>  32 % 26  <=>   6 + 65 = 71 [G]
    [U]     85  <=>  20 + 13  =>  33 % 26  <=>   7 + 65 = 72 [H]
    [V]     86  <=>  21 + 13  =>  34 % 26  <=>   8 + 65 = 73 [I]
    [W]     87  <=>  22 + 13  =>  35 % 26  <=>   9 + 65 = 74 [J]
    [X]     88  <=>  23 + 13  =>  36 % 26  <=>  10 + 65 = 75 [K]
    [Y]     89  <=>  24 + 13  =>  37 % 26  <=>  11 + 65 = 76 [L]
    [Z]     90  <=>  25 + 13  =>  38 % 26  <=>  12 + 65 = 77 [M]

Soln 3:

function rot13(str) {
  // LBH QVQ VG!
  return str.replace(/[A-Z]/g, L =>
    String.fromCharCode((L.charCodeAt(0) % 26) + 65)
  );
}
As you can notice, each number in the range of [65 - 90] maps to a unique number between [0 - 25].
You might have also noticed that each given number (e.g. 65) maps to another number (e.g. 13) which can be used as an offset value
(i.e. 65 + OFFSET) to get the ROT13 of the given number.

E.g. 65 maps to 13 which can be taken as an offset value and added to 65 to give 78.

    [A]  65 % 26 ⇔ 13 + 65 =  78 [N]
    [B]  66 % 26 ⇔ 14 + 65 =  79 [O]
    [C]  67 % 26 ⇔ 15 + 65 =  80 [P]
    [D]  68 % 26 ⇔ 16 + 65 =  81 [Q]
    [E]  69 % 26 ⇔ 17 + 65 =  82 [R]
    [F]  70 % 26 ⇔ 18 + 65 =  83 [S]
    [G]  71 % 26 ⇔ 19 + 65 =  84 [T]
    [H]  72 % 26 ⇔ 20 + 65 =  85 [U]
    [I]  73 % 26 ⇔ 21 + 65 =  86 [V]
    [J]  74 % 26 ⇔ 22 + 65 =  87 [W]
    [K]  75 % 26 ⇔ 23 + 65 =  88 [X]
    [L]  76 % 26 ⇔ 24 + 65 =  89 [Y]
    [M]  77 % 26 ⇔ 25 + 65 =  90 [Z]
    [N]  78 % 26 ⇔  0 + 65 =  65 [A]
    [O]  79 % 26 ⇔  1 + 65 =  66 [B]
    [P]  80 % 26 ⇔  2 + 65 =  67 [C]
    [Q]  81 % 26 ⇔  3 + 65 =  68 [D]
    [R]  82 % 26 ⇔  4 + 65 =  69 [E]
    [S]  83 % 26 ⇔  5 + 65 =  70 [F]
    [T]  84 % 26 ⇔  6 + 65 =  71 [G]
    [U]  85 % 26 ⇔  7 + 65 =  72 [H]
    [V]  86 % 26 ⇔  8 + 65 =  73 [I]
    [W]  87 % 26 ⇔  9 + 65 =  74 [J]
    [X]  88 % 26 ⇔ 10 + 65 =  75 [K]
    [Y]  89 % 26 ⇔ 11 + 65 =  76 [L]
    [Z]  90 % 26 ⇔ 12 + 65 =  77 [M]

Soln 4:

function rot13(str) {
    const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    return str
      .split('')
      .map(char => {  
        const pos = alphabet.indexOf(char);      
        return pos >= 0 ? alphabet[(pos + 13) % 26] : char;
      })
      .join('');
}
@Oluwaseun4244
Copy link

My solution:

function rot13(str) {
let arr = str.split("")
let result = []
let alphabets = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"

let alphabetsArray = alphabets.split("")
// let result
for (let i=0; i<arr.length; i++){

let index = alphabetsArray.findIndex(letter => {
return (letter === arr[i])
})
if (index >= 0){

result.push(alphabetsArray.slice(index + 13, index + 14)[0])
} else {
  result.push(arr[i])
}

}

return result.join("")
}

console.log(rot13("SERR PBQR PNZC"));

@TienHaiMGM
Copy link


function rot13(str) {
let regex = /./g;
let newStr = str.match(regex).join("");
let newArr = [];
for(let i = 0; i < newStr.length; i++){
let number = newStr.charCodeAt(i);
if(number < 78 && number >= 65){
number += 13;
} else if(number <= 90 && number >= 78){
number -=13;
}
newArr.push(String.fromCharCode(number));
}
newArr = newArr.join("");
return str.replace(newStr,newArr);
}

rot13("SERR PBQR PNZC");

@furkanimir
Copy link

function rot13(str) {
var astr=[];
var character;
var charCode;
str.split(" ");
for (var i = 0; i<str.length;i++){
for(var j = 0; j<str[i].length;j++){
if(str[i].charCodeAt(j)>=65 && str[i].charCodeAt(j)<=90){
charCode = str[i].charCodeAt(j) - 65 + 13;
charCode = (charCode%26)+65;
}else{
charCode=str[i].charCodeAt(j);
}
character = String.fromCharCode(charCode);
astr.push(character);
}
}
return astr.join('');
}
console.log(rot13("GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT.")); //THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.

@zehmCode
Copy link

zehmCode commented Aug 24, 2022

This is my solution but, I don't know why it doesn't submit:

const alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L","M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

function rot13(str) {
  str = str.split(" ");
  let getAlphabet;
  let newStr= "";
  for(var i = 0;i<str.length; i++){
    for(var j = 0; j<str[i].length; j++){
      if(alphabet.includes(str[i][j])){
        getAlphabet = alphabet.indexOf(str[i][j]) + 13;
        if(getAlphabet >= alphabet.length){
          getAlphabet -= 26;
        }
      }else{
        newStr+= str[i][j];
        break;
      }
      newStr+= str[i][j].replace(str[i][j], alphabet[getAlphabet]);
    }
    newStr+= " ";
  }
  str = strr;
  return str;
}

@Scuba2105
Copy link

This is my solution but, I don't know why it doesn't submit:

const alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L","M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

function rot13(str) {
  str = str.split(" ");
  let getAlphabet;
  let newStr= "";
  for(var i = 0;i<str.length; i++){
    for(var j = 0; j<str[i].length; j++){
      if(alphabet.includes(str[i][j])){
        getAlphabet = alphabet.indexOf(str[i][j]) + 13;
        if(getAlphabet >= alphabet.length){
          getAlphabet -= 26;
        }
      }else{
        newStr+= str[i][j];
        break;
      }
      newStr+= str[i][j].replace(str[i][j], alphabet[getAlphabet]);
    }
    newStr+= " ";
  }
  str = strr;
  return str;
}

Hi Zehm Code,
I don't know if you will see this but I have very slightly changed your code and it is now working. It seems there was a problem with your newStr += " " near the end of the code adding a space to the end of the return string. You can see it if you try and concatenate some text to the end. I just removed it with trim() and the code seems to be working OK. See the code below;

const alphabet = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L","M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

function rot13(str) {
str = str.split(" ");
let getAlphabet;
let newStr= "";
for(var i = 0;i<str.length; i++){
for(var j = 0; j<str[i].length; j++){
if(alphabet.includes(str[i][j])){
getAlphabet = alphabet.indexOf(str[i][j]) + 13;
if(getAlphabet >= alphabet.length){
getAlphabet -= 26;
}
}else{
newStr+= str[i][j];
break;
}
newStr+= alphabet[getAlphabet];
console.log(newStr)
}
newStr+= " ";
}
str = newStr.trim();

return str;
}

@Jeko92
Copy link

Jeko92 commented Jan 2, 2023

Hi,
That is a very good compilation of probable solutions to this problem and I think can be useful for everybody who would like to
experiment more. I thought I will add my version of the solution too.
Here it goes:

function rot13(str) {
  const alphabet = ['A','B', 'C','D','E','F','G','H','I','J','K','L','M','N', 'O','P','Q','R','S','T','U','V','W','X','Y','Z'];
  return str.split('')
                 .map( el => {let indexOfEl = alphabet.indexOf(el);
                                      return alphabet.indexOf(el) === -1 ? el  : 
                                                  indexOfEl-13 < 0 ? alphabet[indexOfEl+13] : 
                                                  alphabet[indexOfEl-13] })
                  .join('');
}

@n-prokofieva
Copy link

Hi! My solution is:

function rot13(str) {
  const alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];

  const decodedStr = [...str].reduce((acc,el) => {
    if (el.match(/[A-Z]/)) {
      let index = alphabet.indexOf(el);
      acc += alphabet[(index >= 13) ? (index - 13) : (index + 13)];
    } else {
      acc += el;
    }
    return acc;
  }, "");

  return decodedStr;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment