4
\$\begingroup\$

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:
            print('Please enter a valid character')
        elif is_not_one_letter:
            print('Please enter one letter at a time')
        else:
            print('You already guessed that letter')
        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?

\$\endgroup\$
1
  • \$\begingroup\$ 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. \$\endgroup\$
    – Raphael
    Jan 7, 2020 at 9:21

2 Answers 2

1
\$\begingroup\$

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:
        print('Please enter a valid character')
    elif is_not_one_letter:
        print('Please enter one letter at a time')
    else:
        print('You already guessed that letter')
    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:
            guessed_chars.add(guess)
            if guess in secret_word:
                correct_chars.add(guess)
                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? :)

\$\endgroup\$
1
  • \$\begingroup\$ 10/10 for the answer man. \$\endgroup\$
    – Elbasel
    Jan 7, 2020 at 20:19
0
\$\begingroup\$

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:
      print('Your dead homie!\nThe word was: ', word_choice)
      break
  guess = str(input('Guess a letter: '))
  if guess in word_choice:
      print(guess, 'is in the word!\n')
      cor_guess.append(guess)
  else:
      print('Your dying slowly!\n')
      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)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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