Caesar Cipher in Python
Beginner · Text and words · about 40 minutes to build · 102 lines
Encrypt a message by shifting its letters, decrypt it again, or crack a secret message without the key by trying every shift.
The program
Press Run and answer its questions in the box that appears under the code.
Open it in the full editor to save it, share it and keep building.
Sample run
What the program printed, and what was typed (in yellow).
Caesar cipher 1) Encrypt 2) Decrypt 3) Crack (try every shift) 4) Quit Choose 1-4: 1 Message to encrypt: Meet me at the park at noon Shift (1-25): 3 Encrypted: Phhw ph dw wkh sdun dw qrrq 1) Encrypt 2) Decrypt 3) Crack (try every shift) 4) Quit Choose 1-4: 2 Message to decrypt: Wkh fdvh lv forvhg Shift (1-25): three The shift must be a whole number, like 3. Shift (1-25): 3 Decrypted: The case is closed 1) Encrypt 2) Decrypt 3) Crack (try every shift) 4) Quit Choose 1-4: 3 Secret message: Dro dbokcebo sc exnob dro yvn yku dboo 1: Cqn canjbdan rb dwmna cqn xum xjt cann 2: Bpm bzmiaczm qa cvlmz bpm wtl wis bzmm 3: Aol aylhzbyl pz bukly aol vsk vhr ayll 4: Znk zxkgyaxk oy atjkx znk urj ugq zxkk 5: Ymj ywjfxzwj nx zsijw ymj tqi tfp ywjj 6: Xli xviewyvi mw yrhiv xli sph seo xvii 7: Wkh wuhdvxuh lv xqghu wkh rog rdn wuhh 8: Vjg vtgcuwtg ku wpfgt vjg qnf qcm vtgg 9: Uif usfbtvsf jt voefs uif pme pbl usff 10: The treasure is under the old oak tree 11: Sgd sqdzrtqd hr tmcdq sgd nkc nzj sqdd 12: Rfc rpcyqspc gq slbcp rfc mjb myi rpcc 13: Qeb qobxprob fp rkabo qeb lia lxh qobb 14: Pda pnawoqna eo qjzan pda khz kwg pnaa 15: Ocz omzvnpmz dn piyzm ocz jgy jvf omzz 16: Nby nlyumoly cm ohxyl nby ifx iue nlyy 17: Max mkxtlnkx bl ngwxk max hew htd mkxx 18: Lzw ljwskmjw ak mfvwj lzw gdv gsc ljww 19: Kyv kivrjliv zj leuvi kyv fcu frb kivv 20: Jxu jhuqikhu yi kdtuh jxu ebt eqa jhuu 21: Iwt igtphjgt xh jcstg iwt das dpz igtt 22: Hvs hfsogifs wg ibrsf hvs czr coy hfss 23: Gur gernfher vf haqre gur byq bnx gerr 24: Ftq fdqmegdq ue gzpqd ftq axp amw fdqq 25: Esp ecpldfcp td fyopc esp zwo zlv ecpp Most likely shift: 10, which gives: The treasure is under the old oak tree 1) Encrypt 2) Decrypt 3) Crack (try every shift) 4) Quit Choose 1-4: 4 Goodbye.
How it is built
1. Shifting one letter
ord() turns a letter into a number and chr() turns a number back into a letter. Subtracting the code of A (or a) gives a position from 0 to 25, and % ALPHABET_SIZE wraps Z round to A. Anything that is not a letter comes back unchanged.
def shift_letter(letter, shift): """Move one letter along the alphabet, wrapping from Z back to A.""" if letter.isupper(): start = ord("A") elif letter.islower(): start = ord("a") else: return letter # spaces, digits and punctuation stay as they are position = ord(letter) - start return chr(start + (position + shift) % ALPHABET_SIZE)
2. The whole message
Encrypting and decrypting use the same function: decrypting shifts by the negative amount. "".join() glues the shifted letters back into one string.
def caesar(text, shift): """Shift every letter in the text by the same amount.""" return "".join(shift_letter(letter, shift) for letter in text)
3. Telling English from nonsense
To crack a message without the key, the program needs a way to recognise English. It counts the words found in COMMON_WORDS, after stripping punctuation from each word.
def english_score(text): """Count how many words of the text are common English words.""" words = text.lower().split() return sum(1 for word in words if word.strip(".,;:?'\"") in COMMON_WORDS)
4. Trying every shift
A Caesar cipher has only 25 possible keys, so the program tries them all, prints each attempt and keeps the shift with the best score. This is a brute-force attack, and it is why the Caesar cipher cannot protect real secrets.
def brute_force(text): """Try all 25 shifts, print each, return the most English one (or None).""" best_shift, best_score = None, 0 for shift in range(1, ALPHABET_SIZE): attempt = caesar(text, -shift) print(f" {shift:>2}: {attempt}") score = english_score(attempt) if score > best_score: best_shift, best_score = shift, score return best_shift
5. Asking for a shift
int() raises ValueError for text like three, so the except block prints a hint and the loop asks again. The range check turns away 0 and 26, which would leave the message unchanged.
def ask_shift(): """Ask for a shift from 1 to 25 until the answer is valid.""" while True: answer = input("Shift (1-25): ").strip() try: shift = int(answer) except ValueError: print("The shift must be a whole number, like 3.") continue if 1 <= shift < ALPHABET_SIZE: return shift print("Pick a number from 1 to 25.")
6. The menu
A while True loop shows the menu and runs the chosen action. Option 4 uses break to leave the loop, which ends the program.
def main(): """Show the menu and run the chosen action until the user quits.""" print("Caesar cipher") while True: print("\n1) Encrypt 2) Decrypt 3) Crack (try every shift) 4) Quit") choice = input("Choose 1-4: ").strip() if choice == "1": text = ask_text("Message to encrypt: ") print("Encrypted:", caesar(text, ask_shift())) elif choice == "2": text = ask_text("Message to decrypt: ") print("Decrypted:", caesar(text, -ask_shift())) elif choice == "3": text = ask_text("Secret message: ") best = brute_force(text) if best is None: print("No shift gave common English words. Read the list.") else: print(f"Most likely shift: {best}, which gives:") print(" " + caesar(text, -best)) elif choice == "4": print("Goodbye.") break else: print("Please type 1, 2, 3 or 4.")
Make it bigger
- Add a Vigenere cipher, which uses a keyword so each letter gets its own shift.
- Count letter frequencies and guess the shift from the most common letter, which is usually e in English.
- Show the three best guesses from the cracker instead of one.
- Read the message from a text file and save the encrypted version to a new file.
What it practises
- ord() and chr()
- % for wrapping around
- joining strings
- try/except ValueError
- a menu loop