Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 127 additions & 45 deletions exercise/ps4b.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ def load_words(file_name):
'''
print("Loading word list from file...")
# inFile: file
inFile = open(file_name, 'r')
# wordlist: list of strings
wordlist = []
for line in inFile:
wordlist.extend([word.lower() for word in line.split(' ')])
print(" ", len(wordlist), "words loaded.")
return wordlist
try:
inFile = open(file_name, 'r')
# wordlist: list of strings
wordlist = []
for line in inFile:
wordlist.extend([word.lower() for word in line.split(' ')])
print(" ", len(wordlist), "words loaded.")
return wordlist
except FileNotFoundError:
print(f"Warning: File {file_name} not found!")
return []

def is_word(word_list, word):
'''
Expand All @@ -35,12 +39,6 @@ def is_word(word_list, word):
word (string): a possible word.

Returns: True if word is in word_list, False otherwise

Example:
>>> is_word(word_list, 'bat') returns
True
>>> is_word(word_list, 'asdf') returns
False
'''
word = word.lower()
word = word.strip(" !@#$%^&*()-_+={}[]|\:;'<>?,./\"")
Expand All @@ -50,10 +48,14 @@ def get_story_string():
"""
Returns: a story in encrypted text.
"""
f = open("story.txt", "r")
story = str(f.read())
f.close()
return story
try:
f = open("story.txt", "r")
story = str(f.read())
f.close()
return story
except FileNotFoundError:
print("Warning: story.txt not found!")
return ""

### END HELPER CODE ###

Expand All @@ -70,15 +72,16 @@ def __init__(self, text):
self.message_text (string, determined by input text)
self.valid_words (list, determined using helper function load_words)
'''
pass #delete this line and replace with your code here
self.message_text = text
self.valid_words = load_words(WORDLIST_FILENAME)

def get_message_text(self):
'''
Used to safely access self.message_text outside of the class

Returns: self.message_text
'''
pass #delete this line and replace with your code here
return self.message_text

def get_valid_words(self):
'''
Expand All @@ -87,7 +90,7 @@ def get_valid_words(self):

Returns: a COPY of self.valid_words
'''
pass #delete this line and replace with your code here
return self.valid_words[:]

def build_shift_dict(self, shift):
'''
Expand All @@ -103,7 +106,17 @@ def build_shift_dict(self, shift):
Returns: a dictionary mapping a letter (string) to
another letter (string).
'''
pass #delete this line and replace with your code here
lower_keys = string.ascii_lowercase
upper_keys = string.ascii_uppercase
shift_dict = {}

for i in range(26):
# Map lowercase
shift_dict[lower_keys[i]] = lower_keys[(i + shift) % 26]
# Map uppercase
shift_dict[upper_keys[i]] = upper_keys[(i + shift) % 26]

return shift_dict

def apply_shift(self, shift):
'''
Expand All @@ -117,7 +130,14 @@ def apply_shift(self, shift):
Returns: the message text (string) in which every character is shifted
down the alphabet by the input shift
'''
pass #delete this line and replace with your code here
mapping_dict = self.build_shift_dict(shift)
encrypted_text = []
for char in self.message_text:
if char in mapping_dict:
encrypted_text.append(mapping_dict[char])
else:
encrypted_text.append(char)
return "".join(encrypted_text)

class PlaintextMessage(Message):
def __init__(self, text, shift):
Expand All @@ -135,31 +155,34 @@ def __init__(self, text, shift):
self.message_text_encrypted (string, created using shift)

'''
pass #delete this line and replace with your code here
Message.__init__(self, text)
self.shift = shift
self.encryption_dict = self.build_shift_dict(shift)
self.message_text_encrypted = self.apply_shift(shift)

def get_shift(self):
'''
Used to safely access self.shift outside of the class

Returns: self.shift
'''
pass #delete this line and replace with your code here
return self.shift

def get_encryption_dict(self):
'''
Used to safely access a copy self.encryption_dict outside of the class

Returns: a COPY of self.encryption_dict
'''
pass #delete this line and replace with your code here
return self.encryption_dict.copy()

def get_message_text_encrypted(self):
'''
Used to safely access self.message_text_encrypted outside of the class

Returns: self.message_text_encrypted
'''
pass #delete this line and replace with your code here
return self.message_text_encrypted

def change_shift(self, shift):
'''
Expand All @@ -171,7 +194,9 @@ def change_shift(self, shift):

Returns: nothing
'''
pass #delete this line and replace with your code here
self.shift = shift
self.encryption_dict = self.build_shift_dict(shift)
self.message_text_encrypted = self.apply_shift(shift)


class CiphertextMessage(Message):
Expand All @@ -185,7 +210,8 @@ def __init__(self, text):
self.message_text (string, determined by input text)
self.valid_words (list, determined using helper function load_words)
'''
pass #delete this line and replace with your code here
# --- ĐÃ SỬA LỖI TẠI DÒNG DƯỚI ĐÂY ---
Message.__init__(self, text)

def decrypt_message(self):
'''
Expand All @@ -196,29 +222,85 @@ def decrypt_message(self):
the message, then we would expect 26 - s to be the best shift value
for decrypting it.

Note: if multiple shifts are equally good such that they all create
the maximum number of valid words, you may choose any of those shifts
(and their corresponding decrypted messages) to return

Returns: a tuple of the best shift value used to decrypt the message
and the decrypted message text using that shift value
'''
pass #delete this line and replace with your code here
best_shift = 0
max_valid_words = 0
best_message = ""

# Try all possible shifts from 0 to 25
for s in range(26):
# Apply shift to the current ciphertext
decrypted_try = self.apply_shift(s)

# Split into words to check validity
words = decrypted_try.split(' ')
valid_count = 0

# Count valid words
for word in words:
if is_word(self.valid_words, word):
valid_count += 1

# Update best shift if we found more valid words
if valid_count > max_valid_words:
max_valid_words = valid_count
best_shift = s
best_message = decrypted_try

return (best_shift, best_message)

if __name__ == '__main__':

# #Example test case (PlaintextMessage)
# plaintext = PlaintextMessage('hello', 2)
# print('Expected Output: jgnnq')
# print('Actual Output:', plaintext.get_message_text_encrypted())
#
# #Example test case (CiphertextMessage)
# ciphertext = CiphertextMessage('jgnnq')
# print('Expected Output:', (24, 'hello'))
# print('Actual Output:', ciphertext.decrypt_message())
# Example test case (PlaintextMessage)
plaintext = PlaintextMessage('hello', 2)
print('Expected Output: jgnnq')
print('Actual Output:', plaintext.get_message_text_encrypted())

#TODO: WRITE YOUR TEST CASES HERE
# Example test case (CiphertextMessage)
ciphertext = CiphertextMessage('jgnnq')
print('Expected Output:', (24, 'hello'))
print('Actual Output:', ciphertext.decrypt_message())

#TODO: best shift value and unencrypted story
# TODO: WRITE YOUR TEST CASES HERE
print("\n------------------------------")
print("MY CUSTOM TEST CASES:")

# 1. Test PlaintextMessage
print("\n1. Testing PlaintextMessage:")
my_msg = PlaintextMessage("Python is awesome!", 4)
print("Original:", my_msg.get_message_text())
print("Shift:", my_msg.get_shift())
print("Encrypted:", my_msg.get_message_text_encrypted())

# Thay đổi shift
print("-> Changing shift to 1...")
my_msg.change_shift(1)
print("New Encrypted:", my_msg.get_message_text_encrypted())

# 2. Test CiphertextMessage
print("\n2. Testing CiphertextMessage:")
# Giả sử ta có chuỗi đã mã hóa: "Python is awesome!" -> shift 1 -> "Qzuipo jt bxftpnf!"
encrypted_str = "Qzuipo jt bxftpnf!"
cipher_msg = CiphertextMessage(encrypted_str)

# Giải mã
result = cipher_msg.decrypt_message()
print(f"Input Encrypted: {encrypted_str}")
print(f"Best Shift found: {result[0]}")
print(f"Decrypted Text: {result[1]}")

# TODO: best shift value and unencrypted story
print("\n------------------------------")
print("DECODING STORY.TXT:")

pass #delete this line and replace with your code here
story_text = get_story_string()
if story_text:
story_cipher = CiphertextMessage(story_text)
decoded_story = story_cipher.decrypt_message()
print(f"Best Shift: {decoded_story[0]}")
print("Story Content:\n")
print(decoded_story[1])
else:
print("Could not load story.txt to decrypt.")
Loading