6
\$\begingroup\$

I created this version of Hangman in Python 3. Does anyone have any tips for optimization or improvement?

import random
HANGMAN = (
'''
-------+''',
'''
       +
       |
       |
       |
       |
       |
-------+''',
'''
  -----+
       |
       |
       |
       |
       |
-------+''',
'''
  -----+
  |    |
       |
       |
       |
       |
-------+''',
'''
  -----+
  |    |
  O    |
       |
       |
       |
-------+''',
'''
  -----+
  |    |
  O    |
  |    |
       |
       |
-------+''',
'''
  -----+
  |    |
  O    |
 /|    |
       |
       |
-------+''',
'''
  -----+
  |    |
  O    |
 /|\   |
       |
       |
-------+''',
'''
  -----+
  |    |
  O    |
 /|\   |
 /     |
       |
-------+''',
'''
  -----+
  |    |
  O    |
 /|\   |
 / \   |
       |
-------+''')
MAX = len(HANGMAN) - 1
WORDS = ('jazz', 'buzz', 'hajj', 'fuzz', 'jinx', 'jazzy', 'fuzzy', 'faffs', 'fizzy', 'jiffs', 'jazzed', 'buzzed', 'jazzes', 'faffed', 'fizzed', 'jazzing', 'buzzing', 'jazzier', 'faffing', 'fuzzing')
sizeHangman = 0
word = random.choice(WORDS)
hiddenWord = list('-' * len(word))
lettersGuessed = []
print('\tHANGMAN GAME\n\t\tBy Lewis Cornwall\n')
while sizeHangman < MAX:
    print('This is your hangman:' + HANGMAN[sizeHangman] + '\nThis is the word:\n' + ''.join(hiddenWord) + '\nThese are the letters you\'ve already guessed:\n' + str(lettersGuessed))
    guess = input('Guess a letter: ').lower()
    while guess in lettersGuessed:
        print('You\'ve already guessed that letter!')
        guess = input('Guess a letter: ').lower()
    if guess in word:
        print('Well done, "' + guess + '" is in my word!')
        for i in range(len(word)):
            if guess == word[i]:
                hiddenWord[i] = guess
                if hiddenWord.count('-') == 0:
                    print('Congratulations! My word was ' + word + '!')
                    input('Press <enter> to close.')
                    quit()
    else:
        print('Unfortunately, "' + guess + '" is not in my word.')
        sizeHangman += 1
    lettersGuessed.append(guess)
print('This is your hangman: ' + HANGMAN[sizeHangman] + 'You\'ve been hanged! My word was actually ' + word + '.')
input('Press <enter> to close.')
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Not an optimization, but you could load your words from a - possibly encrypted - text file instead of having them in your raw code :) \$\endgroup\$ – Morwenn Mar 11 '13 at 13:22
6
\$\begingroup\$

You can avoid repeating guess = input('Guess a letter: ').lower() by changing the inner while loop to:

while True:
    guess = input('Guess a letter: ').lower()
    if guess in lettersGuessed:
        print('You\'ve already guessed that letter!')
    else:
        break

Instead of for i in range(len(word)) use:

for i, letter in enumerate(word):

You could also avoid indexing by i altogether by using a list comprehension. I'm not sure if this is as clear in this case, though.

hiddenWord = [guess if guess == letter else hidden for letter, hidden in zip(word, hiddenWord)]

Instead of hiddenWord.count('-') == 0 use:

'-' not in hiddenWord

The docs say quit() should not be used in programs. Your count('-') == 0 check is unnecessarily inside the for i loop. If you move it after the loop, you could use break to exit the main loop. You could add an else clause to the main with statement for dealing with the "You've been hanged" case. The main loop becomes:

while sizeHangman < MAX:
    print('This is your hangman:' + HANGMAN[sizeHangman] + '\nThis is the word:\n' + ''.join(hiddenWord) + '\nThese are the letters you\'ve already guessed:\n' + str(lettersGuessed))
    while True:
        guess = input('Guess a letter: ').lower()
        if guess in lettersGuessed:
            print('You\'ve already guessed that letter!')
        else:
            break
    if guess in word:
        print('Well done, "' + guess + '" is in my word!')
        for i, letter in enumerate(word):
            if guess == letter:
                hiddenWord[i] = guess
        if '-' not in hiddenWord:
            print('Congratulations! My word was ' + word + '!')
            break
    else:
        print('Unfortunately, "' + guess + '" is not in my word.')
        sizeHangman += 1
    lettersGuessed.append(guess)
else:
    print('This is your hangman: ' + HANGMAN[sizeHangman] + 'You\'ve been hanged! My word was actually ' + word + '.')
input('Press <enter> to close.')
\$\endgroup\$
3
\$\begingroup\$

Some ideas:

  • You might want to make sure that the guess is actually a single letter.

  • lettersGuessed should be a set, so membership tests (guess in lettersGuessed) is a constant time operation.

  • Similarly, you could store either the (correctly) guessed letters or the letters that still have to be (correctly) guessed in a set. For example:

    word = random.choice(WORDS)
    requiredGuesses = set(word)
    lettersGuessed = set()
    
    # then later
    if guess in lettersGuessed:
        print('You already guessed this')
    elif guess in requiredGuesses:
        print('Correct guess!')
        requiredGuesses.remove(guess)
        lettersGuessed.add(guess)
    else:
        print('Incorrect guess')
    

    This also makes the test if the whole word was guessed easier:

    if not requiredGuesses:
        print('You found the word')
    
  • Instead of keeping a hiddenWord which you keep adjusting whenever a correct guess is made, you could just create the hidden output on the fly:

    print(''.join('-' if l in requiredGuesses else l for l in word))
    
  • You might want to add a class for this game that encapsulates some of the game logic.

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

You should implement the ability to read words from a txt document. This can be done by:

f = open("wordlist.txt", 'w')
word_list.append(f.readlines())

this would allow you to have far bigger lists to choose from.

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