# A simple hangman game

I'm coding a simple hangman for the MIT Online Course Ps.

Here's what I wrote so far:


from string import ascii_lowercase
import random
def hint(word_so_far):
pass

def is_word_guessed(secret_word, c_letters):
for char in secret_word:
if char not in c_letters:
return False
return True

def rem_letters(l_guessed):
rem_letters = ''
for char in ascii_lowercase:
if char not in l_guessed:
rem_letters += char +' '
return rem_letters.upper()

def word_so_far(secret_word, c_letters):
word_so_far = ''
for char in secret_word:
if char not in c_letters:
word_so_far += '_ '
else:
word_so_far += char + ' '
return word_so_far

def print_war_msg(wars_rem, g_rem, is_not_valid, is_not_one_letter):
if g_rem >= 1:
if is_not_valid:
elif is_not_one_letter:
print('Please enter one letter at a time')
else:
print()
if wars_rem == 1:
print('You have one more warning.')
elif wars_rem == 0:
print('You have no warnings remaining so you lose one guess!')
print('... You now have 3 warning.')
else:
print('You have', wars_rem, 'warnings left.')
print()

def game_start_msg(secret_word):
print()
print('Welcome to the game Hangman!')
print('I\'m thinking of a game that is', len(secret_word), 'letters long.')

def guess_start_msg(g_rem, secret_word, l_guessed):
print('-' * 40)
print()
if g_rem == 1:
print('You have one more guess')
else:
print('You have', g_rem, 'guesses left.')
print()
print('Word so far:', word_so_far(secret_word, l_guessed))
print()
print('Available Letters :', rem_letters(l_guessed))
print()

def end_game_msg(g_rem, secret_word):
def total_score(g_rem, secret_word):
uniq = ''
for char in secret_word:
if char not in uniq:
uniq += char
return len(uniq) * g_rem
if g_rem <= 0:
print('Unfortunately you ran out of guesses,\nThe Secret word was: >>>', secret_word, '<<<')
else:
print('Congrats, You win! Your total score is', total_score(g_rem, secret_word))
def hangman(secret_word):
l_guessed = []
c_letters = []
wars_rem = 3
g_rem = 6
game_start_msg(secret_word)
print(is_word_guessed(c_letters, secret_word))
while not is_word_guessed(secret_word, c_letters) and g_rem > 0:
guess_start_msg(g_rem, secret_word, l_guessed)
guess = input('Please guess a letter: ').lower()
print()
if guess == 'q':
break
elif guess == '*':
pass
# Conditions for the guessed letter
is_not_valid = not guess.isalpha()
is_not_one_letter = len(guess) != 1 and guess != 'cmd'
is_guessed = guess in l_guessed
while guess == 'cmd':
print('Enter Command...')
cmd = input ()
if cmd == 'raise guess':
by = int(input('Raise by?'))
g_rem += by
print(f'Gusses Remaining increased by {by} , current guesses are {g_rem}.')
elif cmd == 'reveal':
win_game = input('Do You Want To Win? (Y/N)').upper()
if win_game == 'Y':
c_letters = [lett for lett in secret_word]
else:
print(f'Secret word is {secret_word}')

elif cmd == 'exit':
break

else:
print('''List Of Possible Commands:\
\nraise guess
\nreveal
\nexit
''')
continue
if guess == 'cmd':
continue

if is_not_valid or is_not_one_letter or is_guessed:
wars_rem -= 1
print_war_msg(wars_rem, g_rem, is_not_valid, is_not_one_letter)
else:
l_guessed.append(guess)
if guess in secret_word:
c_letters.append(guess)
print('Good Guess!')
print()
else:
if guess in 'aeiou':
g_rem -= 2
print('You lose two guesses because you guessed a wrong vowel')
else:
g_rem -= 1
if guess not in 'aeiou' :
print('Oops, that letter is not in my word')
print()
if wars_rem == 0:
g_rem -= 1
wars_rem = 3

end_game_msg(g_rem, secret_word)
list_of_words = ['hello', 'say', 'zebra', 'jake', 'man', 'human', 'god', 'fuel', 'car', 'sea', 'cat', 'dog', 'building']
secret_word = random.choice(list_of_words)
hangman(secret_word)


Any recommendations on ways I can improve this?

• I didn't look in detail yet but... 1) You can use callback functions to return the results to the user, in this case, "print()" works fine because you are using CLI (command line interface). If you want to send the results to a GUI, etc. 2) You can also create at least two classes in this program, one for your callbacks (prints, flags, whatever) and another for the game. 3) Use fstrings (more readable) ex: print(f'You have {g_rem} guesses left.'). If you need help just ping. Jan 7, 2020 at 9:21

### Avoid mixing import styles

from string import ascii_lowercase
import random


Either way of importing is fine. But try and stick with one style rather than mixing different ones. So:

from string import ascii_lowercase
from random import choice


### Names

Names should be familiar and self-explanatory to the reader. You have abbreviated "warning" as "war" which I've never seen before and which I find confusing.

Same thing for l_guessed. What is l? And "rem" seem to stand for "remaining." I changed all those to "left" because "left" is synonymous with "remaining" and easy to type. And "char" is a well-established abbrevation for "character" and "char" is easier to type (and read!) than "letter" so I changed that too.

I've renamed the following:

• c_letters => correct_chars
• g_rem => guesses_left
• l_guessed => guessed_chars
• lett => char
• list_of_words => words
• print_war_msg => print_warning_msg
• rem_letters = chars_left
• wars_rem => warnings_left

### Sets

A useful feature one step up from the beginner level is sets. Sets should almost always be used instead of lists when you don't care about the order of the items contained. For example, you don't care about the order of the guesses, or the correct characters so both guessed_chars and correct_chars should be sets.

Using sets, you can calculate the total score for a winning player like this:

score = len(set(secret_word)) * guesses_left


### Consistent string formatting

In some parts of the code, you use f-strings for formatting but not in other parts. It's better to be consistent and use the same formatting style wherever possible.

### Avoid using nested functions

It is legal and sometimes useful to nest function in Python, like this:

def end_game_msg(g_rem, secret_word):
def total_score(g_rem, secret_word):
...


But more often than not, it makes the code harder to read for no added benefit.

### Don't define variables before they are needed

In your main loop, you seem to have some kind of superuser mode:

# Conditions for the guessed letter
is_not_valid = not guess.isalpha()
is_not_one_letter = len(guess) != 1 and guess != 'cmd'
is_guessed = guess in guessed_chars
while guess == 'cmd':
print('Enter Command...')
...
if guess == 'cmd':
continue
if is_not_valid or is_not_one_letter or is_guessed:


Observe that is_not_valid, is_not_one_letter and is_guessed are defined many lines before the if-statement in which they are used. You should move them downwards so that they are adjacent to the if-statement.

### Final code

from string import ascii_lowercase
from random import choice

def is_word_guessed(secret_word, c_letters):
for char in secret_word:
if char not in c_letters:
return False
return True

def chars_left(guessed_chars):
chars_left = ''
for char in ascii_lowercase:
if char not in guessed_chars:
chars_left += char + ' '
return chars_left.upper()

def word_so_far(secret_word, correct_chars):
word_so_far = ''
for char in secret_word:
if char not in correct_chars:
word_so_far += '_ '
else:
word_so_far += char + ' '
return word_so_far

def print_warning_msg(warnings_left, is_not_valid, is_not_one_letter):
if is_not_valid:
elif is_not_one_letter:
print('Please enter one letter at a time')
else:
print()
if warnings_left == 1:
print('You have one more warning.')
elif warnings_left == 0:
print('You have no warnings remaining so you lose one guess!')
print('... You now have 3 warning.')
else:
print(f'You have {warnings_left} warnings left.')
print()

def game_start_msg(secret_word):
print()
print('Welcome to the game Hangman!')
print(f'I\'m thinking of a word that is {len(secret_word)} letters long.')

def guess_start_msg(guesses_left, secret_word, guessed_chars):
print('-' * 40)
print()
if guesses_left == 1:
print('You have one more guess')
else:
print(f'You have {guesses_left} guesses left.')
print()
print(f'Word so far: {word_so_far(secret_word, guessed_chars)}')
print()
print(f'Available Letters: {chars_left(guessed_chars)}')
print()

def end_game_msg(guesses_left, secret_word):
if guesses_left <= 0:
print('Unfortunately you ran out of guesses,')
print(f'The Secret word was: >>>{secret_word}<<<')
else:
score = len(set(secret_word)) * guesses_left
print(f'Congrats, You win! Your score is {score}.')

def hangman(secret_word):
guessed_chars = set()
correct_chars = set()
warnings_left = 3
guesses_left = 10
game_start_msg(secret_word)
while not is_word_guessed(secret_word, correct_chars) and guesses_left > 0:
guess_start_msg(guesses_left, secret_word, guessed_chars)
guess = input('Please guess a letter: ').lower()
print()
if guess == 'q':
break
while guess == 'cmd':
print('Enter Command...')
cmd = input ()
if cmd == 'raise guess':
by = int(input('Raise by?'))
guesses_left += by
print(f'Gusses Remaining increased by {by},'
f'current guesses are {guesses_left}.')
elif cmd == 'reveal':
win_game = input('Do You Want To Win? (Y/N)').upper()
if win_game == 'Y':
correct_chars = set(secret_word)
else:
print(f'Secret word is {secret_word}')
elif cmd == 'exit':
break
else:
print('''List Of Possible Commands:\
\nraise guess
\nreveal
\nexit
''')
continue
if guess == 'cmd':
continue
is_not_valid = not guess.isalpha()
is_not_one_letter = len(guess) != 1
is_guessed = guess in guessed_chars
if is_not_valid or is_not_one_letter or is_guessed:
warnings_left -= 1
print_warning_msg(warnings_left, is_not_valid, is_not_one_letter)
else:
if guess in secret_word:
print('Good Guess!')
print()
else:
if guess in 'aeiou':
guesses_left -= 2
print('You lose two guesses because you guessed a wrong vowel')
else:
guesses_left -= 1
print('Oops, that letter is not in my word')
print()
if warnings_left == 0:
guesses_left -= 1
warnings_left = 3

end_game_msg(guesses_left, secret_word)

words = [
'hello', 'say', 'zebra', 'jake', 'man', 'human',
'god', 'fuel', 'car', 'sea', 'cat', 'dog', 'building'
]
secret_word = choice(words)
hangman(secret_word)


### More suggestions

A very useful string function in Python is the join function. By learning about it, you'll improve your Python programming a lot.

  +----\
|    o
|   /|\
|    \\
/ \


What's a hangman game without a hanging man? :)

• 10/10 for the answer man. Jan 7, 2020 at 20:19

You have 147 lines of code in your program. It is very complex running backwards and forward through functions when it can be handled with straight forward logic. Below is an unfinished simple example of just 18 lines of code that handles pretty much all of the foundation without the need to define functions. It includes list comprehension and a just few conditional catch statements.

import random

words = ['cheese', 'biscuits', 'hammer', 'evidence']
word_choice = [i.lower() for i in random.choice(words)]
lives = 3
cor_guess = []
while True:
print('Letters found: {}\nWord length: {}'.format(cor_guess, len(word_choice)))
if lives == 0:
break
guess = str(input('Guess a letter: '))
if guess in word_choice:
print(guess, 'is in the word!\n')
cor_guess.append(guess)
else:
lives -= 1
...
...


With print you do not need to add extra print line to get another blank line, you can use triple quotation marks and place the information where ever you like. For instance:

print('''
lives = {}
#hangman picture#

letters found: {}
'''.format(lives, letters_found)