Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # 6.00 Problem Set 4
- #
- # Caesar Cipher Skeleton
- #
- import string
- import random
- WORDLIST_FILENAME = "words.txt"
- # -----------------------------------
- # Helper code
- # (you don't need to understand this helper code)
- def load_words():
- """
- Returns a list of valid words. Words are strings of lowercase letters.
- Depending on the size of the word list, this function may
- take a while to finish.
- """
- print ("Loading word list from file...")
- # inFile: file
- inFile = open(WORDLIST_FILENAME, 'r', 0)
- # line: string
- line = inFile.readline()
- # wordlist: list of strings
- wordlist = line.split()
- print " ", len(wordlist), "words loaded."
- return wordlist
- wordlist = load_words()
- def is_word(wordlist, word):
- """
- Determines if word is a valid word.
- wordlist: list of words in the dictionary.
- word: a possible word.
- returns True if word is in wordlist.
- Example:
- >>> is_word(wordlist, 'bat') returns
- True
- >>> is_word(wordlist, 'asdf') returns
- False
- """
- word = word.lower()
- #if len(word) > 0:
- #if word[0] == " ":
- #return False
- word = word.strip(" !@#$%^&*()-_+={}[]|\:;'<>?,./\"")
- return word in wordlist
- def random_word(wordlist):
- """
- Returns a random word.
- wordlist: list of words
- returns: a word from wordlist at random
- """
- return random.choice(wordlist)
- def random_string(wordlist, n):
- """
- Returns a string containing n random words from wordlist
- wordlist: list of words
- returns: a string of random words separated by spaces.
- """
- return " ".join([random_word(wordlist) for _ in range(n)])
- def random_scrambled(wordlist, n):
- """
- Generates a test string by generating an n-word random string
- and encrypting it with a sequence of random shifts.
- wordlist: list of words
- n: number of random words to generate and scamble
- returns: a scrambled string of n random words
- NOTE:
- This function will ONLY work once you have completed your
- implementation of apply_shifts!
- """
- s = random_string(wordlist, n) + " "
- shifts = [(i, random.randint(0, 26)) for i in range(len(s)) if s[i-1] == ' ']
- return apply_shifts(s, shifts)[:-1]
- def get_fable_string():
- """
- Returns a fable in encrypted text.
- """
- f = open("fable.txt", "r")
- fable = str(f.read())
- f.close()
- return fable
- # (end of helper code)
- # -----------------------------------
- #
- # Problem 1: Encryption
- #
- def build_coder(shift):
- """
- Returns a dict that can apply a Caesar cipher to a letter.
- The cipher is defined by the shift value. Ignores non-letter characters
- like punctuation and numbers.
- shift: -27 < int < 27
- returns: dict
- Example:
- >>> build_coder(3)
- {' ': 'c', 'A': 'D', 'C': 'F', 'B': 'E', 'E': 'H', 'D': 'G', 'G': 'J',
- 'F': 'I', 'I': 'L', 'H': 'K', 'K': 'N', 'J': 'M', 'M': 'P', 'L': 'O',
- 'O': 'R', 'N': 'Q', 'Q': 'T', 'P': 'S', 'S': 'V', 'R': 'U', 'U': 'X',
- 'T': 'W', 'W': 'Z', 'V': 'Y', 'Y': 'A', 'X': ' ', 'Z': 'B', 'a': 'd',
- 'c': 'f', 'b': 'e', 'e': 'h', 'd': 'g', 'g': 'j', 'f': 'i', 'i': 'l',
- 'h': 'k', 'k': 'n', 'j': 'm', 'm': 'p', 'l': 'o', 'o': 'r', 'n': 'q',
- 'q': 't', 'p': 's', 's': 'v', 'r': 'u', 'u': 'x', 't': 'w', 'w': 'z',
- 'v': 'y', 'y': 'a', 'x': ' ', 'z': 'b'}
- (The order of the key-value pairs may be different.)
- """
- ### TODO.
- if shift == 27:
- shift = 0
- try:
- shift = int(shift)
- if shift <-26 or shift > 26:
- print "An integer between -26 <= shift <= 26 should be enter"
- return
- except ValueError:
- print "You must enter an integer value"
- return
- alphabet = ['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',' ']
- if shift < 0:
- shift = len(alphabet) + shift
- dictionary = {}
- for i in xrange(0,len(alphabet)):
- dictionary[alphabet[i]] = alphabet[(i+shift)%27]
- dictionary[alphabet[i].lower()] = alphabet[(i+shift)%27].lower()
- #print dictionary
- return dictionary
- def build_encoder(shift):
- """
- Returns a dict that can be used to encode a plain text. For example, you
- could encrypt the plain text by calling the following commands
- >>>encoder = build_encoder(shift)
- >>>encrypted_text = apply_coder(plain_text, encoder)
- The cipher is defined by the shift value. Ignores non-letter characters
- like punctuation and numbers.
- shift: 0 <= int < 27
- returns: dict
- Example:
- >>> build_encoder(3)
- {' ': 'c', 'A': 'D', 'C': 'F', 'B': 'E', 'E': 'H', 'D': 'G', 'G': 'J',
- 'F': 'I', 'I': 'L', 'H': 'K', 'K': 'N', 'J': 'M', 'M': 'P', 'L': 'O',
- 'O': 'R', 'N': 'Q', 'Q': 'T', 'P': 'S', 'S': 'V', 'R': 'U', 'U': 'X',
- 'T': 'W', 'W': 'Z', 'V': 'Y', 'Y': 'A', 'X': ' ', 'Z': 'B', 'a': 'd',
- 'c': 'f', 'b': 'e', 'e': 'h', 'd': 'g', 'g': 'j', 'f': 'i', 'i': 'l',
- 'h': 'k', 'k': 'n', 'j': 'm', 'm': 'p', 'l': 'o', 'o': 'r', 'n': 'q',
- 'q': 't', 'p': 's', 's': 'v', 'r': 'u', 'u': 'x', 't': 'w', 'w': 'z',
- 'v': 'y', 'y': 'a', 'x': ' ', 'z': 'b'}
- (The order of the key-value pairs may be different.)
- HINT : Use build_coder.
- """
- ### TODO.
- if shift == 27:
- shift = 0
- try:
- shift = int(shift)
- if shift < 0 or shift > 26:
- print "An integer between 0 <= shift <= 26 should be enter"
- return
- except ValueError:
- print "You must enter an integer value"
- return
- #print build_coder(shift)
- return build_coder(shift)
- def build_decoder(shift):
- """
- Returns a dict that can be used to decode an encrypted text. For example, you
- could decrypt an encrypted text by calling the following commands
- >>>encoder = build_encoder(shift)
- >>>encrypted_text = apply_coder(plain_text, encoder)
- >>>decrypted_text = apply_coder(plain_text, decoder)
- The cipher is defined by the shift value. Ignores non-letter characters
- like punctuation and numbers.
- shift: 0 <= int < 27
- returns: dict
- Example:
- >>> build_decoder(3)
- {' ': 'x', 'A': 'Y', 'C': ' ', 'B': 'Z', 'E': 'B', 'D': 'A', 'G': 'D',
- 'F': 'C', 'I': 'F', 'H': 'E', 'K': 'H', 'J': 'G', 'M': 'J', 'L': 'I',
- 'O': 'L', 'N': 'K', 'Q': 'N', 'P': 'M', 'S': 'P', 'R': 'O', 'U': 'R',
- 'T': 'Q', 'W': 'T', 'V': 'S', 'Y': 'V', 'X': 'U', 'Z': 'W', 'a': 'y',
- 'c': ' ', 'b': 'z', 'e': 'b', 'd': 'a', 'g': 'd', 'f': 'c', 'i': 'f',
- 'h': 'e', 'k': 'h', 'j': 'g', 'm': 'j', 'l': 'i', 'o': 'l', 'n': 'k',
- 'q': 'n', 'p': 'm', 's': 'p', 'r': 'o', 'u': 'r', 't': 'q', 'w': 't',
- 'v': 's', 'y': 'v', 'x': 'u', 'z': 'w'}
- (The order of the key-value pairs may be different.)
- HINT : Use build_coder.
- """
- ### TODO.
- if shift == 27:
- shift = 0
- try:
- shift = int(shift)
- if shift < 0 or shift > 26:
- print "An integer between 0 <= shift <= 26 should be enter"
- return
- except ValueError:
- print "You must enter an integer value"
- return
- #print build_coder(shift)
- shift = -shift
- return build_coder(shift)
- def apply_coder(text, coder):
- """
- Applies the coder to the text. Returns the encoded text.
- text: string
- coder: dict with mappings of characters to shifted characters
- returns: text after mapping coder chars to original text
- Example:
- >>> apply_coder("Hello, world!", build_encoder(3))
- 'Khoor,czruog!'
- >>> apply_coder("Khoor,czruog!", build_decoder(3))
- 'Hello, world!'
- """
- ### TODO.
- coded_text = []
- for c in text:
- if c in coder.keys():
- coded_text.append(coder[c])
- else:
- coded_text.append(c)
- return "".join(coded_text)
- def apply_shift(text, shift):
- """
- Given a text, returns a new text Caesar shifted by the given shift
- offset. The empty space counts as the 27th letter of the alphabet,
- so spaces should be replaced by a lowercase letter as appropriate.
- Otherwise, lower case letters should remain lower case, upper case
- letters should remain upper case, and all other punctuation should
- stay as it is.
- text: string to apply the shift to
- shift: amount to shift the text
- returns: text after being shifted by specified amount.
- Example:
- >>> apply_shift('This is a test.', 8)
- 'Apq hq hiham a.'
- """
- ### TODO.
- return apply_coder(text, build_encoder(shift))
- #
- # Problem 2: Codebreaking.
- #
- def find_best_shift(wordlist, text):
- """
- Decrypts the encoded text and returns the plaintext.
- text: string
- returns: 0 <= int 27
- Example:
- >>> s = apply_coder('Hello, world!', build_encoder(8))
- >>> s
- 'Pmttw,hdwztl!'
- >>> find_best_shift(wordlist, s) returns
- 8
- >>> apply_coder(s, build_decoder(8)) returns
- 'Hello, world!'
- """
- ### TODO
- decrypted_text = []
- number_of_words = 0
- shift_value = 0
- for i in xrange(27):
- s = apply_coder(text, build_decoder(i))
- if not s[0] == " ":
- transitory_array1 = s.split(" ")
- if is_word(wordlist, transitory_array1[0]):
- transitory_array2 = []
- for j in range(0, len(transitory_array1)):
- if is_word(wordlist, transitory_array1[j]):
- transitory_array2.append(transitory_array1[j] + " ")
- if len(transitory_array2) > len(decrypted_text):
- decrypted_text = transitory_array2
- shift_value = i
- return shift_value
- #
- # Problem 3: Multi-level encryption.
- #
- def apply_shifts(text, shifts):
- """
- Applies a sequence of shifts to an input text.
- text: A string to apply the Ceasar shifts to
- shifts: A list of tuples containing the location each shift should
- begin and the shift offset. Each tuple is of the form (location,
- shift) The shifts are layered: each one is applied from its
- starting position all the way through the end of the string.
- returns: text after applying the shifts to the appropriate
- positions
- Example:
- >>> apply_shifts("Do Androids Dream of Electric Sheep?", [(0,6), (3, 18), (12, 16)])
- 'JufYkaolfapxQdrnzmasmRyrpfdvpmEurrb?'
- """
- ### TODO.
- result = text[:]
- for i in xrange(len(shifts)):
- position = shifts[i][0]
- text1 = result[position:]
- text1 = apply_shift(text1,shifts[i][1])
- if position > 0:
- result = result[0:position] + text1
- else:
- result = text1[:]
- return result
- #
- # Problem 4: Multi-level decryption.
- #
- def find_best_shifts(wordlist, text):
- """
- Given a scrambled string, returns a shift key that will decode the text to
- words in wordlist, or None if there is no such key.
- Hint: Make use of the recursive function
- find_best_shifts_rec(wordlist, text, start)
- wordlist: list of words
- text: scambled text to try to find the words for
- returns: list of tuples. each tuple is (position in text, amount of shift)
- Examples:
- >>> s = random_scrambled(wordlist, 3)
- >>> s
- 'eqorqukvqtbmultiform wyy ion'
- >>> shifts = find_best_shifts(wordlist, s)
- >>> shifts
- [(0, 25), (11, 2), (21, 5)]
- >>> apply_shifts(s, shifts)
- 'compositor multiform accents'
- >>> s = apply_shifts("Do Androids Dream of Electric Sheep?", [(0,6), (3, 18), (12, 16)])
- >>> s
- 'JufYkaolfapxQdrnzmasmRyrpfdvpmEurrb?'
- >>> shifts = find_best_shifts(wordlist, s)
- >>> print apply_shifts(s, shifts)
- Do Androids Dream of Electric Sheep?
- """
- s = text
- shifts = []
- position = 0
- #This code is not recursive so it runs until the length of s is 0 this will signal that the last word has been decoded
- while len(s) > 0:
- #tupple is the array where the possible word and its shift value is store: Note that there can be more than one valid word
- #that is the reason I use of an array of tupples.
- tupple = []
- #For the full text we run all shifts after each shift we split s1 and put its values in a matrix1.
- #Note that the first value of the matrix will be a piece of text right before the first space
- for i in xrange(27):
- s1 = apply_coder(s,build_decoder(i))
- matrix1 = s1.split()
- #We evaluate if the contents of matrix1[0] is a valid word if is is we save it in tupple with its shift value
- if is_word(wordlist,matrix1[0]) and not s1[0] == " ":
- tupple.append((matrix1[0], i))
- #since there can be more than one valid word in tupple after each shift we need to choose one and proceed with the decryption
- #The rule use in this algorithm is when possible choose the word that is the longer one in almost all cases there is only one valid word
- #but for those rare cases where there is more than 1 then we choose the one that is greater in length. This increases greatly the probability that
- #the right word has been choosen since in most cases the other valid word will be one letter words.
- max = len(tupple[0][0])
- max_shift = tupple[0][1]
- #decoded = tupple[0][0] #This line is needed if you want to print the string as is been decoded before you run apply_shifts as to test the validity of the algorithm
- #This is the actual part of the code that chooses the valid word with its respective shift value base on the max length of the possible choices
- if len(tupple) > 1:
- for i in xrange(len(tupple)-1):
- if len(tupple[i+1][0]) > max:
- max = len(tupple[i+1][0])
- #decoded = tupple[i+1][0] #Use if want to print the message as is been decoded
- max_shift = tupple[i+1][1]
- #we apply the selected shift value to string s and split its values and put them in matrix1
- s = apply_coder(s,build_decoder(max_shift))
- matrix1 = s.split()
- #text += decoded + " " #Use if you want to print the message as is been decoded
- #This part of the code has to do with the implementation of build_decoder that is the reason of 27-max_shift
- if max_shift == 0:
- max_shift = 27
- shifts.append((position, 27 - max_shift)) #this is the array of tupples that contains the position and shift value
- position += len(matrix1[0]) + 1 #The initial position is 0 and then after that we increment by the length of word in matrix1[0] which is the decoded word + 1 for the space
- s = s[len(matrix1[0])+1:] #We shorten s by the decoded word so we are left only with code that still needs to be decoded
- #print text #Use if you want to see the message printed as is been decoded
- return shifts
- def find_best_shifts_rec(wordlist, text, start):
- """
- Given a scrambled string and a starting position from which
- to decode, returns a shift key that will decode the text to
- words in wordlist, or None if there is no such key.
- Hint: You will find this function much easier to implement
- if you use recursion.
- wordlist: list of words
- text: scambled text to try to find the words for
- start: where to start looking at shifts
- returns: list of tuples. each tuple is (position in text, amount of shift)
- """
- ### TODO.
- def decrypt_fable():
- """
- Using the methods you created in this problem set,
- decrypt the fable given by the function get_fable_string().
- Once you decrypt the message, be sure to include as a comment
- at the end of this problem set how the fable relates to your
- education at MIT.
- returns: string - fable in plain text
- """
- ### TODO.
- s = get_fable_string()
- bar = find_best_shifts(wordlist,s)
- return apply_shifts(s,bar)
- #What is the moral of the story?
- #
- #
- #
- #
- #
- print decrypt_fable()
Advertisement
Add Comment
Please, Sign In to add comment