How can I ensure incremental changes in deciphered messages in Python substitution cipher decoding?

42 views Asked by At

I am working on a decipher method. I want to decipher a sentence manually by user input.

import random


class Cryptography:
    def __init__(self, input_file):
        self.input_file = input_file
        with open(self.input_file, 'r+') as file:
            self.content = file.read()
        self.sentence = ''
        self.code = {
            'A': 'A', 'Ą': 'A', 'B': 'A', 'C': 'A',
            'Ć': 'A', 'D': 'A', 'E': 'A', 'Ę': 'A',
            'F': 'A', 'G': 'A', 'H': 'A', 'I': 'A',
            'J': 'A', 'K': 'A', 'L': 'A', 'Ł': 'A',
            'M': 'A', 'N': 'A', 'Ń': 'A', 'O': 'A',
            'Ó': 'A', 'P': 'A', 'R': 'A', 'S': 'A',
            'Ś': 'A', 'T': 'A', 'U': 'A', 'W': 'A',
            'Y': 'A', 'Z': 'A', 'Ż': 'A', 'Ź': 'A',
        }
        self.replaced_sentence = ''
        self.welcome = '\nWITAJ\nMASZ TERAZ OKAZJE ODSZYFROWAĆ POWYŻSZĄ WIADOMOSĆ\nZA KAŻDYM RAZEM JAK ZAMIENISZ ' \
                       'LITERKI WYSWIETLONA ZOSTANIE\nCZĘŚCIOWO ODSZYFROWANA WIADOMOŚĆ.\nPOWODZENIA!\n '

    # repair converts whatever is in self.content to self.sentence, so it is one line of text without large whitespaces.
    def repair(self):
        for char in self.content:
            if char == '\n':
                char = char.replace('\n', ' ')
            char = char.capitalize()
            self.sentence += char
        self.sentence = " ".join(self.sentence.split())
        print(self.sentence)

    def encrypt(self):
        numbers = list(range(32))
        random.shuffle(numbers)
        letters = list(self.code.keys())
        encrypted_code = {}
        for i, letter in enumerate(letters):
            encrypted_code[letter] = letters[numbers[i]]
        self.code = encrypted_code
        print(f"Encrypted successfully!")

    def transform(self):
        special_char = [' ', ',', '!', '.', '(', ')', ';',]
        for char in self.sentence:
            if char in special_char:
                replaced_char = char
            else:
                replaced_char = self.code.get(char)
            self.replaced_sentence += replaced_char

    def decode(self):
        guessed_sentence = self.replaced_sentence
        spec_char = zip([' ', ',', '!', '.', '(', ')', ';'], [' ', ',', '!', '.', '(', ')', ';'])
        self.code.update(spec_char)
        while True:
            print(guessed_sentence)
            guess = input('please type eg. A = S : ')
            guess = guess.strip()
            guess = guess.replace(' ', '')
            letter1 = guess[0].upper()
            letter2 = guess[2].upper()
            char_mapping = self.code
            char_mapping[letter1] = letter2
            old_string = self.replaced_sentence
            guessed_sentence = ''
            for char in old_string:
                guessed_sentence += char_mapping[char]
            if guessed_sentence == self.sentence:
                break


test = Cryptography('message')
test.repair()
test.encrypt()
test.transform()
test.decode()
  1. __init__(self, input_file): Initializes the Cryptography class with an input file. It reads the content of the input file and initializes attributes such as sentence, code, replaced_sentence, and welcome.

  2. repair(self): Processes the content read from the input file, removes extra whitespaces, capitalizes characters, and stores the processed sentence in the self.sentence attribute.

  3. encrypt(self): Generates a random mapping of letters based on the original mapping provided in self.code, effectively encrypting the message.

  4. transform(self): Applies the encryption mapping generated in encrypt method to transform the original sentence into an encrypted one, storing the result in self.replaced_sentence.

  5. decode(self): Initiates the deciphering process. It prompts the user to input letter substitutions, applies those substitutions to the encrypted message, and prints the partially deciphered message until it matches the original sentence.

self.welcome is not used in this version, it is just aestethics, also in polish so it doesn't matter.

I want an output that shows encrypted message but with changes only where implemented.

To clarify, here is the output of the whole function (sentence is in polish but I don't think it will disturb understanting of the case):

LITWO! OJCZYZNO MOJA! TY JESTEŚ JAK ZDROWIE.
Encrypted successfully!
UDŹHĘ! ĘJZMŻMEĘ TĘJŃ! ŹŻ JAGŹAI JŃŁ MBĆĘHDA.
please type eg. A = S : U=L
LBCRL! LJMTWTAL ŹLJÓ! CW JŃOCŃD JÓF TĄSLRBŃ.
please type eg. A = S : D=I
LICRL! LJMTWTAL ŹLJÓ! CW JŃOCŃD JÓF TĄSLRIŃ.
please type eg. A = S : Ź=T
LITRL! LJMTWTAL ŹLJÓ! TW JŃOTŃD JÓF TĄSLRIŃ.
please type eg. A = S : 

As you can see the encrypted message changes everytime I make changes. This is the outcome that I pursue:

LITWO! OJCZYZNO MOJA! TY JESTEŚ JAK ZDROWIE.
Encrypted successfully!
UDŹHĘ! ĘJZMŻMEĘ TĘJŃ! ŹŻ JAGŹAI JŃŁ MBĆĘHDA.
please type eg. A = S : U=L
LDŹHĘ! ĘJZMŻMEĘ TĘJŃ! ŹŻ JAGŹAI JŃŁ MBĆĘHDA.
please type eg. A = S : D=I
LIŹHĘ! ĘJZMŻMEĘ TĘJŃ! ŹŻ JAGŹAI JŃŁ MBĆĘHIA.
please type eg. A = S : Ź=T
LITHĘ! ĘJZMŻMEĘ TĘJŃ! TŻ JAGTAI JŃŁ MBĆĘHIA.
please type eg. A = S : 

How can I change the code so it produces desired outcome?

2

There are 2 answers

0
Mark Tolonen On BEST ANSWER

Heavily refactored solution. The key is to use str.translate along with a current list of replacements to generate the partially decoded message from the encrypted message each time. Because translating one letter to say 'A' might be confusing with an untranslated 'A', I print the current translation and original sentence each time to make it easier to use (for me, anyway):

import random

class Cryptography:
    def __init__(self, message):
        self.sentence = message.upper()
        self.alphabet = 'AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŻŹ'
        keys = [ord(a) for a in self.alphabet] # keys must be ordinals
        values = list(keys)  # need mutable list for shuffle
        random.shuffle(values)
        self.code = dict(zip(keys, values))  # build translation table
        self.replaced_sentence = self.sentence.translate(self.code)  # translate
        print(f'Encrypted successfully!')

    def decode(self):
        guessed_sentence = self.replaced_sentence
        # build up a translation table.  Start with hyphens for unknowns.
        guessed_code = dict(zip([ord(a) for a in self.alphabet], '-' * len(self.alphabet)))
        while True:
            print(self.replaced_sentence)
            guess = input('please type eg. A = S : ')
            guess = guess.strip()
            guess = guess.replace(' ', '')
            letter1 = guess[0].upper()
            letter2 = guess[2].upper()
            guessed_code[ord(letter1)] = ord(letter2)  # update translation table
            guessed_sentence = self.replaced_sentence.translate(guessed_code)  # translate
            print(guessed_sentence)
            if guessed_sentence == self.sentence:
                print('Success!')
                break

test = Cryptography('Litwo! Ojczyzno moja! Ty jesteś jak zdrowie.')
test.decode()

Output:

Encrypted successfully!
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : s=l
L----! -------- ----! -- ------ --- -------.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : w=i
LI---! -------- ----! -- ------ --- -----I-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : Ź=i
LII--! -------- ----! I- ---I-- --- -----I-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : Ź=t
LIT--! -------- ----! T- ---T-- --- -----I-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : j=w
LITW-! -------- ----! T- ---T-- --- ----WI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : i=o
LITWO! O------O -O--! T- ---T-- --- ---OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : o=j
LITWO! OJ-----O -OJ-! T- J--T-- J-- ---OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : z=c
LITWO! OJC----O -OJ-! T- J--T-- J-- ---OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : b=z
LITWO! OJCZ-Z-O -OJ-! T- J--T-- J-- Z--OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : m=y
LITWO! OJCZYZ-O -OJ-! TY J--T-- J-- Z--OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : f=n
LITWO! OJCZYZNO -OJ-! TY J--T-- J-- Z--OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : k=m
LITWO! OJCZYZNO MOJ-! TY J--T-- J-- Z--OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : Ł=a
LITWO! OJCZYZNO MOJA! TY J--T-- JA- Z--OWI-.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : a=e
LITWO! OJCZYZNO MOJA! TY JE-TE- JA- Z--OWIE.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : r=s
LITWO! OJCZYZNO MOJA! TY JESTE- JA- Z--OWIE.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : y=ś
LITWO! OJCZYZNO MOJA! TY JESTEŚ JA- Z--OWIE.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : h=k
LITWO! OJCZYZNO MOJA! TY JESTEŚ JAK Z--OWIE.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : Ć=d
LITWO! OJCZYZNO MOJA! TY JESTEŚ JAK ZD-OWIE.
SWŹJI! IOZBMBFI KIOŁ! ŹM OARŹAY OŁH BĆĘIJWA.
please type eg. A = S : Ę=r
LITWO! OJCZYZNO MOJA! TY JESTEŚ JAK ZDROWIE.
Success!
0
Krzysztof Mrozik On

I have modified anwser by Mark Tolonen, by changing decode function so it does exactly what I wanted:

import random

class Cryptography:
    def __init__(self, message):
        self.sentence = message.upper()
        self.alphabet = 'AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŻŹ'
        keys = [ord(a) for a in self.alphabet]  # keys must be ordinals
        values = list(keys)  # need mutable list for shuffle
        random.shuffle(values)
        self.code = dict(zip(keys, values))  # build translation table
        self.replaced_sentence = self.sentence.translate(self.code)  # translate
        print(f'Encrypted successfully!')

    def decode(self):
        guessed_sentence = self.replaced_sentence
        # Lists will store each character separately providing information about place and character
        list_enc = []
        list_guess = []

        for i in self.replaced_sentence:
            list_enc += i

        for i in guessed_sentence:
            list_guess += i

        # hits will store temporarely data about which coordinate of the list to change
        hits = []

        print(self.replaced_sentence)
        while True:
            guess = input('please type eg. A = S : ')
            guess = guess.strip()
            guess = guess.replace(' ', '')
            letter1 = guess[0].upper()
            letter2 = guess[2].upper()

            # this loop searches for places where letters should be changed, and appends these coordinates to hits
            for i in range(len(list_enc)):
                if letter1 == list_enc[i]:
                    hit = i
                    hits.append(hit)

            # this loop changes letters in list_guess in designated coordinantes in hits
            for i in range(len(hits)):
                list_guess[hits[i]] = letter2

            # join back list to string
            guessed_sentence1 = ''.join(list_guess)

            # reset hits to empty list
            hits = []

            print(guessed_sentence1)
            if guessed_sentence == self.sentence:
                print(f"{guessed_sentence}\nCONGRATULATIONS!")
                break


test = Cryptography('Litwo! Ojczyzno moja! Ty jesteś jak zdrowie.')
test.decode()

Output looks like this:

Encrypted successfully!
LITWO! OJCZYZNO MOJA! TY JESTEŚ JAK ZDROWIE.
WŚZCŁ! ŁJSŹYŹŃŁ RŁJĆ! ZY JFNZFU JĆB ŹDGŁCŚF.
please type eg. A = S : W=L
LŚZCŁ! ŁJSŹYŹŃŁ RŁJĆ! ZY JFNZFU JĆB ŹDGŁCŚF.
please type eg. A = S : Ś=I
LIZCŁ! ŁJSŹYŹŃŁ RŁJĆ! ZY JFNZFU JĆB ŹDGŁCIF.
please type eg. A = S : Z=T