Caesar Cypher guessing game Caeser's cipher code where there are a converter and a game part with user input. The code includes keys (settable and not settable).

import random


def change_letter(original, new_message, key_number, alpha):
    for letter in original:
        if letter in alpha.lower():
            new_message += alpha[(alpha.lower().index(letter) + key_number) % 26].lower()
        elif letter in alpha:
            new_message += alpha[(alpha.index(letter) + key_number) % 26]
        else:
            new_message += letter

    return new_message


def encipher(the_key, message, inp=True):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    output_message = ""

    if inp:
        try:
            the_key = int(input('Key?  ・  '))
        except ValueError:
            the_key = input('\nNot valid input  ・  ')
            while not the_key.isnumeric():
                the_key = input('Not valid input  ・  ')

    output_message = change_letter(message, output_message, int(the_key), alphabet)

    return output_message


def convert():
    while True:
        key = None
        type_word = input('\nType your word  ・  ')
        print('\n{}  ::  {}'.format(type_word, encipher(key, type_word)))
        convert_again = input('\nWould you like to convert again? | Y/N  ・  ').upper()

        if convert_again == 'Y' or convert_again == 'YES':
            continue
        break


def play():
    while True:
        key = random.randint(1, 25)
        words = ('WWE', 'Javascript', 'Pythonista', 'Computer Science', 'Eighty-three', 'Event Log', '5K Freestyle')
        word = random.choice(words)
        question = input('\nWhat is {} of key {}  ・  '.format(word, key)).lower()
        changed_word = encipher(key, word, inp=False)

        if question == changed_word.lower():
            print(f'Congratulations! You guessed {changed_word} from {word}')
        else:
            print(f'Oops! Wrong answer. The word is {changed_word} from {word}')

        play_again = input('\nWould you like to play again? | Y/N  ・  ')

        if play_again.upper() == 'YES' or play_again.upper() == 'Y':
            continue
        break


def main():
    while True:
        convert_or_play = input('C to convert, P to play  ・  ')
        if convert_or_play.upper() == 'C':
            convert()
        elif convert_or_play.upper() == 'P':
            play()
        else:
            break
        print()


if __name__ == '__main__':
    main()
def change_letter(original, new_message, key_number, alpha):



docstrings: Python documentation strings (or docstrings) provide a convenient way of associating documentation with Python modules, functions, classes, and methods.It’s specified in source code that is used, like a comment, to document a specific segment of code.


You should include a docstring indicating what the parameters are and what the function does:

def change_letter(original, new_message, key_number, alpha):
    """
    Encode message.
    original: a string (word)
    new_message: empty string
    key_number: int
    alpha: a string containing lowercase alphabet
    """


unecessary function parameter: new_message

There is no need to bother user with entering an empty string.

string immutability::

new_message += alpha[(alpha.lower().index(letter) + key_number) % 26].lower()


A string is immutable in Python therefore each time you're adding to a string, you're creating another string instance and this is pretty inefficient, use list comprehension syntax instead.

list comprehension syntax:

You can use list comprehensions to replace this whole function in the following way:

def encode(message: str, shift: int):
   """Encode and return message."""
return ''.join([chr(ord(char) + shift) for char in message.lower()])


the use of ord and chr is very useful in this case since both do not care about whether a character is uppercase, lowercase or digit, they convert everything and they are much more efficient than alpha.lower().index(letter) + key_number) % 26 which call .lower() and .index() for each letter in the word.

def encipher(the_key, message, inp=True):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"



same comments regarding docstrings.
you can use string.ascii_uppercase instead of manually writing the letters with possibility of a slip of hand and the creation of a bug in the case of a missed letter.
if inp: I did not find a single case where inp might be set to False so there is no need for this parameter.


This whole encipher() can be shortened to the following:

def encipher(message, encryption_key=input('Enter key: ')):
    """Encipher according to a user given key, return an error if key is not digit."""
    while not encryption_key.isdigit():
        encryption_key = input('Invalid input, enter a number: ')
    return encode(message, int(encryption_key))


f-strings: since you're using Python 3, in Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces. The expressions are replaced with their values.

print('\n{}  ::  {}'.format(type_word, encipher(key, type_word)))


expression can be written using f-strings in the following way:

print(f"\n{type_word} : {encipher(key, type_word)}")


same goes for question = input('\nWhat is {} of key {}  ・  '.format(word, key)).lower()

can be written: 

question = input(f'What is the translation of {word} using {key} as key? ').lower()


Here's a refactored version of the code:

import random


def encode(message: str, shift: int):
    """Encode and return message."""
    return ''.join([chr(ord(char) + shift) for char in message.lower()])


def encipher():
    """Encode message interactively."""
    message = input('Enter word/message: ')
    encryption_key = input('Enter key: ')
    while not encryption_key.isdigit():
        key = input('Invalid input, enter a number: ')
    encoded_message = encode(message, int(encryption_key))
    print(f'{message} has been encoded to {encoded_message}')
    re_encode = input(f'Would you like to convert {encoded_message} again? y/n: ').lower()
    while re_encode.lower() not in 'yesno':
        print(f'Invalid input {re_encode}')
        re_encode = input(f'Would you like to convert another word? y/n: ').lower()
    if re_encode in 'no':
        print('Thank you for using Cesar cipher.')
        exit(0)
    if re_encode in 'yes':
        encipher()


def play():
    """Play a cipher game."""
    key = random.randint(1, 25)
    words = ('WWE', 'Javascript', 'Pythonista', 'Computer Science', 'Eighty-three', 'Event Log', '5K Freestyle')
    word = random.choice(words)
    answer = input(f'What is the translation of {word} using {key} as key? ')
    encoded_word = encode(word, key)
    if answer == encoded_word:
        print(f'Correct guess {encoded_word}')
    if answer != encoded_word:
        print(f'Oops! wrong guess {answer} the correct answer is {encoded_word}')
    replay = input('Would you like to play again? y/n: ')
    while replay not in 'yesno':
        print(f'Invalid input {replay}')
        replay = input('Would you like to play again? y/n: ')
    if replay in 'no':
        print('Thank you for using Cesar cipher.')
        exit(0)
    if replay in 'yes':
        play()


if __name__ == '__main__':
    while True:
        game_or_encode = input('Enter e to encode or p to play e/p: ').lower()
        while game_or_encode not in 'ep':
            print(f'Invalid entry {game_or_encode}')
            game_or_encode = input('Enter e to encode or p to play e/p: ').lower()
        if game_or_encode == 'e':
            encipher()
        if game_or_encode == 'p':
            play()