22 votes

Programming Challenge: Make a Caesar cipher!

The point of this thread is to post your code for solving the task. Other will comment with feedback and new ideas. Post what language (and version, if relevant) your code is written in.
Have fun!

Task description

Your task is to make a caesar cipher that takes a word and an integer as arguments.
An article explaining what the cipher does.

Input description

A word followed by a space and an integer.

Output description

The ciphered word.

Sample onput

A 1
Caesar 5
Tildes 25

Sample output

B
Hfjxfw
Shkcdr

Bonus 1

Make the cipher work backwards.

Sample input

B 1
Hfjxfw 5
Shkcdr 25

Sample output

A
Caesar
Tildes

Bonus 2

Make the cipher handle special characters.

Sample onput

A_ 1
Cae?sar 5
Til!des 25

Sample output

B
Hfj?xfw
Shk!cdr

33 comments

  1. [4]
    kgz
    (edited )
    Link
    I like python. def caesar(s, i): return ''.join([[lambda _: list(filter( lambda _: _[0],[(__(), _()) for __, _ in { lambda: _ >= 97 and _ <= 122: lambda: ___(_, 96), lambda: _ >= 65 and _ <= 90:...

    I like python.

    def caesar(s, i):
        return ''.join([[lambda _: list(filter(
            lambda _: _[0],[(__(), _()) for __, _ in {
            lambda: _ >= 97 and _ <= 122: lambda: ___(_, 96),
            lambda: _ >= 65 and _ <= 90: lambda: ___(_, 64),
            lambda: 1: lambda: chr(_)}.items()]))[0][1] for ___ in [
            lambda _, __: chr((_ - __ + i) % 26 + __)]
        ][0](ord(___)) for ___ in s])
    
    print(caesar('A', 1))
    print(caesar('Caesar', 5))
    print(caesar('Tildes', 25))
    
    5 votes
    1. [2]
      skosulor
      Link Parent
      Could you explain how your code works? I like python as well but am lacking a bit in experience of it and this makes no sense to me.

      Could you explain how your code works? I like python as well but am lacking a bit in experience of it and this makes no sense to me.

      1 vote
      1. jgb
        Link Parent
        This program is intentionally very obtuse, but what it's doing is converting each character into an ASCII code, 'normalising' those codes so that they lie in the range 1...26 (regardless of...

        This program is intentionally very obtuse, but what it's doing is converting each character into an ASCII code, 'normalising' those codes so that they lie in the range 1...26 (regardless of whether they are upper or lower case), shifting the codes by the required amount, then converting them back into ASCII codes in the required region of the table (for upper or lower case characters respectively). This program relies on the fact that the alphabet is contiguous in ASCII, so that ASCII(J) + 6 = ASCII(P), for example. It also relies on the fact that Caeser shift wraps around, allowing the shifted code to be coerced back into the 1...26 range with the modulo operator.

        1 vote
    2. Social
      Link Parent
      You and your one-liners. :P

      You and your one-liners. :P

  2. [2]
    Elusive
    (edited )
    Link
    My solution in C/C++, including the bonus challenges: void caesar(char* message, int shift) { while (*message) { char base = 0; if (*message >= 'A' && *message <= 'Z') base = 'A'; if (*message >=...

    My solution in C/C++, including the bonus challenges:

    void caesar(char* message, int shift) {
        while (*message) {
            char base = 0;
            if (*message >= 'A' && *message <= 'Z') base = 'A';
            if (*message >= 'a' && *message <= 'z') base = 'a';
            if (base) *message = ((*message - base + shift) % 26 + 26) % 26 + base;
            ++message;
        }
    }
    
    4 votes
    1. Wombat
      Link Parent
      Props. One of the cleanest solutions and in c++. That's a bamf right here

      Props. One of the cleanest solutions and in c++. That's a bamf right here

      1 vote
  3. [6]
    sniper24
    (edited )
    Link
    https://github.com/tsikorksi/historic-crypto I tried my hand hand at some simple cryptography a while ago, but here's the relevant functions : def shift_encode(message, shift): x = [] if shift >=...

    https://github.com/tsikorksi/historic-crypto I tried my hand hand at some simple cryptography a while ago, but here's the relevant functions :

    def shift_encode(message, shift):
       x = []
       if shift >= 26:
           shift = shift % 26
       for i in range(0, len(message)):
           message_int = ord(message[i])
           if message_int + shift > 122:
               message_int = message_int + shift - 26
           else:
               message_int += shift
           x.append(chr(message_int))
       print(x)
    
    
    def shift_decode(message, shift):
       x = []
       if shift >= 26:
           shift = shift % 26
       for i in range(0, len(message)):
           message_int = ord(message[i])
           if message_int - shift < 97:
               message_int = message_int + 26
           message_int -= shift
           x.append(chr(message_int))
       print(x)
    
    
    def shift_brute(message):
       x = []
       for i in range(0, 26):
           for j in range(0, len(message)):
               temp = ord(message[j])
               if temp + i > 122:
                   temp = temp + i - 26
               else:
                   temp += i
               x.append(chr(temp))
           print(x)
           x = []
    

    Its not the finest solution but fairly easy to understand, despite the lack of comments (I've gotten better about that since I wrote this) and the output is poorly formatted here, but its better on the github.

    3 votes
    1. [5]
      bel
      Link Parent
      As an ex-teaching assistant, I'm obligated to look for the weird stuff no one cares about. What about negative shifts? Then again, it'd probably be better to clean up negatives in a common...

      As an ex-teaching assistant, I'm obligated to look for the weird stuff no one cares about. What about negative shifts? Then again, it'd probably be better to clean up negatives in a common preceding function, like when gathering input. Are negative shifts even legal in a Caesar cipher or are left and right shifts their own category?

      1 vote
      1. [4]
        sniper24
        Link Parent
        Negatives work fine, because the code changes the ASCII value, rather than the actual char, so a -4 shift will work fine.

        Negatives work fine, because the code changes the ASCII value, rather than the actual char, so a -4 shift will work fine.

        1 vote
        1. [3]
          bel
          Link Parent
          Would a -50? I have not tested it, but I assume it would fail given your initial if shift >= 26

          Would a -50? I have not tested it, but I assume it would fail given your initial if shift >= 26

          1. [2]
            sniper24
            (edited )
            Link Parent
            Unexpectedly, it works fine. What does break it is a negative value > 255 (or less than -255, you know what I mean)

            Unexpectedly, it works fine. What does break it is a negative value > 255 (or less than -255, you know what I mean)

            1. bel
              Link Parent
              In case you didn't catch it on reddit the other day, Hitting this Block for 416 Years Crashes Paper Mario is now relevant.

              In case you didn't catch it on reddit the other day, Hitting this Block for 416 Years Crashes Paper Mario is now relevant.

              2 votes
  4. [7]
    chewbacca
    Link
    MoonScript caesar = (input, shift) -> coded = "" for index = 1, input\len! char = input\sub index,index byte = char\byte! local byteOffset if byte >= 97 and byte <= 122 byteOffset = 96 elseif byte...

    MoonScript

    caesar = (input, shift) ->
    	coded = ""
    	for index = 1, input\len!	
    		char = input\sub index,index
    		byte = char\byte!
    		
    		local byteOffset
    		if byte >= 97 and byte <= 122
    			byteOffset = 96
    		elseif byte >= 65 and byte <= 90
    			byteOffset = 64
    		else
    			coded ..= char
    			continue
    			
    		coded ..= string.char (byte - byteOffset + shift) % 26 + byteOffset
    	coded
    	
    -- Tests
    print caesar("A", 1)		-- B
    print caesar("Caesar", 5)	-- Hfjxfw
    print caesar("Tildes", 25)	-- Shkcdr
    
    print caesar("B", -1)		-- A
    print caesar("Hfjxfw", -5)	-- Caesar
    print caesar("Shkcdr", -25)	-- Tildes
    
    print caesar("A_", 1)		-- B_
    print caesar("Cae?sar", 5)	-- Hfj?xfw
    print caesar("Til!des", 25)	-- Shk!cdr
    
    3 votes
    1. [6]
      bel
      Link Parent
      MoonScript isn't the most prevalent language, yet you used it two days in a row. What got you started on it?

      MoonScript isn't the most prevalent language, yet you used it two days in a row. What got you started on it?

      2 votes
      1. [5]
        chewbacca
        Link Parent
        MoonScript compiles to Lua, which I've had a lot of experience with. It's a cleaner syntax, allows for object-oriented programming (better than you'd get in Lua at least), and has more features....

        MoonScript compiles to Lua, which I've had a lot of experience with. It's a cleaner syntax, allows for object-oriented programming (better than you'd get in Lua at least), and has more features.
        If you're interested, I'd recommend looking at the reference or "Learn MoonScript in 15 Minutes".

        1. [2]
          sniper24
          Link Parent
          How mature would you say the language is? How useful do you find it for larger scripts?

          How mature would you say the language is? How useful do you find it for larger scripts?

        2. [2]
          bel
          Link Parent
          I'm halfway through skimming your Learn MoonScript link, and this stuff is crazy. Dynamic typing is always pleasant to deal with, but the dependency on spacing for function calls and negation...

          I'm halfway through skimming your Learn MoonScript link, and this stuff is crazy. Dynamic typing is always pleasant to deal with, but the dependency on spacing for function calls and negation makes me think debugging would be a nightmare. Do you use Lua professionally or for some hobby?

          1. chewbacca
            Link Parent
            You don't have to use that spacing. You can always call functions normally (i.e., with parenthesis.) The spacing is just syntactic sugar for when you think it'd look nicer. The script behaves...

            You don't have to use that spacing. You can always call functions normally (i.e., with parenthesis.) The spacing is just syntactic sugar for when you think it'd look nicer. The script behaves exactly the same when written as follows:

            caesar = (input, shift) ->
            	coded = ""
            	for index = 1, input\len()
            		char = input\sub(index,index)
            		byte = char\byte()
            		
            		local byteOffset
            		if (byte >= 97 and byte <= 122)
            			byteOffset = 96
            		elseif (byte >= 65 and byte <= 90)
            			byteOffset = 64
            		else
            			coded ..= char
            			continue
            			
            		coded ..= string.char((byte - byteOffset + shift) % 26 + byteOffset)
            	coded
            

            I don't use it professionally, just as a hobby.

  5. Kremor
    Link
    Here's my solution in rust. It has a very basic argument parsers: The first argument must be the string that you want to encrypt. The second argument is the number of times you want to shift a...

    Here's my solution in rust. It has a very basic argument parsers:

    1. The first argument must be the string that you want to encrypt.
    2. The second argument is the number of times you want to shift a letter, if the argument is not a valid number, it uses 0 instead.
    3. Adding more arguments will shift backwards, it doesn't matter what the arguments are or how many.
        use std::env;
        
        fn cipher (string: &str, offset: u8, backwards: bool) -> String {
            let mut bytes: Vec<u8> = Vec::new();
        
            let offset = offset % 26;
        
            for b in string.as_bytes() {
                match b {
                    65...90 => bytes.push(
                        if backwards {
                             (*b - 13 - offset) % 26 + 65
                        } else {
                            (*b - 13 + offset) % 26 + 65
                        } 
                    ),
                    97...122 => bytes.push(
                        if backwards {
                            (*b - 19 - offset) % 26 + 97
                        } else {
                            (*b - 19 + offset) % 26 + 97
                        }
                    ),
                    _ => bytes.push(*b),
                }
            } 
    
            match String::from_utf8(bytes) {
                Ok(s) => s,
                Err(_) => String::from("Error!: Unable to process string"),
            }
        }
    
        fn main() {
            let args: Vec<_> = env::args().collect();
    
            if args.len() < 3 {
                println!("Not enought arguments.");
                return;
            }
    
            let string = &args[1];
            let offset = match args[2].parse::<u8>() {
                Ok(n) => n,
                Err(_) => 0
            };
            let backwards = if args.len() >= 4 {true} else {false};
            
            println!("{}", cipher(string, offset, backwards));
        }
    
    3 votes
  6. ABC
    (edited )
    Link
    def caesar(text, num): outstr = "" for i in range(len(text)): if ("A" <= text[i] and text[i] <= "Z"): outstr += chr( (((ord(text[i]) - ord("A")) + num) % 26) + ord("A") ) elif ("a" <= text[i] and...
    def caesar(text, num):
    	outstr = ""
    	for i in range(len(text)):
    		if ("A" <= text[i] and text[i] <= "Z"):
    			outstr += chr( (((ord(text[i]) - ord("A")) + num) % 26) + ord("A") )
    		elif ("a" <= text[i] and text[i] <= "z"):
    			outstr += chr( (((ord(text[i]) - ord("a")) + num) % 26) + ord("a") )
    		else:
    			outstr += text[i]
    	return outstr
    
    >>> caesar("test", 3)
    'whvw'
    >>> caesar("whvw", -3)
    'test'
    >>> caesar("te!st", 3)
    'wh!vw'
    >>> caesar("wh!vw", -3)
    'te!st'
    >>>
    
    2 votes
  7. tyil
    Link
    Perl 6 Code #! /usr/bin/env false use v6.c; #| Specify an exported sub which takes and returns the format specified in the #| challenge. sub caesar ( Str:D $input, --> Str ) is export { # Use a...

    Perl 6

    Code

    #! /usr/bin/env false
    
    use v6.c;
    
    #| Specify an exported sub which takes and returns the format specified in the
    #| challenge.
    sub caesar (
    	Str:D $input,
    	--> Str
    ) is export {
    	# Use a regex to extract the cleartext and the shift value from the input
    	if ($input ~~ / ^ (.*) " " (\d+) $ /) {
    		return shift-string(~$0, +$1);
    	}
    }
    
    sub caesar-reverse (
    	Str:D $input,
    	--> Str
    ) is export {
    	# Use a regex to extract the cleartext and the shift value from the input
    	if ($input ~~ / ^ (.*) " " (\d+) $ /) {
    		return shift-string(~$0, -+$1);
    	}
    }
    
    #| Shift all characters in a given C<Str> a number of C<$shift> times.
    sub shift-string (
    	Str:D $clear,
    	Int:D $shift,
    	--> Str
    ) {
    	$clear.comb.map({ shift-char($_, $shift) }).join;
    }
    
    #| Shift a single character a number of C<$shift> times.
    multi sub shift-char (
    	Str:D $character is copy where *.chars == 1,
    	Int:D $shift where * > 0,
    	--> Str
    ) {
    	for ^($shift % 26) {
    		$character = shift-forward($character);
    	}
    
    	$character;
    }
    
    multi sub shift-char (
    	Str:D $character is copy where *.chars == 1,
    	Int:D $shift where * < 0,
    	--> Str
    ) {
    	for ^(-$shift % 26) {
    		$character = shift-backward($character);
    	}
    
    	$character;
    }
    
    #| Shift a single character one time
    sub shift-forward (
    	Str:D $character where *.chars == 1,
    	--> Str
    ) {
    	return 'a' if $character eq 'z';
    	return 'A' if $character eq 'Z';
    
    	$character.succ;
    }
    
    sub shift-backward (
    	Str:D $character where *.chars == 1,
    	--> Str
    ) {
    	return 'z' if $character eq 'a';
    	return 'Z' if $character eq 'A';
    
    	$character.pred;
    }
    

    Tests

    Challenge

    #! /usr/bin/env perl6
    
    use v6.c;
    
    use Local::Tildes::Challenge;
    use Test;
    
    plan 3;
    
    is caesar("A 1"), "B";
    is caesar("Caesar 5"), "Hfjxfw";
    is caesar("Tildes 25"), "Shkcdr";
    

    Bonus 1

    #! /usr/bin/env perl6
    
    use v6.c;
    
    use Local::Tildes::Challenge;
    use Test;
    
    plan 3;
    
    is caesar-reverse("B 1"), "A";
    is caesar-reverse("Hfjxfw 5"), "Caesar";
    is caesar-reverse("Shkcdr 25"), "Tildes";
    

    Bonus 2

    #! /usr/bin/env perl6
    
    use v6.c;
    
    use Local::Tildes::Challenge;
    use Test;
    
    plan 3;
    
    is caesar("A_ 1"), "B_";
    is caesar("Cae?sar 5"), "Hfj?xfw";
    is caesar("Til!des 25"), "Shk!cdr";
    

    Notes

    I've changed one test for the second bonus part, where the original challenge posed that A_ should become B, dropping the trailing _. This behaviour looks odd, and since there's other typos in the OP as well I assumed that one to be a type as well.

    Having looked through the comments a bit, I've noticed most people don't even pass the actual challenge, as they don't use a single string as output. Instead, they already split the clear-text string and the number of shift rounds manually when handing it over to a function.

    2 votes
  8. thykka
    Link
    No JavaScript solution? OK, I'll bite... JavaScript const cc = { A: 'A'.charCodeAt(0), Z: 'Z'.charCodeAt(0), a: 'a'.charCodeAt(0), z: 'z'.charCodeAt(0) } const toCharCode = letter =>...

    No JavaScript solution? OK, I'll bite...

    JavaScript

    const cc = {
      A: 'A'.charCodeAt(0),
      Z: 'Z'.charCodeAt(0),
      a: 'a'.charCodeAt(0),
      z: 'z'.charCodeAt(0)
    }
    const toCharCode = letter => letter.charCodeAt(0);
    const fromCharCode = charCode => String.fromCharCode(charCode);
    const isLowerCaseAlpha = lcc => lcc >= cc.a && lcc <= cc.z;
    const isUpperCaseAlpha = lcc => lcc >= cc.A && lcc <= cc.Z;
    const modulo = (n, m) => ((n % m) + m) % m;
    const caesar = (lcc, shift) =>
      isUpperCaseAlpha(lcc) ? modulo((lcc - cc.A + shift), 26) + cc.A :
      isLowerCaseAlpha(lcc) ? modulo((lcc - cc.a + shift), 26) + cc.a :
      lcc;
    const cipher = (text, shift) =>
      text.split('').map(toCharCode)
      .map(lcc => caesar(lcc, shift))
      .map(fromCharCode).join('');
    const uncipher = (text, shift) => cipher(text, -shift);
    

    tests

    console.assert(cipher('A', 1)       === 'B');
    console.assert(cipher('Caesar', 5)  === 'Hfjxfw');
    console.assert(cipher('Tildes', 25) === 'Shkcdr');
    
    console.assert(uncipher('B', 1)       === 'A');
    console.assert(uncipher('Hfjxfw', 5)  === 'Caesar');
    console.assert(uncipher('Shkcdr', 25) === 'Tildes');
    
    console.assert(cipher('A_', 1)       === 'B_');
    console.assert(cipher('Cae?sar', 5)  === 'Hfj?xfw');
    console.assert(cipher('Til!des', 25) === 'Shk!cdr');
    
    2 votes
  9. [2]
    ruspaceni
    (edited )
    Link
    So i just gave this a bit of a go since I haven't done much scratch programming recently. Just fixing bugs/logic errors in my discord bot and whatnot. Sorry for the abomonation i just created. I...

    So i just gave this a bit of a go since I haven't done much scratch programming recently. Just fixing bugs/logic errors in my discord bot and whatnot. Sorry for the abomonation i just created. I think it works though

    https://goplay.space/#FbAEPwlpkdX

    
    import (
    	"fmt"
    	"strings"
    )
    
    func main() {
    	fmt.Println(ceastheday("haha yes", 14))
    
    }
    
    func ceastheday(s string, n int) (c string) {
    	dontjudgemeforthis := "abcdefghijklmnopqrstuvwxyz"
    	enc := ""
    	for x := 0; x < len(s); x++ {
    		//fmt.Println(strings.ToLower(string(s[x])), x,strings.Contains(dontjudgemeforthis, string(s[x])))
    
    		if strings.Contains(dontjudgemeforthis, string(s[x])) {
    			//curletter := string(s[x])
    			curindex := (strings.Index(dontjudgemeforthis, string(s[x])) + n) % 26
    			fmt.Println(curindex)
    			enc += string(dontjudgemeforthis[curindex])
    		} else {
    			enc += " "
    		}
    	}
    
    	return enc
    }
    

    e: A friend just gave this a go (wayyyy more experienced than me) so here's a more golang-ish approach
    https://goplay.space/#G8OxQ-5ILq8

    1 vote
    1. planNine
      Link Parent
      I see what you did there...

      So i just gave this a bit of a go

      I see what you did there...

      2 votes
  10. [2]
    BBBence1111
    Link
    C# code. Works forwards and backwards, should handle whatever special characters you code into it. Input is changed to: word number decrypt? Decrypts on "y". char[] chars = new char[] {'a', 'b',...

    C# code. Works forwards and backwards, should handle whatever special characters you code into it. Input is changed to:

    word number decrypt?
    

    Decrypts on "y".

            char[] chars = new char[] {'a', 'b', 'c', 'd' };
            string[] input = Console.ReadLine().Split(' ');
            string output = "";
            int increase = 0;
            for (int i = 0; i < input[0].Length; i++)
            {
                for (int j = 0; j < chars.Length; j++)
                {
                    if (input[0][i] == chars[j])
                    {
                        if (input[2] == "y")
                        {
                            increase = j - int.Parse(input[1]);
                            if (increase < 0)
                            {
                                increase = increase + chars.Length;
                            }
                        }
                        else
                        {
                            increase = j + int.Parse(input[1]);
                            if (increase >= chars.Length)
                            {
                                increase = increase - chars.Length;
                            }
                        }
                        output = output + chars[increase];
                        increase = 0;
                    }
                }
            }
            Console.WriteLine(output);
            Console.ReadLine();
    
    1 vote
    1. BBBence1111
      Link Parent
      I just noticed that this breaks if you use more than twice the number of characters. To fix that, change the if statement that checks if it's in range into a while loop.

      I just noticed that this breaks if you use more than twice the number of characters. To fix that, change the if statement that checks if it's in range into a while loop.

  11. [6]
    Scion
    (edited )
    Link
    Edit: Found a bug where if the key is too large (like 2000) then it doesn't work properly. I'm not sure why so if anyone knows I'd appreciate if you could share. Very new to C (learning it through...

    Edit: Found a bug where if the key is too large (like 2000) then it doesn't work properly. I'm not sure why so if anyone knows I'd appreciate if you could share.

    Very new to C (learning it through CS50 ), but I actually was on the Caesar Cypher assignment today anyway, so good timing. I'm positive there are ways to do this infinitely better, but this works as far as I can tell. And yes, I'm using the library the course staff built for class use, which conveniently includes a get_string() function...

    #include <cs50.h>
    #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    //Caesar Cypher -- shift all letters in plaintext by key
    int main(int argc, string argv[]) {
        int key;
        // confirm only 1 command line argument, convert argument (key) to integer
        if (argc == 2){
            string arg = argv[1];
            key = atoi(arg);
        } else {
            printf("Error: Only enter one argument\n");
            return 1;
        }
        // Get plaintext to encrypt, store in string variable 'plain'
        string plain = get_string("Enter your text here >> ");
    
        // Get length of plaintext input
        int len = strlen(plain);
    
        // Shift each letter by the key, preserving case
        int i;
        for (i = 0; i < len; i++) {
            if (islower(plain[i])) {
                plain[i] = plain[i] - 'a';
                plain[i] += key;
                plain[i] = (plain[i] % 26) + 'a';
                printf("%c", plain[i]);
            } else if (isupper(plain[i])) {
                plain[i] = plain[i] - 'A';
                plain[i] += key;
                plain[i] = (plain[i] % 26) + 'A';
                printf("%c", plain[i]);
            } else {
                printf("%c", plain[i]);
            }
        }
        printf("\n");
        return 0;
    }
    
    1 vote
    1. [2]
      Social
      Link Parent
      CS50 is a great course. Enjoy it! :)

      CS50 is a great course. Enjoy it! :)

      1. Scion
        Link Parent
        Thanks! So far so good.

        Thanks! So far so good.

    2. [3]
      planNine
      Link Parent
      In response to the edit: why would you even need such a big key (2000)?

      In response to the edit: why would you even need such a big key (2000)?

      1. [2]
        Scion
        Link Parent
        You wouldn't as far as I can tell. But in theory the key size shouldn't matter, yet it seems to.

        You wouldn't as far as I can tell. But in theory the key size shouldn't matter, yet it seems to.

        1. Wombat
          Link Parent
          Theory breaks down when the unpredictable happens in reality: hypothetical new user interfaces allow for words to be mapped to a new letter encoding. That's highly unlikely but whose to say it...

          Theory breaks down when the unpredictable happens in reality: hypothetical new user interfaces allow for words to be mapped to a new letter encoding. That's highly unlikely but whose to say it won't happen