pythonpython-3.xencryptioncaesar-cipher

Beginner working on a Caesar Cipher and running into not-whole-input-specific problems


I'm working on a Caesar Cipher, but running into big issues with the encryption portion!

My code:

direction = input("Type 'encode' to encrypt, type 'decode' to decrypt:\n")
text = input("Type your message:\n").lower()
shift = int(input("Type the shift number:\n"))

message = []
def caesar(text, shift):
  message = list(text)
  if shift > 26:
    shift = shift % 26
  for i in message:
    if i in alphabet:
      position = message.index(i)
      location = alphabet.index(i)
      if direction == "decode":
        shift = shift * -1
      newlet = alphabet[location + shift] 
      message.insert(position,newlet)
      message.remove(i)
      print(message)

The teacher's correct code:

direction = input("Type 'encode' to encrypt, type 'decode' to decrypt:\n")
text = input("Type your message:\n").lower()
shift = int(input("Type the shift number:\n"))

def caesar(text, shift):
  end_text = ""
  if direction == "decode":
    shift *= -1
  for char in text:
    if char in alphabet:
      position = alphabet.index(char)
      new_position = position + shift
      end_text += alphabet[new_position]
    else:
      end_text += char
  print(f"Here's the {direction}d result: {end_text}")

For example, when the teacher's text input is: "Taylor Swift" with a shift input of 4, it comes out as "xecpsv wamjx" but mine comes out as "xecpav swmjx".

I've tried switching out various things, but I'd just like to understand what's going wrong and why the glitches are so sporadic- sometimes it works just fine for shorter inputs like "two one" but not longer ones like "civilization" or the other way around. Very confusing!

I'll add below an example of the program running output: I made it print the position (the individual numbers) and the message (list) every loop run so I could see what was going on - the position is messed up, and so is the message.

Type 'encode' to encrypt, type 'decode' to decrypt:
encode
Type your message:
taylor swift
Type the shift number:
4
0
['x', 'a', 'y', 'l', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
1
['x', 'e', 'y', 'l', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
2
['x', 'e', 'c', 'l', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
3
['x', 'e', 'c', 'p', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
4
['x', 'e', 'c', 'p', 's', 'r', ' ', 's', 'w', 'i', 'f', 't']
5
['x', 'e', 'c', 'p', 's', 'v', ' ', 's', 'w', 'i', 'f', 't']
4
['x', 'e', 'c', 'p', 'w', 'v', ' ', 's', 'w', 'i', 'f', 't']
4
['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'i', 'f', 't']
9
['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'm', 'f', 't']
10
['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'm', 'j', 't']
11
['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'm', 'j', 'x']
The encoded text is xecpav swmjx

Solution

  • Testing out your algorithms as well as the sample of the teacher's code created issues in running a test with the sample string as both blocks of sample code did not include the value of the "alphabet" variable.

    Traceback (most recent call last):
      File "/home/craig/Python_Programs/Caesar/CaesarOld.py", line 24, in <module>
        caesar(text, shift)
      File "/home/craig/Python_Programs/Caesar/CaesarOld.py", line 14, in caesar
        newlet = alphabet[location + shift] 
    IndexError: string index out of range
    

    Using a variable named "alphabet" with a value of "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" provided a work-around to this issue. And with that, I also received the same output you noted. Referring to the good comments, what is tripping you up is using the list indexing functionality in the "message" list as this list is continually being modified. To illustrate this issue, following is code with a refactored version of your function, adding a "print" statement to denote the unexpected behavior.

    alphabet = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"   # Educated guess of this string
    
    message = []
    def caesar(text, shift):
      message = list(text)
      if shift > 26:
        shift = shift % 26
      for i in message:
        if i in alphabet:
          position = message.index(i)
          location = alphabet.index(i)
          if direction == "decode":
            shift = shift * -1
          newlet = alphabet[location + shift] 
          print("First occurance of", i, " is", position, " and now is", newlet) # Note value changes
          message.insert(position,newlet)
          message.remove(i)
          print(message)
    
    direction = input("Type 'encode' to encrypt, type 'decode' to decrypt: ")
    text = input("Type your message: ").lower()
    shift = int(input("Type the shift number: "))
    
    caesar(text, shift)
    

    This provides output of how characters in the list are identified and modified.

    craig@Vera:~/Python_Programs/Caesar$ python3 CaesarOld.py 
    Type 'encode' to encrypt, type 'decode' to decrypt: encode
    Type your message: Taylor Swift
    Type the shift number: 4
    First occurance of t  is 0  and now is x
    ['x', 'a', 'y', 'l', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of a  is 1  and now is e
    ['x', 'e', 'y', 'l', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of y  is 2  and now is c
    ['x', 'e', 'c', 'l', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of l  is 3  and now is p
    ['x', 'e', 'c', 'p', 'o', 'r', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of o  is 4  and now is s                        <--------- change of 'o' to 's'
    ['x', 'e', 'c', 'p', 's', 'r', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of r  is 5  and now is v
    ['x', 'e', 'c', 'p', 's', 'v', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of s  is 4  and now is w                        <--------- now the first 's' is being changed to 'w'
    ['x', 'e', 'c', 'p', 'w', 'v', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of w  is 4  and now is a                        <--------- now that 'w' is being changed to 'a'
    ['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'i', 'f', 't']
    First occurance of i  is 9  and now is m
    ['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'm', 'f', 't']
    First occurance of f  is 10  and now is j
    ['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'm', 'j', 't']
    First occurance of t  is 11  and now is x
    ['x', 'e', 'c', 'p', 'a', 'v', ' ', 's', 'w', 'm', 'j', 'x']
    craig@Vera:~/Python_Programs/Caesar$ 
    

    As noted, when the character 'o' in "Taylor" is identified and the shift occurs, the character is changed to the character 's'. Later, when the character 's' in "Swift" is encountered, the list is searched for the first occurrence of that character, which is now the fifth character, and so that character is changed via the shift value to the character 'w'. And, the 's' in "Swift" is not changed. Subsequently, the next character processed is the 'w' character in "Swift". Again, using the list indexing functionality, the first occurrence of that character is the fifth character and is subsequently shifted/changed to the character 'a'. And again the actual character is not changed. That, ultimately is the reason for the output received.

    Using the teacher's function, here is the refactored program with some comments added for explanation.

    alphabet = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"             # Educated guess of alphabetical string
    
    def caesar(text, shift):
      end_text = ""
      if direction == "decode":
        shift *= -1
      for char in text:
        if char in alphabet:
          position = alphabet.index(char)       # Finds alphabetical position of the text character
          new_position = position + shift       # Stores the shifted character in a new work variable
          end_text += alphabet[new_position]    # Appends the new character in the text
        else:
          end_text += char
      print(f"Here's the {direction}d result: {end_text}")
    
    direction = input("Type 'encode' to encrypt, type 'decode' to decrypt: ")
    text = input("Type your message: ").lower()
    shift = int(input("Type the shift number: "))
    
    caesar(text, shift)
    

    Following are some highlights.

    Once all text characters have been processed, the resulting encoded/decoded text is printed. Following is a test of both encoding and decoding text.

    craig@Vera:~/Python_Programs/Caesar$ python3 Caesar.py
    Type 'encode' to encrypt, type 'decode' to decrypt: encode
    Type your message: Taylor Swift
    Type the shift number: 4
    Here's the encoded result: xecpsv wamjx
    craig@Vera:~/Python_Programs/Caesar$ python3 Caesar.py
    Type 'encode' to encrypt, type 'decode' to decrypt: decode
    Type your message: xecpsv wamjx
    Type the shift number: 4
    Here's the decoded result: taylor swift
    

    The main bits to take away from this is to probably delve some more into Python tutorial literature, especially as it pertains using the index functions for strings and for lists. And, be careful in reusing variables, lists, and so forth.