17
\$\begingroup\$

I just started learning about Python at school and decided to start this little project with the basic knowledge I have. Most of the code is pretty lengthy and I know there are probably more efficient ways of doing it so Im looking for any improvements and tips that will help my code.

#Python Game - Hangman

#Imports
import random
import string
import sys

#Custom Functions
def Lives(Incorrect): #This function tells the player how many lives they have left when called
    LivesLeft = IncorrectLimit - Incorrect
    print("You have", LivesLeft, "lives left.")

def PicNo(Incorrect): #This function prints the hangman picture to the corresponding incorrect guesses
    if Incorrect == 1: 
        print(Pic1)
    if Incorrect == 2:
        print(Pic2)
    if Incorrect == 3:
        print(Pic3)
    if Incorrect == 4:
        print(Pic4)
    if Incorrect == 5:
        print(Pic5)
    if Incorrect == 6:
        print(Pic6)
    if Incorrect == 7:
        print(Pic7)

def Hint(Incorrect, WordChoice): #This function prints a hint when the player gets down to their last two guesses
    if Incorrect == 6:
        WordChoice = list(WordChoice)
        WordHint = random.choice(WordChoice)
        WordChoice = "".join(WordChoice)
        print("Hint - the word contains the letter: ", WordHint)

#Constants
List1 = ["fable","surf","english","paris","flower","laptop","sand","classroom", "blonde", "star", "blue", "hat", "snow", "tennis", "gate", "palm","internet","bricks", "bird", "mountain", "crate", "jumper", "whiteboard", "teacher", "table", "paving", "vehicle", "roundabout", "grass", "window", "cat", "dog", "bag", "bycicle", "jewellery", "headphones"] #These are all the words that can possibly be taken
IncorrectLimit = 7 #The number of guesses a player can get incorrect
Infinity = 9999999999999999999999999999999999 #An amount set for for loops
Pic1 = ('''
  +---+
  |   |
      |
      |
      |
      |
=========''')
Pic2 = ('''
  +---+
  |   |
  O   |
      |
      |
      |
=========''')
Pic3 = ('''
  +---+
  |   |
  O   |
  |   |
      |
      |
=========''')
Pic4 = ('''
  +---+
  |   |
  O   |
 /|   |
      |
      |
=========''')
Pic5 = ('''
  +---+
  |   |
  O   |
 /|\  |
      |
      |
=========''')
Pic6 = ('''
  +---+
  |   |
  O   |
 /|\  |
 /    |
      |
=========''')
Pic7 = ('''
  +---+
  |   |
  O   |
 /|\  |
 / \  |
      |
=========''') #The hangman pictures

#Variables
Incorrect = 0 #The incorrect count starts at 0
List2 = [] #List2 starts empty

#Start - Menu
print("PYTHON HANGMAN") #Startup title and menu
print("Coded by - -\n")

input("Press \"Enter\" to start a game." + "\n")

#GAME START

#Processing
for i in range(Infinity):
    WordChoice = (random.choice(List1)) #Randomly chooses a word from List 1
    LetterCount = len(WordChoice) #Returns the amount of letters in the chosen word
    print("<-----[NEW GAME]----->\n")
    print("The word is", LetterCount, "letters long.") #Prints the letter count of the word
    if LetterCount == 1: #Prints the 'mystery' words length with underscores and spaces
        L = ("_")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 2:
        L = ("__")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 3:
        L = ("___")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 4:
        L = ("____")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 5:
        L = ("_____")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 6:
        L = ("______")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 7:
        L = ("_______")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 8:
        L = ("________")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 9:
        L = ("_________")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 10:
        L = ("__________")
        print(L.replace("", " ")[1: -1], "\n")

    GuessList = list(L) #Sets the chosen mystery underscore string to a list
    LivesLeft = IncorrectLimit - Incorrect #Calculates the lives left

    for i in range(Infinity): #Loops the following indented code
        if GuessList == WordChoice:
            break

        Hint(Incorrect, WordChoice) #The hint custom function

        for i in range(Infinity): #Loops the guessing process
            Guess1 = input("Guess a letter: ") #The players letter guess
            GuessLength = len(Guess1)
            if GuessLength > 1:
                print("Please enter only one letter.") #Repeats the guessing process if there is more than one letter
            elif Guess1 == "":
                print("Please enter a letter.") #Repeats the guessing process if there is not input
            else:
                break

        Guess1 = Guess1.lower() #Turns the input into a lowercase string
        List2.append(Guess1) #Adds the guess to List2

        if WordChoice.find(Guess1) >= 0: #A boolean that finds if the guess is contained in the word
            GuessList = list(GuessList) #Sets GuessList to a list
            for x, y in enumerate(WordChoice): #Replaces the underscore from GuessList with the correct corresponding letter 
                if Guess1 == y:
                    GuessList[x] = y
            print("The letter", Guess1.upper(), "was correct.") #Prints a message telling the player they were correct
            GuessList = "".join(GuessList) #Sets GuessList back to a string

        else:
            Incorrect += 1 #Increases Incorrect up 1 everytime the player is incorrect
            print("Incorrect.")
            GuessList = "".join(GuessList) #Sets GuessList back to a string
            PicNo(Incorrect) #The PicNo custom function
            Lives(Incorrect) #The Lives custom function

        List2 = "".join(List2) #Sets List2 to a string
        print("Guessed Letters: ",List2.upper()) #Prints the players guessed letters
        List2 = list(List2) #Sets List2 back to a list
        List2.append(", ") #Adds a comma and space to List2 for formatting
        print(GuessList.replace("", " ")[1: -1], "\n") #Prints the GuessList with spaces

        if GuessList == WordChoice: #Determines if the player has won the game
            print("Well done, you guessed the correct word!")
            Incorrect = 0 
            List2 = []
            GuessList = []
            NewGame = input("Would you like to play Hangman again? [Y]es, [N]o: \n")
            if NewGame == "Y":
                break
            else:
                sys.exit()

        if Incorrect == 7: #Determines if the player has lost the game
            print("Game over.")
            print("The word was: ", WordChoice + ".")
            Incorrect = 0 
            List2 = []
            GuessList = []
            NewGame = input("Would you like to play Hangman again? [Y]es, [N]o: \n")
            if NewGame == "Y":           
                break
            else:
                sys.exit()
\$\endgroup\$
  • 9
    \$\begingroup\$ @t3chb0t its not that hard to understand, its hangman... Not only did he actually tell you "What it is supposed to do" he also commented extensively and pretty plainly showed you "how it works". \$\endgroup\$ – opa Jul 26 '17 at 16:22
23
\$\begingroup\$

Welcome to Python, and welcome to Code Review! Here are some basic tips to help you along.

0.5. EDIT: I think you also have an unused import. import string is not needed.

  1. Consider using a style guideline like PEP8. This is more of a general suggestion and will make your future code much more readable and maintainable.

  2. Consider defining all your constants at the top of the file. This makes them easier to find. In other languages, you cannot use a variable before you define it, so this is a good habit to get used to as well.

  3. Python is a self-documenting language. Generally, in Python and other languages, comments should not be used to state the obvious. Use comments to explain algorithms or non-trivial aspects of the code.

    #Custom Functions
    def Lives(Incorrect): #This function tells the player how many lives they have left when called
    

    This is not a very useful comment.

  4. Consider refactoring some commonly used functions:

    def PicNo(Incorrect): #This function prints the hangman picture to the corresponding incorrect guesses
        if Incorrect == 1: 
            print(Pic1)
        if Incorrect == 2:
            print(Pic2)
        if Incorrect == 3:
            print(Pic3)
        if Incorrect == 4:
            print(Pic4)
        if Incorrect == 5:
            print(Pic5)
        if Incorrect == 6:
            print(Pic6)
        if Incorrect == 7:
            print(Pic7)
    

    Generally, this is a bad idea and goes against the principle of DRY. An alternative is using a Python dictionary/list to store the hangman states. For example:

    PICS = ['''
      +---+
      |   |
          |
          |
          |
          |
    =========''', '''
      +---+
      |   |
      O   |
          |
          |
          |
    =========''', ... # et cetera
    
    # Accessing it
    print(PICS[0])
    
    def PicNo(Incorrect):
        print(PICS[Incorrect])
    # This function is a bit of a redundancy and you shouldn't need it.
    
  5. Wording?

    def Hint(Incorrect, WordChoice): #This function prints a hint when the player gets down to their last two guesses
        if Incorrect == 6:
    

    Do you mean:

    if Incorrect >= 6:
    
  6. Consider selecting random words from a text file instead.

    List1 = ["fable", ....]
    

    Try to pick a more description variable name. Also, you shouldn't store large constants like this in your program. What you can do is store them in a text file and read them in from the program.

    words.txt:
    fable
    surf
    english
    paris
    ...
    
    
    Code:
    with open("words.txt") as f:
        List1 = f.read().strip().split('\n')
    

    Look up file manipulation in Python.

  7. ???

    Infinity = 9999999999999999999999999999999999
    

    LOL. Technically not infinity, your program would theoretically end if I played until the heat death of the universe. It seems like you're using it for a infinite loop. Why don't you use a while loop instead? An infinite while loop looks like this:

    while True:
        # code in here will loop infinitely
        break # This statement exits the loop
    
  8. Descriptive variable names

    List2 = []
    

    Again, pick a more useful variable name. You want to be able to come back to this in 5 years and still understand what is going on.

  9. Don't repeat yourself.

    if LetterCount == 1: #Prints the 'mystery' words length with underscores and spaces
        L = ("_")
        print(L.replace("", " ")[1: -1], "\n")
    if LetterCount == 2:
        L = ("__")
        print(L.replace("", " ")[1: -1], "\n")
    ...
    

    You don't need parentheses when declaring a string.

    L = "_"
    

    Furthermore, you can achieve this with the following code:

    L = "_" * LetterCount
    

    This shortens your code to:

    if LetterCount >= 1 and LetterCount <= 10:
        L = "_" * LetterCount
        print(L.replace("", " ")[1: -1], "\n")
    

    You should feel suspicious any time you're repeating the same code over and over. Consider refactoring into another function whenever that happens.

  10. Avoid using sys.exit() to exit your programs. It's like killing your program with a chainsaw. Use a return or break to end the code flow. It's good practice to wrap your main code in a main() function or if __name__ == '__main__': guard.

Python is a great language and you're off to a good start. Keep at it and happy coding.

\$\endgroup\$
  • 1
    \$\begingroup\$ About #7: In Python 2, it would take until the heat death of the universe before you can start playing. (Assuming you have several Yottabytes of swap.) // Python 2's range() returns a list, while Python 3's range() and Python 2's xrange are implemented as generators. \$\endgroup\$ – Oskar Skog Jul 26 '17 at 15:53
  • \$\begingroup\$ Ah yes that's right. I assumed Python 3 because OP used Python 3 style print statements. \$\endgroup\$ – omgimanerd Jul 26 '17 at 18:26
  • \$\begingroup\$ I know, I just wanted to point out. Writing Python code that works with both Python 2 and 3 is relatively easy, I usually do this myself. If you pay just a little attention to various small details, your code will work both on modern systems where 3 is the default version and on legacy systems where Python 3 is unavailable. // PS. Python 3 does not have a print statement, it's a function. And that syntax works in Python 2 too. \$\endgroup\$ – Oskar Skog Jul 26 '17 at 19:19
  • 1
    \$\begingroup\$ Most people using Python 2 without the need for forwards compatibility would not use parentheses simply because it's easier to type. I know for quick one-off scripts, I'll prefer writing it that way. \$\endgroup\$ – omgimanerd Jul 26 '17 at 20:40
  • \$\begingroup\$ I know. Just wanted to point out about range. :-) \$\endgroup\$ – Oskar Skog Jul 26 '17 at 22:13
4
\$\begingroup\$

Temporary review, I'll try to improve it.

Style

Python has a style guide called PEP 8. It is definitly worth reading and following (except if you have good reasons not to).

You'll find various tools to check your code compliancy to PEP 8.

Among the major "problems" in your code, variable should be lower_case_with_underscores.

To infinity and beyond

for i in range(Infinity): could and should be written while True:.

If you actually need an infinite counter, you can use itertools.count

Regarding "Prints the 'mystery' words length with underscores and spaces

You are using multiple multually exclusive conditions, you could use elif.

Also, you are using useless parentheses, you could write L = "____".

Instead of writing the different strings with n underscores, you could write : L = "_" * LetterCount.

You could use join to add spaces between the underscores instead of using replace and slice selection.

Thus, the whole code becomes:

print(" ".join('_' * LetterCount), "\n")
\$\endgroup\$
4
\$\begingroup\$

Both answer are nice and cover everything, just one thing for your def PicNo. Most of the time, if i have more than 3 if, i'll just go for a switch with a dictionary :

def PicNo(Incorrect):
    switcher = {
        0: '''
  +---+
  |   |
      |
      |
      |
      |
========='''
        1: "2nd pic",
        2: "3rd pic",
    }
    return switcher.get(Incorrect, "Default pic")
\$\endgroup\$
  • 2
    \$\begingroup\$ In the future you should pay more attention to the code in the code block. (I've edited for you this time.) // I've heard that the easiest way to insert code is to paste it, highlight it and then press Ctrl+K. \$\endgroup\$ – Oskar Skog Jul 26 '17 at 15:55
  • \$\begingroup\$ ooooooh I wasn't aware of that. Thanks! I was always using the arrows then "space space space space" like a monkey \$\endgroup\$ – pwnsauce Jul 27 '17 at 11:27
2
\$\begingroup\$
  • All your variables and functions' names do not respect the naming conventions as described here.
  • Your comments are too long, so it is better to write them just before the corresponding line of code and span them on multiple short lines if necessary.
  • Those multiple if statements can be refactored using a dictionary (as described here, for example)
\$\endgroup\$

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.