L3 - StackingFlowing/CTProgramming GitHub Wiki

You can follow the below instructions and start writing codes.

If you get lost and does not know how to proceed, check the final code at the end of each section.

3.1 The Caesar cipher... from a computer's point of view

Variables

dictionary = "abcdefghijklmnopqrstuvwxyz"

First, we set the dictionary as 26 alphabet (a to z). We will need them later.

key = int(input("Enter your key: "))
message = input("Enter your message: ")

We now ask for user inputs. These include the number of the offset, and the message to be encrypted.

Remember to use int() to convert a string into integer! If you are unsure why int() is needed, learn more about it here.

newMessage = ""

Finally, we create a new variable newMessage so we can store the encrypted message later.

For loop

for character in message:
    position = dictionary.find(character)

This section means "for each character in the user-input message, it fill find its position in the dictionary (abcdefghijklmnopqrstuvwxyz)". THe position is another varuable that we have created just now.

Example: if the character is a I should expect the value of position now set to 0.

    newPosition = (position + key)
    newMessage += dictionary[newPosition]

The dictionary[newPosition] corresponds to a character in abcdefghijklmnopqrstuvwxyz, and the character will be added to newMessage. The newPosition is simply the position of the character + the key.

Now the new message after encrypting will become the message that all characters had changed to a new position, which means that there will be a new character that repreesents the origanal character.

Print

The last step: Print out the encrypted newMessage. And we are done!

print()  # This line prints an empty line in the console
print(newMessage)

Result

After writing all the code down, you should get something like this:

# VARIABLES
dictionary = "abcdefghijklmnopqrstuvwxyz"
key = int(input("Enter your key: "))
message = input("Enter your message: ")
newMessage = ""

# FOR LOOP
for character in message:
    position = dictionary.find(character)
    newPosition = (position + key)
    newMessage += dictionary[newPosition]

# PRINT
print()  # This line prints an empty line in the console
print(newMessage)

Remember to check if your code works. Try it with your friends!

3.2 Decryption

What if the user wants to choose between encryption and decryption?

We will have to ask the user to make the choice.

In your variables section (it is usually the first block of code), add the following line:

encrypting = input("Would you like to encrypt or decrypt? ( e / d ) ")

This variable will store the user's choice. Now, we need to process the user's choice.

We know that encryption is just position of the character + key of offset. Decryption is the reverse: we do position - key.

So, look for this line of code in your program:

    newPosition = (position + key)

Delete it, and do the following:

if the user chooses to encrypt, we will just use addition.

    if encrypting == "e":
        newPosition = (position + key)

elif the user chooses to decrypt, we will use subtraction.

    elif encrypting == "d":
        newPosition = (position - key)

Result

# VARIABLES
dictionary = "abcdefghijklmnopqrstuvwxyz"
encrypting = input("Would you like to encrypt or decrypt? ( e / d ) ")  # We added this
key = int(input("Enter your key: "))
message = input("Enter your message: ")
newMessage = ""

# FOR LOOP
for character in message:
    position = dictionary.find(character)
    if encrypting == "e":                   # We added this
        newPosition = (position + key)      #   ...and this
    elif encrypting == "d":                 #   ...and this
        newPosition = (position - key)      #   ...and this.
    newMessage += dictionary[newPosition]

# PRINT
print()
print(newMessage)

Remember to check if your program works!

3.3 Debugging

We will need to test our program!

When we test, we should think of all possible inputs our user may use. For example:

  • Is there any invalid message that a user can input?
  • Is there any invalid key that a user can input?
  • What if the user gives the wrong answer to Would you like to encrypt or decrypt? ( e / d )?

We should try to break our program to ensure it is absolutely perfect.

Scenario 1: A very large number for key

Run your program, and set the value of key to 100.

> Enter your key: 100

We get an error, and that is not good!

Even if the user wrote a very large number, we could still process this information in real life.

We just choose a letter and count to 100 from that letter. Let's choose a as our letter.

  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
  ^  1  2  3  4...                                              ...22 23 24 25

When we count to 25 we get z. What is the 26th letter?

Since we ran out of alphabet, we can only go back to the letter we started with!

  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
 26 27 28...                                                    ...48 49 50 51

If you have the patience to count to 100, you will notice that every now and then we will have to go back and count from the start.

If you are too lazy to count, here are the rest of the loops for you to see.

  a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100...

So, if the offset is 100 and the letter is a, we know that it will be converted to w.

This is the same as using an offset of 22, or an offset of 48, or an offset of 74. Do you see a pattern?

The position of a is 0, because we start counting from it. But it is also the 26th, 52nd and 78th letter because we start counting the alphabet from the start. And there are 26 alphabet! Do you see a pattern?

The position of b is 1, but also 27, 53 and 79. We can write these as 26+1, 52+1 and 78+1.

We can summarise this property using division.

27 / 26 = 1 remainder 1
53 / 26 = 2 remainder 1
79 / 26 = 3 remainder 1
...

Although the size of the numbers are different, they share the same remainder 1... which is also the position of b in the alphabet!

We can use this feature in Python to deal with large numbers, because we have the special symbol % to deal with remainder.

Challenge 1

Fix your program so it can deal with large numbers for key. All you need to do is to add % somewhere.

Hint: you should only need to change these two lines:

newPosition = (position + key)

and

newPosition = (position - key)

If you need help, click me for more information about % (modulo).

Scenario 2: Invalid message

For your message, write a sentence with capital letters and symbols, like: Merry Christmas!

Encrypt it with any key you like. If we use a key of 1 we get afsszaaisjtunbta.

Now decrypt it with the same key. What do you notice?

> zerryzzhristmasz

When we do the encryption, our program will use dictionary.find() at some point. This function only recognises lower-case alphabet because we specified all 26 of them in dictionary.

When it sees an unfamiliar letter (like M or ! in our message) it will just return -1... which means the program will try to find() the last alphabet in our dictionary! That explains why the decrypted message is filled with meaningless z's.

We can fix this by adding more characters (letters and symbols) to our dictionary, but that will make it longer and difficult to remember.

Instead, we can just let our program "ignore" such characters and add it to the encrypted message.

Challenge 2

Right now, in the for loop, our program will find the corresponding alphabet to a character, and add that to newMessage:

newMessage += dictionary[newPosition]

But if a character will cause find() to return -1, we should just add that character to newMessage.

else, the program should still add the encrypted letter to the message.

Hint: you will only add a minimum of 3 new lines. Remember indentation for an if statement!

Scenario 3: Bad choice

When we ask Would you like to encrypt or decrypt? ( e / d ) , what if the user enters neither e nor d?

If such thing happens, our program will not run properly.

We will have to guide our user to strictly enter either e or d if they enter something else. How do we do it?


# add this under your VARIABLES block
while encrypting != "e" and encrypting != "d":
    encrypting = input("Would you like to encrypt or decrypt? ( e / d ) ")

Challenge 3

Figure out what these 2 lines mean. That's it!

They may look easy to understand, but what do while, != and and actually suggest?

Ask your peers or search online. Or use your intuition.