# 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"

• 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)
print(f'Correct guess {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()

• what does "''.join([chr(ord(char) + shift) for char in message.lower()])" explicitly mean? – miAK Sep 7 '19 at 5:19
• The ord() gets the numerical representation of a character if you add shift then the numerical representation is shifted by the shift value. Then the call to chr() converts a numerical value back to a letter. example: ord('a') = 97, ord('c') = 99 so if you called chr(99) it converts the 99 to its respective letter 'c', the list contains all the converted letters(strings) so a call to join() joins the list into the converted(encoded) word. – user203258 Sep 7 '19 at 5:27
• You might want to check this link docs.python.org/2/library/functions.html – user203258 Sep 7 '19 at 5:28