6
\$\begingroup\$

I've been teaching myself Python over the last few weeks, I have an interest in cryptography, codes, etc. so I figured starting a Morse code translator would be a good project. I know my variable names could be different, it's not really encrypting, decrypting, etc. I'm mostly looking for advice on how I can make the code cleaner and where I can be more efficient.

I think my biggest problem is not really knowing how to handle the inputs in a while loop like I usually would. The problem I had was that I couldn't check if the input was 'e' or 'd' so it just got really wonky.

Areas I know I could improve:

  • Add input loop
  • The if, elif, else for the action
  • Make 'sound' an actual boolean value
  • Find the actual sound time for the dit and dah but that's not really a code issue
# Started: 08/17/2020
# Finished: 08/17/2020
# Takes an input message and outputs the message in morse code
# Keys taken from 'https://en.wikipedia.org/wiki/Morse_code'

from playsound import playsound
import time

# Dictionary that holds each letter and it's corresponding value
dict = {'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': '--..',
        '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
        ' ': '/', '.': '.-.-.-', ',': '.-.-', '?': '..--..', "'": '.----.', '!': '-.-.--', '/': '-..-.', '(': '-.--.', ')': '-.--.-',
        ':': '---...', ';': '-.-.-.', '=': '-...-', '+': '.-.-.', '-': '-....-', '_': '..--.-', '"': '.-..-.', '$': '...-..-', '@': '.--.-.'}

outputMessage = ""               # Holds our output message

# Sounds
sound = 'False'
dit = 'dit.wav'
dah = 'dah.wav'


def Encrypt(message):

    output = ''

    for char in message:
        if char in dict:
            output = output + dict[char]
            output = output + ' '

    return output


def Get_Key(val):
    for key, value in dict.items():
        if val == value:
            return key


def Decrypt(message):

    output = ''

    letters = message.split(' ')

    for letter in letters:
        temp = Get_Key(letter)
        output = output + temp

    return output


def Get_Inputs():
    # Get Inputs
    inputString = input('Enter a message to start.\n')
    action = input('(E)ncrypt or (D)ecrypt?\n')

    # Format Inputs
    message = inputString.lower().strip()
    action = action.lower().strip()

    return message, action


def Play_Sound(message):

    for char in message:
        if char == '.':
            playsound(dit)
        elif char == '-':
            playsound(dah)
        elif char == ' ':
            time.sleep(0.15)
        elif char == '/':
            time.sleep(0.30)


message, action = Get_Inputs()

if action == 'e' or action == 'encrypt':
    outputMessage = Encrypt(message)
elif action == 'd' or action == 'decrypt':
    outputMessage = Decrypt(message)
else:
    print('Error!')

print(outputMessage)

print('')
sound = input('Play sound? (T)rue / (F)alse\n')
if sound.lower().strip() == 't' or sound.lower().strip() == 'true':
    Play_Sound(outputMessage)
\$\endgroup\$
1
  • \$\begingroup\$ Welcome to Code Review, I added morse-code tag to your question. \$\endgroup\$ – dariosicily Aug 18 '20 at 7:31
6
\$\begingroup\$

General Style

Your translation dict uses a keyword and lower case letters. Consider writing constants with upper-case letters and giving them expressive names like MORSE_CODES = {...}.

According to PEP 8, functions should be named using snake_case. CamelCase is reserved for classes: outputMessageoutput_message, def Encrypt(...)def encrypt(...), etc.

Performance

Using the Get_Key function is not very performant, since it performs a linear search of the dict. Just reverse the translation dict once and then use it:

MORSE_ENCODING = {
    'a': '.-',
    'b': '-...',
    ...
}
MORSE_DECODING = {value: key for key, value in MORSE_ENCODING.items()}

...

        temp = MORSE_DECODING[letter]

Handling errors

Currently the Encrypt function silently skips all non-translatable characters. Consider Throwing a ValueError() instead to indicate, that invalid input was provided:

def encode(message):
    """Encodes a string into morse code."""

    code = ''

    for index, char in enumerate(message):
        try:
            code += MORSE_ENCODING[char.lower()]
        except KeyError:
            raise ValueError(f'Char "{char}" at {index} cannot be encoded.')

        code += ' '

    return code[:-1]  # Remove trailing space.


def decode(morse_code):
    """Decodes morse code."""

    message = ''

    for index, sequence in enumerate(morse_code.split()):
        try:
            message += MORSE_DECODING[sequence]
        except KeyError:
            raise ValueError(f'Cannot decode code "{sequence}" at {index}.')

    return message

Correctness

Your Encrypt function currently always returns a trailing space. You can avoid that by returning output[:-1].

Terminology

Converting from morse code to text back and forth is not really an encryption in its sense. You might want to rephrase {en,de}crypt with {en,de}code.

Globals

Using global variables like outputMessage can have nasty side-effects when the program is used as a library. All the code below the def Play_Sound function should go into a def main() function that you can invoke via

if __name__ == '__main__':
    main()

At the bottom of the unit.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.