22
\$\begingroup\$

A Caesar shift is probably something we're all familiar with.

(You might be even doing it as a homework task. If so, please don't copy these answers, your teacher almost certainly doesn't want anything like the answers here.)

Just in case you aren't, a Caesar shift is a very simple form of cipher. It takes a string to be ciphered and an integer. Then for every alphabetical character in the string, perform the following transformation:

  1. Work out the character's position in the alphabet (0 based).
  2. Add to that number the integer received at the beginning.
  3. While the number is bigger than 25, subtract 26 from it.
  4. Work out the position of the alphabet it is in.

Leave the rest of the characters unshifted.

Capital letters have to be respected because what is English without capital letters?

Examples:

abcdefghijklmnopqrstuvwxyz 1 -> bcdefghijklmnopqrstuvwxyza
Spam spam spam sausage and spam! 13 -> Fcnz fcnz fcnz fnhfntr naq fcnz!
abcdefghijklmnopqrstuvwxyz 52 -> abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz -1 -> zabcdefghijklmnopqrstuvwxy
ABCxyz 3 -> DEFabc

Assumptions

  • You may receive any printable ASCII character
  • The input number can be negative and will always be bigger than -128 and less then 128 (-128<x<128)
  • You must be able to encode capital letters and non-capital letters reversibly.
  • You must create a full program, not just a function or snippet
  • You will get your input from STDIN or closest alternate
  • You may choose the format for your input, please state this in your answer
  • The characters that need to be shifted are ASCII codepoints 0x41 - 0x5A and 0x61-0x7A - upper and lower case letters

    • Upper case letters should stay upper
    • Lower case letters should stay lower
    • Characters not in this range should be left as they are
  • Note for this challenge, you only have to cipher strings, you don't have to be able to solve them automatically (but giving -x will reverse the cipher)


Since this is a catalog, languages created after this challenge are allowed to compete. Note that there must be an interpreter so the submission can be tested. It is allowed (and even encouraged) to write this interpreter yourself for a previously unimplemented language. Other than that, all the standard rules of must be obeyed. Submissions in most languages will be scored in bytes in an appropriate preexisting encoding (usually UTF-8).

Catalog

The Stack Snippet at the bottom of this post generates the catalog from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

## Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

## Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:

## [<><](https://esolangs.org/wiki/Fish), 121 bytes

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = 67044; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 32686; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "https://api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "https://api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang, user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

\$\endgroup\$
1
  • 8
    \$\begingroup\$ "You might be even doing it as a homework task. If so, please don't copy these answers, your teacher almost certainly doesn't want anything like the answers here." I wonder what would happen if you handed a teacher a 90-byte kludge of messy characters and shortcuts... \$\endgroup\$ Dec 19, 2015 at 1:43

16 Answers 16

9
\$\begingroup\$

Pyth, 13 bytes

uXGH.<HQrBG1z

Test suite

Basically, we start with the two strings we want to caesar shift, the lowercase and uppercase alphabets. The list containing both of these is generated by rBG1, bifurcate on uppercase. Then, we reduce over this list, starting with the input string and translating first lowercase, then uppercase letters by the appropriate shift.

\$\endgroup\$
1
  • \$\begingroup\$ Very nice, I keep forgetting bifurcation exists... :P \$\endgroup\$ Dec 18, 2015 at 15:30
7
\$\begingroup\$

Pyth, 16

XXzG.<GQJrG1.<JQ

Try it online or run a Test Suite

\$\endgroup\$
1
  • 1
    \$\begingroup\$ +1 for Star Wars input, even though I hate esolangs. \$\endgroup\$
    – Codefun64
    Dec 18, 2015 at 16:06
5
\$\begingroup\$

Bash + bsd-games package, 21

caesar $[($1+130)%26]

Builtins FTW! Almost feels like Mathematica. Pyth answers are still shorter though.

Input string read from STDIN and integer from command-line. e.g.:

$ ./caesar.sh 13 <<< "Spam spam spam sausage and spam!"
Fcnz fcnz fcnz fnhfntr naq fcnz!
$

Or if you don't like the builtin:

Bash + coreutils, 63

printf -va %s {a..z}
t=${a:$1%26}${a:0:$1%26}
tr A-Z$a ${t^^}$t
\$\endgroup\$
2
  • \$\begingroup\$ It seems to me that the coreutils version doesn't work with -127 and/or 127? \$\endgroup\$
    – Neil
    Dec 19, 2015 at 1:29
  • \$\begingroup\$ @Neil Yes. Good catch. Fixed. \$\endgroup\$ Dec 19, 2015 at 22:54
4
\$\begingroup\$

JavaScript (ES6), 122 118 114 111 bytes

alert((p=prompt)().replace(/[a-z]/gi,c=>String.fromCharCode((x=c.charCodeAt(),a=x&96,x-a+n+129)%26-~a),n=+p()))

Saved 4 bytes thanks to @Neil!

Explanation

First prompt takes the input string. The second is the number to shift each letter by.

alert(
  (p=prompt)()              // get input string
    .replace(/[a-z]/gi,c=>  // for each letter
      String.fromCharCode((
        x=c.charCodeAt(),   // x = code of character
        a=x&96,             // a = index of letter a (-1) in same capitalisation
        x-a+n+129)%26-~a    // add N to the letter code and wrap at 26
      ),                    // (+129 is needed to make the % work with negative numbers)
      n=+p()                // get number to shift by
    )
)
\$\endgroup\$
9
  • 1
    \$\begingroup\$ Very nice! But it doesn't work on all inputs; try "abcdefg", -26. This can be fixed by changing the formula to (x-a+n+130)%26. \$\endgroup\$ Dec 18, 2015 at 16:15
  • \$\begingroup\$ @ETHproductions Thanks for catching that! \$\endgroup\$
    – user81655
    Dec 18, 2015 at 21:36
  • \$\begingroup\$ "You must create a full program, not just a function or snippet" \$\endgroup\$ Dec 19, 2015 at 0:58
  • \$\begingroup\$ @LegionMammal978 Thanks, I didn't notice that. \$\endgroup\$
    – user81655
    Dec 19, 2015 at 1:13
  • \$\begingroup\$ Does a=x&96,(x-a+n+129)%26+a+1 help? \$\endgroup\$
    – Neil
    Dec 19, 2015 at 1:26
3
\$\begingroup\$

CJam, 34 22 21 20 bytes

Thanks to FryAmTheEggman for saving 1 byte.

l'[,_el^_26/l~fm<ser

Test it here.

Input is the string to be shifte on the first line and the shift on the second.

Explanation

l    e# Read the first line of input.
'[,  e# Push a string with all ASCII characters up to and including Z.
_el  e# Duplicate and convert to lower case. This only affects the letters.
^    e# Symmetric set-difference: except for the letters, each character appears in both
     e# sets and will be omitted from the difference, but all the letters will be included.
     e# This gives us "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".
_26/ e# Duplicate and split into chunks of 26 characters, separating lower and upper case.
l~   e# Read the second line of input and evaluate.
fm<  e# Shift each of the two substrings by that many characters to the left.
s    e# Convert to a single string, joining both substrings back together.
     e# On the stack are now the input, the letters in alphabetical order and the letters
     e# in shifted order.
er   e# Character transliteration: replace each occurrence of a letter with the character
     e# at the corresponding position in the shifted string.
\$\endgroup\$
3
  • \$\begingroup\$ @FryAmTheEggman The '[,_el^ is a tip from Dennis. I don't know what you mean about f though, it seems like fairly normal usage? \$\endgroup\$ Dec 18, 2015 at 14:35
  • \$\begingroup\$ I guess I just haven't read enough CJam answers :P It just seems really neat to use it as like a map but change argument order. \$\endgroup\$ Dec 18, 2015 at 14:40
  • \$\begingroup\$ @FryAmTheEggman actually, I don't need the @ at all. :) \$\endgroup\$ Dec 18, 2015 at 14:42
2
\$\begingroup\$

Java, 249 Bytes

This is as short as I could get it. Reading from stdin eats a ton of bytes. A solution using command line args is noticeably shorter but, this task specified stdin for input.

Input format is the String first followed by the shift number on a new line.

interface C{static void main(String[]a){java.util.Scanner r=new java.util.Scanner(System.in);String s=r.nextLine();int i=(r.nextInt()+26)%26;s.chars().forEach(c->System.out.print((char)(c>64&c<91|c>96&c<123?c<91?65+(c+i-65)%26:97+(c+i-97)%26:c)));}}

Using command line Arguments this solution is only 188 bytes. Input is the String as the first argument and the shift as the second.

interface C{static void main(String[]a){int i=(Integer.parseInt(a[1])+26)%26;a[0].chars().forEach(c->System.out.print((char)(c>64&c<91|c>96&c<123?c<91?65+(c+i-65)%26:97+(c+i-97)%26:c)));}}
\$\endgroup\$
1
\$\begingroup\$

R, 111 bytes

code

n=scan();s=scan(,"");for(l in as.numeric(sapply(s,charToRaw))){v=97;if(l<97)v=65;cat(intToUtf8((l+n-v)%%26+v))}

ungolfed

n <- scan()                           # input integer
s <- scan(,"")                        # input string letter by letter
z <- as.numeric(sapply(s,charToRaw))  # get ASCII index of character
for (l in z){                         # loop through chars
  v=97                                # base index of not capitalized chars
  if(l<97)v=65                        # base index of capitalized chars
  cat(intToUtf8((l+n-v)%%26+v))       # paste the char of the shifted index
}

This program takes the user input from STDIN, first the integer shifter and then the string, character by character.

\$\endgroup\$
1
\$\begingroup\$

Perl, 81 bytes

(+1 for the -p flag)

s/[^ ]+ //;$n=$&%26;eval"y/a-zA-Z/".($x=chr(97+$n)."-za-".chr$n+96).uc$x."/"if$n

Still working on golfing it down...

Test:

llama@llama:...code/perl/ppcg67044caesar$ printf '1 abcdefghijklmnopqrstuvwxyz\n13 Spam spam spam sausage and spam!\n52 abcdefghijklmnopqrstuvwxyz\n-1 abcdefghijklmnopqrstuvwxyz\n3 ABCxyz' | perl -p caesar.pl; echo
bcdefghijklmnopqrstuvwxyza
Fcnz fcnz fcnz fnhfntr naq fcnz!
abcdefghijklmnopqrstuvwxyz
zabcdefghijklmnopqrstuvwxy
DEFabc
\$\endgroup\$
1
\$\begingroup\$

Japt, 45 43 bytes

Ur"[^\\W_]"_c +V+#¶-(A=Zc <#a?#A:#a)%26+A d

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Python 2, 163 160 bytes

Not sure if I can still golf it down..

import sys;k=sys.argv
def f(x,n):r=chr((ord(x.lower())-97+n)%26+97);return(x,[r,r.upper()][x.isupper()])
print''.join(f(x,int(k[2]))[x.isalpha()] for x in k[1])

Since it's pretty unreadable, here is an ungolfed version:

import sys

def shift(x,n):
    # shift character x by n (all in lowercase)
    r = chr((ord(x.lower())-97+n)%26+97)
    if x.isalpha() and x.islower():
        return r
    elif x.isalpha() and x.isupper():
        return r.upper()
    else:
        return x

# 'map' the function shift to each character of the input   
output = ''.join(shift(x,int(sys.argv[2])) for x in sys.argv[1])
print(output)

Concerning the input: It expects two arguments, the first must be a string and the second an integer (the shift amount). Examples (file is called csr.py):

$ python csr.py gnu 9
pwd
$ python csr.py "Spam spam spam sausage and spam\!" 13
Fcnz fcnz fcnz fnhfntr naq fcnz!

Note: In the second example an escape character and "" are needed

\$\endgroup\$
1
\$\begingroup\$

Python 2, 118 116 bytes

s,n=input()
print''.join([[c,chr((ord(c)-97+n)%26+97)]['`'<c<'{'],chr((ord(c)-65+n)%26+65)]['@'<c<'[']for c in s)
\$\endgroup\$
1
  • \$\begingroup\$ You might want to use lists instead of the if/else instances (codegolf.stackexchange.com/a/62/36885 ). For example, print''.join([[c,chr((ord(c)-97+n)%26+97)]['~'<c<'{'],chr((ord(c)-65+n)%26+65)]['@'<c<'[']for c in s) is a little shorter, and should work the same. (Except change the tilde to a backtick like you had before--I couldn't get the backtick to display right.) \$\endgroup\$
    – mathmandan
    Dec 18, 2015 at 17:20
1
\$\begingroup\$

Mathematica, 117 bytes

Echo[InputString[]~StringReplace~Thread[Join[a=Alphabet[],b=ToUpperCase@a]->(c=RotateLeft)[a,d=Input[]]~Join~c[b,d]]]

Takes the string, followed by a newline, followed by the shifting factor. Might still be golfable...

\$\endgroup\$
1
\$\begingroup\$

Perl 6, 73+1 = 74 bytes

$ perl6 -pe 's:g:i/<[a..z]>/{chr ((my$o=ord ~$/)-(my$a=$o+&96+1)+BEGIN get%26)%26+$a}/' # 73+1

The first line of input is the number of characters to shift the letters up by.

Usage:

$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'1
abcdefghijklmnopqrstuvwxyz'
bcdefghijklmnopqrstuvwxyza
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'13
Spam spam spam sausage and spam!'
Fcnz fcnz fcnz fnhfntr naq fcnz!
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'52
abcdefghijklmnopqrstuvwxyz'
abcdefghijklmnopqrstuvwxyz
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'-1
abcdefghijklmnopqrstuvwxyz'
zabcdefghijklmnopqrstuvwxy
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'3
ABCxyz'
DEFabc
$ perl6 -pe 's:g:i/<[a..z]>/{...}/' <<< \
'1000000000000000000000000000000000000000
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ'
mnopqrstuvwxyzabcdefghijkl
MNOPQRSTUVWXYZABCDEFGHIJKL
\$\endgroup\$
1
\$\begingroup\$

C++, 163 154 152 bytes

#include<cstdio>
#include<cstdlib>
int main(int x,char**a){for(int c,b,s=atoi(a[1]);1+(c=getchar());putchar(c<b|c>b+26?c:(c+s-b+26)%26+b))b=c<97?65:97;}

Usage:

$ ./caesar -1 <<< "123 a A z Z aBcDeFgHiKlMnOpQrStUvWxYz"
123 z Z y Y zAbCdEfGhJkLmNoPqRsTuVwXy
\$\endgroup\$
0
\$\begingroup\$

k4, 80 bytes

The program accepts the shift number as a command-line argument and reads text from stdin.

Due to a technical constraint, negative shifts must be encoded with an underscore instead of a hyphen-minus. (Without the parser for interpreting this encoding, the solution would be 64 bytes.)

% wc -c c.k
80 c.k
% cat c.k
c:{x;,/x{y!(x_y),x#y}'.Q`a`A}
.z.pi:{1@x^c[.q.mod[.*{x^((!).$"_-")x}.z.x]26]x;}
% 

Here's the examples executed:

% echo abcdefghijklmnopqrstuvwxyz|q c.k 1
bcdefghijklmnopqrstuvwxyza
% echo 'Spam spam spam sausage and spam!'|q c.k 13
Fcnz fcnz fcnz fnhfntr naq fcnz!
% echo abcdefghijklmnopqrstuvwxyz|q c.k 52
abcdefghijklmnopqrstuvwxyz
% echo abcdefghijklmnopqrstuvwxyz|q c.k _1
zabcdefghijklmnopqrstuvwxy
% echo ABCxyz|q c.k 3
DEFabc
%

And here's a silly little test harness that verifies both encode and decode. (This is zsh; for bash or ksh, change the for loop indexing to ((i=0;i<5;i++)). One-based arrays, ugh....)

% a=(abcdefghijklmnopqrstuvwxyz 'Spam spam spam sausage and spam!' abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz ABCxyz)
% b=(1 13 52 _1 3)
% c=(bcdefghijklmnopqrstuvwxyza 'Fcnz fcnz fcnz fnhfntr naq fcnz!' abcdefghijklmnopqrstuvwxyz zabcdefghijklmnopqrstuvwxy DEFabc)
% for ((i=1;i<=5;i++))
for> do
for>     r=$(echo "${a[i]}"|q c.k "${b[i]}")
for>     s=$(echo "$r"|if [[ ${b[i]} == _* ]]; then q c.k "${b[i]/_}"; else q c.k "_${b[i]}"; fi)
for>     printf '%s\t%s\n' "$([[ ${c[i]} == $r ]] && echo good || echo bad)" "$([[ ${a[i]} == $s ]] && echo good || echo bad)"
for> done
good    good
good    good
good    good
good    good
good    good
% 
\$\endgroup\$
0
\$\begingroup\$

Stax, 2 bytes

ç'

Run and debug it

\$\endgroup\$
2
  • \$\begingroup\$ There's really no point in packing the source code. \$\endgroup\$
    – lyxal
    Mar 3, 2021 at 8:19
  • \$\begingroup\$ it gets me funny points \$\endgroup\$
    – Razetime
    Mar 3, 2021 at 8:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.