3

I'm trying to create a Python function that uses the Caesar cipher to encrypt a message.

So far, the code I have is

letter = input("Enter a letter: ")
def alphabet_position(letter):
alphabet_pos = {'A':0, 'a':0, 'B':1, 'b':1, 'C':2, 'c':2, 'D':3,
                'd':3, 'E':4, 'e':4, 'F':5, 'f':5, 'G':6, 'g':6,
                'H':7, 'h':7, 'I':8, 'i':8, 'J':9, 'j':9, 'K':10,
                'k':10, 'L':11, 'l':11, 'M':12, 'm':12, 'N': 13,
                'n':13, 'O':14, 'o':14, 'P':15, 'p':15, 'Q':16,
                'q':16, 'R':17, 'r':17, 'S':18, 's':18, 'T':19,
                't':19, 'U':20, 'u':20, 'V':21, 'v':21, 'W':22,
                'w':22, 'X':23, 'x':23, 'Y':24, 'y':24, 'Z':25, 'z':25 }
pos = alphabet_pos[letter]
return pos  

When I try to run my code, it will ask for the letter but it doesn't return anything after that

Please help if you have any suggestions.

  • 1
    Fix your indentation, currently your code has an `IndentationError` – EdChum Aug 30 '18 at 14:58
  • 1
    The other problem is that all you did was define a function, you never actually call it like `alphabet_position(letter)` – EdChum Aug 30 '18 at 15:00
  • Possible duplicate of [Short rot13 function - Python](https://stackoverflow.com/questions/3269686/short-rot13-function-python) – roganjosh Aug 30 '18 at 15:01
  • @Cut7er the OP will access the value of the dict by key, they are not trying to access by index – EdChum Aug 30 '18 at 15:02
  • Possible duplicate of [Function That Receives and Rotates Character - Caesar Cipher](https://stackoverflow.com/questions/41009009/function-that-receives-and-rotates-character-caesar-cipher) – Sianur Aug 30 '18 at 15:06
  • `>>> codecs.encode('ab no', 'rot_13')` → `'no ab'` – Klaus D. Aug 30 '18 at 15:10

5 Answers5

0

you would need to access your dictionary in a different way:

pos = alphabet_pos.get(letter)
return pos

and then you can finally call the function.

alphabet_position(letter)
Cut7er
  • 1,209
  • 9
  • 24
0

You can define two dictionaries, one the reverse of the other. You need to be careful on a few aspects:

  • Whether case is important. If it's not, use str.casefold as below.
  • What happens when you roll off the end of the alphabet, e.g. 13th letter after "z". Below we assume you start from the beginning again.
  • Don't type out the alphabet manually. You can use the string module.

Here's a demo:

letter = input("Enter a letter: ")

from string import ascii_lowercase

def get_next(letter, n):
    pos_alpha = dict(enumerate(ascii_lowercase))
    alpha_pos = {v: k for k, v in pos_alpha.items()}
    return pos_alpha[alpha_pos[letter.casefold()] + n % 26]

get_next(letter, 13)

Enter a letter: a
'n'
jpp
  • 159,742
  • 34
  • 281
  • 339
0

If you need a entirely new encoded dict

import string
import numpy as np, random
letters = string.ascii_uppercase
d=dict(zip(list(letters),range(0,len(letters))))
encoded_dic={}

def get_caesar_value(v, by=13):
    return(v+by)%26

for k,v in d.items():
    encoded_dic[k]=chr(65+get_caesar_value(v))
print(encoded_dic)

Output:

{'A': 'N', 'C': 'P', 'B': 'O', 'E': 'R', 'D': 'Q', 'G': 'T', 'F': 'S', 'I': 'V', 'H': 'U', 'K': 'X', 'J': 'W', 'M': 'Z', 'L': 'Y', 'O': 'B', 'N': 'A', 'Q': 'D', 'P': 'C', 'S': 'F', 'R': 'E', 'U': 'H', 'T': 'G', 'W': 'J', 'V': 'I', 'Y': 'L', 'X': 'K', 'Z': 'M'}
mad_
  • 8,121
  • 2
  • 25
  • 40
0

The code you have only maps letters to a position. We'll rewrite it and make a rotate function.

Code

import string
import itertools as it


LOOKUP = {
    **{x:i for i, x in enumerate(string.ascii_lowercase)},
    **{x:i for i, x in enumerate(string.ascii_uppercase)}
}


def abc_position(letter):
    """Return the alpha position of a letter."""
    return LOOKUP[letter]

def rotate(letter, shift=13):
    """Return a letter shifted some positions to the right; recycle at the end."""
    iterable = it.cycle(string.ascii_lowercase)
    start = it.dropwhile(lambda x: x != letter.casefold(), iterable)

    # Advance the iterator
    for i, x in zip(range(shift+1), start):
        res = x
    if letter.isupper():
        return res.upper()
    return res

Tests

func = abc_position
assert func("a") == 0
assert func("A") == 0
assert func("c") == 2
assert func("z") == 25

func = rotate
assert func("h") == "u"
assert func("a", 0) == "a"
assert func("A", 0) == "A"
assert func("a", 2) == "c"
assert func("c", 3) == "f"
assert func("A", 2) == "C"
assert func("a", 26) == "a"
# Restart after "z"
assert func("z", 1) == "a"
assert func("Z", 1) == "A"

Demo

>>> letter = input("Enter a letter: ")
Enter a letter: h
>>> rot = rotate(letter, 13)
>>> rot
'u'
>>> abc_position(rot)
20

Here we rotated the letter "h" 13 positions, got a letter and then determined the position of this resultant letter in the normal string of abc's.


Details

abc_position()

This function was rewritten to lookup the position of a letter. It merges two dictionaries:

  1. one that enumerates a lowercase ascii letters
  2. one that enumerates a uppercase ascii letters

The string module has this letters already.

rotate()

This function only rotates lowercase letters; uppercase letters are translated from the lowercase position. The string of letters is rotated by making an infinite cycle (an iterator) of lowercase letters.

  1. The cycle is first advanced to start at the desired letter. This is done by dropping all letters that don't look like the one passed in.
  2. Then it is advanced in a loop some number of times equal to shift. The loop is just one way to consume or move the iterator ahead. We only care about the last letter, not the ones in between. This letter is returned, either lower or uppercase.

Since a letter is returned (not a position), you can now use your abc_position() function to find it's normal position.

Alternatives

Other rotation functions can substitute rotate():

import codecs


def rot13(letter):
    return codecs.encode(letter, "rot13")

def rot13(letter):
    table = str.maketrans( 
        "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", 
        "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm")
    return str.translate(letter, table)

However, these options are constrained to rot13, while rotate() can be shifted by any number. Note: rot26 will cycle back to the beginning, e.g. rotate("a", 26) -> a.

See also this post on how to make true rot13 cipher.

See also docs on itertools.cycle and itertools.dropwhile.

pylang
  • 40,867
  • 14
  • 129
  • 121
-2

You can do it with quick calculations from ord and chr functions instead:

def encrypt(letter):
    return chr((ord(letter.lower()) - ord('a') + 13) % 26 + ord('a'))

so that:

print(encrypt('a'))
print(encrypt('o'))

outputs:

n
b
blhsing
  • 91,368
  • 6
  • 71
  • 106