3
\$\begingroup\$

I have been using Python 3 to learn programming, and this is a very basic task, but I want to gain good programming patterns/habits at the very beginning.

import sys
import random

words_list = ['russia', 'china', 'usa', 'canada', 'ghana'] #pre-made list of our words to guess
accepted_input = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
word_guess = random.choice(words_list)
word_to_guess = []
for i in range(0, len(word_guess)):
    word_to_guess.append(word_guess[i])
currently = []
for i in word_to_guess:  # fill in our currently guessed list with "_"
    currently.append("_")
guess_letters = []
guesses = 0


#make sure the program will work if we run it using old versions of Python
if sys.version_info[0] < 3:
    input = raw_input

def hangman_graphic(guesses):
    print("")
    print("Currently you know: ", end=" ")
    for i in range(0, len(currently)):
        print(currently[i], end=" ")
    print("")
    print("You have already tried letters:", end=" ")
    for i in range(0, len(guess_letters)):
        print(guess_letters[i], end=" ")
    print("")
    if guesses == -1: # you won graphic
        print("       0      ")
        print("     ~~|~~    ")
        print("      / \     ")
        print("You just saved me!")
        print("You won!")
    elif guesses == 0:
        print("________      ")
        print("|      |      ")
        print("|             ")
        print("|             ")
        print("|             ")
        print("|             ")
    elif guesses == 1:
        print("________      ")
        print("|      |      ")
        print("|      0      ")
        print("|             ")
        print("|             ")
        print("|             ")
    elif guesses == 2:
        print("________      ")
        print("|      |      ")
        print("|      0      ")
        print("|     /       ")
        print("|             ")
        print("|             ")
    elif guesses == 3:
        print("________      ")
        print("|      |      ")
        print("|      0      ")
        print("|     /|      ")
        print("|             ")
        print("|             ")
    elif guesses == 4:
        print("________      ")
        print("|      |      ")
        print("|      0      ")
        print("|     /|\     ")
        print("|             ")
        print("|             ")
    elif guesses == 5:
        print("________      ")
        print("|      |      ")
        print("|      0      ")
        print("|     /|\     ")
        print("|     /       ")
        print("|             ")
    else:   # you lost graphic
        print("________      ")
        print("|      |      ")
        print("|      0      ")
        print("|     /|\     ")
        print("|     / \     ")
        print("|             ")
        print("The noose tightens around your neck, and you feel the")
        print("sudden urge to urinate.")
        print("GAME OVER!")

def guess_input():
    global guesses
    global currently
    global word_to_guess

    while True:
        user_input = input("Please input your guess (a single letter): ")
        if user_input in accepted_input:
            if user_input in guess_letters:
                print("You have already inputted this letter before, be more attentive!")
            else:
                guess_letters.append(user_input)
                if user_input in word_to_guess:
                    print("Well done, you guessed the right letter")
                    for i in range(0, len(word_to_guess)):
                        if word_to_guess[i] == user_input:
                            currently[i] = user_input
                else:
                    print("Ops, there is no such letter in our word")
                    guesses = guesses + 1

        else:
            print("INPUT ERROR! Please input single letter in range a-z")

        if guesses > 5: #game over you lost
            hangman_graphic(guesses)
            break
        elif "_" in currently: #keep playing
            hangman_graphic(guesses)
        else: #game over you WON
            hangman_graphic(-1)
            break


def hangman():
    hangman_graphic(guesses)
    guess_input()


if __name__ == "__main__":
    hangman()
    print("END!")
\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

Defining the list of words directly in the program makes it very difficult to add more words later. A better way would be to have a file holding them, and an even better way would be to grab the words off the internet. The best way would be to have a file with the words, and try to update the file from the internet. Of course, no internet would mean no extra words, but that's why you have the file as backup.


It's great that you have an accepted_input, but that isn't the best way to define it. A better way would be

from string import ascii_letters # as accepted_input

Note that Python 2 uses letters, not ascii_letters.


Speaking of Python 2, you have a slight problem with your backwards-compatability. Try running your file with Python 2, and see what happens. You get a syntax error on some of your print lines because end=... is invalid syntax for the print statement. The easy way to fix that is:

from __future__ import print_function

When you define word_to_guess, you are really just making a list of each character in the word. There is a much simpler way:

word_to_guess = list(word_guess)

As for the currently definition, use a list comprehension:

currently = ['_' for _ in word_to_guess]

(_ is a variable name that means basically that this variable isn't used.) Since a string is immutable, you could, more simply, do this:

currently = ['_'] * len(word_to_guess)

or

currently = list('_' * len(word_to_guess))

All the code I have mentioned so far (and the rest of the code before hangman_graphic) is out in the open. You should use an if __name__ == '__main__' block to prevent that code from executing if this module is imported.


A for i in ... loop goes through ... and assigns i to each item. That doesn't need to be numbers. Instead of getting all the indexes of a list, just get the items directly:

for char in currently:
    print(char, end=" ")

or, since all the items in currently are strings, use str.join():

print(" ".join(currently))

The same goes for guess_letters.


An easier and more portable way of displaying the graphics would be a list:

    graphics = [
"""
________      
|      |      
|             
|             
|             
|             """,
"""
________      
|      |      
|      0      
|             
|             
|             """,
...

Of course, you would fill out all the graphics (except the win and lose). You could then do something like:

if guesses == -1:  # Won game
    print("       0      ")
    ...
try:
    print(graphics[guesses])
except IndexError:  # Lost game
    print("________      ")
    print(...)
    ...

Global variables! Run before they kill you! Global variables are not the solution. I don't care what the problem is. If it's just one variable that you are changing, return it instead. If you absolutely, positively need global variables, use a class instead. You just don't need global variables. It gets messy when you use global variables. (Don't worry; I won't start complaining about the coming of a pony.)


You ask the user to type a single letter, but users don't always listen to good advice. You should make sure that it is a single letter. If it isn't, just tell the user the problem, and continue.


You have some nested if-else blocks. That isn't necessary. Instead, check if the letter is valid. You can have multiple elifs. That means you have if user_input in guess_letters: and else: with just one layer of nesting. The structure would be something like:

user_input = input(...).lower()
if len(user_input) != 1:
    print("Please input a single character.")
elif user_input not in accepted_input:
    print("Please input a letter in range a-z.")
elif user_input in guess_letters:
    print("You have already inputted this letter before.")
else:
    guess_letters.append(user_input)
    if user_input in word_to_guess:
        print("Well done; you guessed the right letter.")
        for i in range(len(word_to_guess)):
            if word_to_guess[i] == user_input:
                currently[i] = user_input
    else:
        print("Oops; there is no such letter in our word.")
        guesses += 1

Now doesn't that look cleaner?


Why does hangman() use guesses? We know that it should start with zero, don't we? Just use hangman_graphic(0).

\$\endgroup\$
0
\$\begingroup\$

I would avoid massive IF-ELIF structures by compressing the same end result into a simple function:

function graphics(guesses):
    guesses = guesses - 1 # Offset guesses integer
    hangman = " ________     \n\
               |             \n\
               |             \n\
               |             \n\
               |             "

    stateMod = [[19, "|"], [28, "0"], ...] # Add missing values

    if (guesses < len(stateMod) and guesses != -1):    
        for iteration in range(0, guesses):
            hangman =   hangman[:stateMod[iteration][0] - 1]]\ 
                        + stateMod[iteration][1]\
                        hangman[stateMod[iteration][0] + 1]:]

    print(hangman)

So what is happening is that we take the empty hanging tree, substitute a character in the hangman string with a character taken from the 2 dimensional array (where the number is the character position, followed by the character we use to replace an empty space).

Now all you need to do is call the function with the amount of guesses done and it will print you the updated picture. Don't do things by hand if you can iterate on existing base.

Zondo covered a lot of issues, see his answer.

\$\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.