Caesar Cipher not rotating letters properly? (Python)

844 views Asked by At

I have attempted to make a Caesar Cipher for my first ever Python project at school. I kind of copied out code from a youtube video for the main cipher segment but when I encrypt the message a user types, it does a random cipher instead of a key that he inputs into the shell. This is the code:

abc = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

def main():
    message = input("What's the message to encrypt/decrypt? ")
    key = int(input("What number would you like for your key value? "))
    choice = input("Choose: encrypt or decrypt. ")
    if choice == "encrypt":
        encrypt(message, key)
    elif choice == "decrypt":
        encrypt(message, key * (-1))
    else:
        print("Bad answer, try again.")

def encrypt(message, key):
    cipherText = ""
    for letter in message:
        if letter in abc:
            newPosition = (abc.find(letter) + key) % 26
            cipherText += abc[newPosition]
        else:
            cipherText += letter
    print(cipherText)
    return cipherText

main()

Could someone help me solve this issue please. Also please don't make it very complex since I am a beginner at Python and I don't know very much at all.

THANK YOU!

4

There are 4 answers

1
glibdud On BEST ANSWER

The problem is that you're intertwining upper and lower case letters in your character set. So when you attempt to replace a character with a character, say, 5 characters ahead, you're actually flipping the case and moving 2-3 characters ahead (depending on what case you started with). There are better ways of accomplishing this, but it turns out one simple change can make your code work as expected:

newPosition = (abc.find(letter) + key * 2) % 52

If you double the key when you find your replacement character, then you'll be skipping over both the upper and lower case letters in the character set. And since your doubled key will always be even, you'll end up with the same case you started with. You'll also need to change the modulo correspondingly to 52 as pointed out by R.Sharp.

1
Andrew Graham-Yooll On
abc = 'AaBbCcDdEeFfGgHhIiJjKKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'

def main():
    message = input("What's the message to encrypt/decrypt? ")
    key = int(input("What number would you like for your key value? "))
    choice = input("Choose: encrypt or decrypt. ")
    if choice == "encrypt":
        encrypt(message, key)
    elif choice == "decrypt":
        encrypt(message, key * (-1))
    else:
        print("Bad answer, try again.")

def encrypt(message, key):
    cipherText = ""
    for letter in message:
        if letter in abc:
            newPosition = (abc.find(letter) + key) % 26
            cipherText += abc[newPosition]
        else:
            cipherText += letter
    print(cipherText)
    return cipherText

main()

Missing the message, key arguments in encrypt

1
R.Sharp On

whilst I agree with @glibdud, there is another error. You're taking modulo 26 on value of key + the position in abc. but abc is 52 characters long - so to be able to decrypt what you encrypt, you need to change that to newPosition = (abc.find(letter) + key) % 52

If you want to make the encryption string more arbitrary, say to include some punctuation or numeral characters, replace 26, or 52 with the calculated length of the encryption string.

1
Balaji S On
(abc.find(letter) + key) % 26

since abc has both uppercase and lower case mixed. a key (ex :2) applied to a character 'C' will result in 'D' instead of 'E'.