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()