

Notice that the top line is simply the standard English alphabet. The second line is the "key" for the encryption because it tells you how to transform each letter. For this assignment, the alphabet and the key should both be strings of length 26, containing only lowercase letters. When encrypting a text (the "plaintext"), you take each letter in the text, find it in the top line, note the corresponding letter in the key, and write that down. The resulting text is the "ciphertext." Nonletters are not changed.
When decrypting, you simply reverse the roles of the two lines. I.e., for each letter in the ciphertext, find it's position in the key, note the corresponding letter in the alphabet, and write that down. Voila! You get the plaintext back.
For this all to work, the key must be a permutation of the alphabet. That is, it should have exactly the same letters as the alphabet, but in scrambled order. By the way, your code must work for any legal key, not just the ones in the pictures above.
Please note that this project is mainly about classes and strings. Do not turn strings into lists and manipulate them as lists.
import random
# A global constant defining the alphabet.
LCLETTERS = "abcdefghijklmnopqrstuvwxyz"
# You are welcome to use the following two auxiliary functions, or
# define your own. You don't need to understand this code at this
# point in the semester.
def isLegalKey( key ):
# A key is legal if it has length 26 and contains all letters.
# from LCLETTERS.
return ( len(key) == 26 and all( [ ch in key for ch in LCLETTERS ] ) )
def makeRandomKey():
# A legal random key is a permutation of LCLETTERS.
lst = list( LCLETTERS ) # Turn string into list of letters
random.shuffle( lst ) # Shuffle the list randomly
return ''.join( lst ) # Assemble them back into a string
# There may be some additional auxiliary functions defined here.
# I had several others, mainly used in encrypt and decrypt.
class SubstitutionCipher:
def __init__ (self, key = None ):
"""Create an instance of the cipher with stored key, which
defaults to random."""
if key == None:
self.__key = makeRandomKey()
# otherwise, a key was passed in
...
# Note that these are the required methods, but you may define
# additional methods if you need them. (I didn't need any.)
def getKey( self ):
"""Getter for the stored key."""
def setKey( self, newKey ):
"""Setter for the stored key. Check that it's a legal
key."""
def encryptText( self, plaintext ):
"""Return the plaintext encrypted with respect to the stored key."""
def decryptText( self, ciphertext ):
"""Return the ciphertext decrypted with respect to the stored
key."""
def main():
""" This implements the top level command loop. It
creates an instance of the SubstitutionCipher class and allows the user
to invoke within a loop the following commands: help, getKey, changeKey,
encrypt, decrypt, quit."""
main()
Note: I updated the code above from an earlier version where I had
the __init__ method as:
def __init__ (self, key = makeRandomKey() ):
That probably works for this project, but it's not ideal. The issue
is that the default value is computed when the method is compiled.
That means that makeRandomKey is run once at compile time,
not each time __init__ runs. So if you happen to create
multiple instances of the class, they'd all have the same initial
randomly defined key. In this project, you're probably only creating
one instance of the class, so it probably wouldn't matter, but it's
not good programming. Thanks to Sam for pointing out this issue.
> python Project2.py
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): hELp
The following commands are available:
help: show this message;
getkey: display the current key;
changekey: the user may provide a new legal key or enter
random to generate a key randomly, or quit to end this
command and leave the key unchanged;
encrypt: receive a text from the user, encrypt it using the
current key, and print the result;
decrypt: receive a text from the user, decrypt it using the
current key, and print the result;
quit: exit the command loop;
anything else is an error.
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): getKEY
Current cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): changekey
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: ljljljljljljl
Illegal key entered. Try again!
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: RANdom
New cipher key: rpqusjznvetfdiybclaomgwxkh
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): getKEY
Current cipher key: rpqusjznvetfdiybclaomgwxkh
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): chaNGEkey
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: bapldjgktyisxrnuvwomqhzecf
New cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): getkey
Current cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): ChANGekey
Enter a valid cipher key, 'random' for a random key, or 'quit' to quit: QUIT
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): getkey
Current cipher key: bapldjgktyisxrnuvwomqhzecf
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): enCrypt
Enter a text to encrypt: Now is the time for all good folks to come to the aid of their country!
The encrypted text is: Rnz to mkd mtxd jnw bss gnnl jnsio mn pnxd mn mkd btl nj mkdtw pnqrmwc!
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): deCRYpt
Enter a text to decrypt: Rnz to mkd mtxd jnw bss gnnl jnsio mn pnxd mn mkd btl nj mkdtw pnqrmwc!
The decrypted text is: Now is the time for all good folks to come to the aid of their country!
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): argleBargle
Command not recognized. Try again!
Enter a command (help, getKey, changeKey, encrypt, decrypt, quit): quit
Thanks for visiting!
Note, that if you comment out the call to main(), you should also be able to use your SubstitutionCipher class directly. It might be a good idea to get that running before you build your top level driver routine. Here's what that would look like:
> python
>>> from Project2 import *
>>> cipher = SubstitutionCipher()
>>> cipher.getKey()
'cmaukidjtvlnpgbehowxzryfqs'
>>> text = "Now is the time for all good men to come to the aid of their country."
>>> text
'Now is the time for all good men to come to the aid of their country.'
>>> cipher.encryptText( text )
'Gby tw xjk xtpk ibo cnn dbbu pkg xb abpk xb xjk ctu bi xjkto abzgxoq.'
>>> ciphertext = cipher.encryptText( text )
>>> ciphertext
'Gby tw xjk xtpk ibo cnn dbbu pkg xb abpk xb xjk ctu bi xjkto abzgxoq.'
>>> cipher.decryptText( ciphertext )
'Now is the time for all good men to come to the aid of their country.'
>>> key = makeRandomKey()
>>> key
'cglnypqdsaxjvrwfmhbtkeuoiz'
>>> isLegalKey( key )
True
>>> cipher2 = SubstitutionCipher("abcdfgh")
Key entered is not legal
>>> cipher2 = SubstitutionCipher( key )
>>> cipher2.getKey()
'cglnypqdsaxjvrwfmhbtkeuoiz'
>>> cipher2.encryptText("ABCdef")
'CGLnyp'
>>> cipher2.decryptText("CGLnyp")
'ABCdef'
>>>
Your file must compile and run before submission. It must also contain a header with the following format:
# File: Project2.py # Student: # UT EID: # Course Name: CS303E # # Date Created: # Description of Program:
isLegalKey( key ): # supplied for you
makeRandomKey(): # supplied for you
keyIndex( ch, key ): # return index of ch in the key,
# if it's there
convertLCCharacter( ch, key1, key2 ): # convert lowercase character
convertUCCharacter( ch, key1, key2 ): # convert uppercase character
convertText( text, key1, key2 ): # put it together to convert
# a text
Add robustness: I always find it a good idea to make any system I code as robust and user-friendly as possible. For example, I find it annoying when using a system to have to worry about the case of input commands. Why not make commands case insensitive so that "EnCRypt" works as well as "encrypt"? It's easy to do that; just lowercase the command entered by the user (using comm.lower()) and then compare that to the lowercase version. If they match, accept the command; otherwise, reject it.
Remember: if the command is stored in variable comm, comm.lower() doesn't change comm. You'd want to do something like:
commLower = comm.lower()if you need to preserve comm, to print an error message, for example. If you don't need to preserve comm, you can just do this:
comm = comm.lower()