3
\$\begingroup\$

I have completed the Hangman game. Can somebody look at the code and see what I've done wrong? What should I add to improve it? Are there any bugs in it?

import random 
    
word_list = ['sleep','window']
get_word = random.randint(0,len(word_list)-1)
get_word = word_list[get_word]
result = ["_"] * len(get_word)
x = "_" * len(get_word)
print(x)
attempts = 5
guessed = []
missed = []
while attempts != 0:
    print("Enter a letter: ")
    guess = input()
    if len(guess) == 1:
        for index,value in enumerate(get_word):
            if guess == value:
                result[index] = value
                print("You guessed right")
        else:
            if guess not in get_word:
                attempts -= 1 
                missed.append(guess)
                print("Sorry the letter ", guess , "is not in the word." ,"Now you have" , attempts , "attempts")
                if attempts == 0:
                    print("You didn't guess the word.The word was" , get_word)
    print("".join(result),"\nMissed letters are: ", missed , "\t")

    if "_" not in result:
        result = "".join(result)
        if result == get_word:
            print("Congratulations! You guessed all the letters")
            break
\$\endgroup\$
1
  • \$\begingroup\$ Considering the PEP8, I you should not use indentation between import random and the first code. \$\endgroup\$ Sep 10 at 15:14

1 Answer 1

2
\$\begingroup\$

PEP-8

First off, as mentioned by GregOliveira in the comments, by PEP-8 you don't need the entire program indented (and indeed, it won't run as written). You do, however, need spaces before/after operators, after commas, etc. Getting a linter such as pylint or flake8 will highlight these and make writing good code easier.

Also, your variable names are not terribly clear get_word (sounds more like a verb) might be target_word for example.

Handling input

Because we're taking input from the user, we want to make sure we're handling it appropriately. Currently you are testing the bare input and merely checking whether it is 1 character. We need to consider carefully about what we consider valid input and not, for example, punish the player for entering an upper-case letter, entering the same guess multiple times, entering a character which isn't a letter at all.

Instead of:

guess = input("Enter a letter: ")
if len(guess) == 1:

We could do:

from string import ascii_lowercase  # Or we could define our own set of valid characters
[...]

while True:
    guess = input("Enter a letter:")
    guess = guess.lower()  # Handle capital letters by downcasing all of them
    if len(guess) != 1:
        print(f"Invalid guess '{guess}', must be a single character")
    elif guess not in ascii_lowercase:
        print(f"Invalid guess '{guess}', must be one of {', '.join(ascii_lowercase)}")
    elif guess in missed or guess in result:
        print(f"Invalid guess '{guess}', already guessed.")
    else:
        break

N.B. This is also a good opportunity to use a function.

Python Functions

The Python module random contains the random.choice function (try running the help(random) function in the Python shell to see what functions things have). This function selects one element from a sequence (i.e. your list), so:

get_word = random.randint(0, len(word_list)-1)
get_word = word_list[get_word]

Becomes

get_word = random.choice(word_list)

You also have what appears to be some remnants of old code floating around. E.g.

x = "_" * len(get_word)
print(x)

Where later, you use the more Pythonic:

print("".join(result),"\nMissed letters are: ", missed , "\t")
      ^
      "".join(result)

The input function takes a prompt argument:

input(prompt=None, /)

Read a string from standard input.  The trailing newline is stripped.

The prompt string, if given, is printed to standard output without a
trailing newline before reading input.

If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if available.

Instead of:

print("Enter a letter: ")
guess = input()

We can simply use

guess = input("Enter a letter: ")

Functionality

In this part:

for index,value in enumerate(get_word):
    if guess == value:
        result[index] = value
        print("You guessed right")
else:

You align the else with the for which is probably not what you intend. else on the for is executed if the for loop exits naturally rather than through a break statement. Which in your case it always does. What you probably intend is:

if guess in get_word:
   print("You guessed right")  # This also allows us to avoid printing this multiple times for each time the character appears in the word
   for index,value in enumerate(get_word):
        if guess == value:
            result[index] = value

else:
    attempts -= 1
    missed.append(guess)
    print("Sorry the letter ", guess , "is not in the word." ,"Now you have" , attempts , "attempts")
    if attempts == 0:
        print("You didn't guess the word.The word was" , get_word)

However, we can use this to test whether we have used up all our attempts (since we break on success):

while attempts > 0:
    [...]
    <if correct>:
       break
else:
    print("You didn't guess the word. The word was" , get_word)

Cleaning up

We also don't really need to check whether _ is in our result before testing its validity, we can just straight up test whether we have the right answer.

We can also use f-strings to clean up some of the print statements.

print("Sorry the letter ", guess, "is not in the word.", "Now you have", attempts, "attempts")

Becomes:

print(f"Sorry the letter {guess} is not in the word. You have {attempts} attempts remaining.")

Putting it all together

If we put all these things together, we end up with something that looks like:

"""
Play a simple hangman game
"""

import sys
import random
from string import ascii_lowercase


WORD_LIST = ['sleep', 'window']


def get_user_guess() -> str:
    while True:
        guess = input("Enter a letter:").lower()
        if len(guess) != 1:
            print(f"Invalid guess '{guess}', must be a single character")
        elif guess not in ascii_lowercase:
            print(f"Invalid guess '{guess}', must be one of {', '.join(ascii_lowercase)}")
        elif guess in missed or guess in result:
            print(f"Invalid guess '{guess}', already guessed.")
        elif not guess:  # Handle user giving up
            to_quit = input("Giving up? [y/N]")
            if to_quit.lower() == "y":
                sys.exit()
        else:
            break

    return guess


target_word = random.choice(WORD_LIST)
result = ["_"] * len(target_word)
print("".join(result))

attempts = 5
guessed = []
missed = []

while attempts > 0:
    guess = get_user_guess()
    if guess in target_word:
        print("You guessed right")
        for index, value in enumerate(target_word):
            if guess == value:
                result[index] = value
    else:
        attempts -= 1
        missed.append(guess)
        print(f"Sorry the letter {guess} is not in the word."
              f"You have {attempts} attempts remaining.")

    print("".join(result))

    if "".join(result) == target_word:
        print("Congratulations! You guessed all the letters")
        break

    print(f"Missed letters are: {', '.join(missed)}.")

else:
    print(f"You didn't guess the word. The word was {target_word}.")

\$\endgroup\$
2
  • \$\begingroup\$ For asking user to input the letter i wanted to used a function but i coudn't get the guess from the function i tried to use global variable but thank you a lot! \$\endgroup\$
    – mcccuklev
    Sep 11 at 12:49
  • 1
    \$\begingroup\$ return is what you're looking for, which you can imagine as "set the name of this function to this/these values and leave" \$\endgroup\$ Sep 11 at 14:22

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.