Simple Python hangman game I've created a little hangman game. It's pretty simple, and not to exciting but I want to know if there's a way I could turn this little game into something more extravagant and more exciting. How can I take this delicate piece of crap machinery into something a little more fun and a bit more complicated? For example:


Can I turn the list that is shown into a single string?
Is there a better way to use a random word?
Am I using the for and while loop in the correct syntax?


Source:

from random import randint


WORD_LIST = {
    'rand_word_1': 'sergeanty',
    'rand_word_2': 'amphipneustic',
    'rand_word_3': 'hypochil',
    'rand_word_4': 'grus',
    'rand_word_5': 'depressed',
    'rand_word_6': 'distortedly',
    'rand_word_7': 'unselected',
    'rand_word_8': 'crakow',
    'rand_word_9': 'preendorsing',
    'rand_word_10': 'convenient'
}


HANGMAN = (
    """
    x-------x
    """,
    """
    x-------x
    |
    |
    |
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |       |
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |      /|\\
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |      /|\\
    |      /
    |
    """,
    """
    x-------x
    |       |
    |       0
    |      /|\\
    |      / \\
    |
    GAME OVER
    """
)


MAX = len(HANGMAN) - 1
num = randint(1, 10)
num_string = str(num)
words = 'rand_word_{}'.format(num_string)
WORD_TO_GUESS = WORD_LIST[words]
HIDDEN = ['_'] * len(WORD_TO_GUESS)
LETTERS_GUESSED = []


def begin_game():
    hang_size = 0
    print "\tHANGMAN!"
    word_arr = list(WORD_TO_GUESS)

    while hang_size &lt; MAX:
        print str(HIDDEN)
        user_guess = raw_input('Guess a letter dude: ')

        if user_guess in LETTERS_GUESSED:
            print 'You already guessed that.. PAY ATTENTION!'
            user_guess = raw_input('Guess a letter dude: ')

        if user_guess in word_arr:
            print "Yeah yeah.. We're all impressed.. It's in the word woohoo.."

            for num in range(len(word_arr)):
                if user_guess == word_arr[num]:
                    HIDDEN[num] = user_guess
                    if HIDDEN.count('_') == 0:
                        print 'You win! Finally you did something right!'
                        quit()

        else:
            print "{}.. Really? That's the best you can do.. Not in my word..".format(user_guess)
            hang_size += 1
            print HANGMAN[hang_size]


begin_game()
Overview

I can see that you are a beginner and that is totally fine! Some of the points I will be making might, sound a bit harsh and go over your head. If they do try to improve the parts of the code you do understand, and instead post a follow up question. 



LMGTFY / RTFM

Asking a question on a SE, site is a great way to learn. However another is searching for answers beforehand. In fact all three questions in your bullet list could be googled / found in the manual. 


Turn a list into a string Python
How do I randomly select an item from a list using Python?
When to use for and while loop


As you know CodeReview has it's own hangman tag -- since you tagged your own question with the tag. This means people have asked hangman related questions earlier, and you could have browsed these questions before posting your own. As an example, even I have asked a question about hangman. Here is another very similar question.

This is not to discredit you for asking, however drawing suggestions and inspiration from similar problems is a fantastic way to learn.



PEP 8 - Style Guide for Python Code

As to pick the lowest hanging fruits. Consistency is a key part of programming, as it greatly increases readability. It also tells a bit about the coders eye for detail. Lets' look at how you have named your global constants:

MAX
num 
num_string 
words 
WORD_TO_GUESS 
HIDDEN 
LETTERS_GUESSED 


Do you see the inconsistency? Pep 8 recommends the following naming-conventions:


CAPITALIZED_WITH_UNDERSCORES for constants
UpperCamelCase for class names
lowercase_separated_by_underscores for other names




General structure

A very rough sketch of how a standard python program should look like is as follows

import

global CONSTANTS
global VARIABLES

functions

classes

if __name__ == '__main__':

    main()


All of your code should be split into short simple functions, which each serve a single purpose. You should in general have as little code as possible floating around that is not part of a function. This includes minimizing the need for global variables and constants. There are some exceptions to this rule, however your code is not one of them. 



Some more pointers

As earlier answers stated you can change your dictionary word list into

WORD_LIST = [
    'sergeanty',
    'amphipneustic',
    'hypochil',
    'grus',
    'depressed',
    'distortedly',
    'unselected',
    'crakow',
    'preendorsing',
    'convenient'
]


As mentioned earlier you use too many global variables. This 

MAX = len(HANGMAN) - 1
num = randint(1, 10)
num_string = str(num)
words = 'rand_word_{}'.format(num_string)
WORD_TO_GUESS = WORD_LIST[words]
HIDDEN = ['_'] * len(WORD_TO_GUESS)
LETTERS_GUESSED = []


can be shortened into 

MAX = len(HANGMAN) - 1
word_to_guess = random.choice(WORD_LIST)
hidden = ['_'] * len(word_to_guess)
letters_guessed = set()


Where I have used the choice function from the random module. For an example of use see below


   &gt;&gt;&gt; import random
   &gt;&gt;&gt; a = ["Stack", "Overflow", "rocks"]
   &gt;&gt;&gt; print(random.choice(a))
   rocks



for a more detailed explanation see the second bullet point at the start. 

You might also have noticed that some of the variables are not CAPITALIZED that is because they do not need to be global variables, and can be moved into the main function.

def play_hangman():
    hang_size = 0
    word_to_guess = random.choice(WORD_LIST)

    hidden = ['_'] * len(word_to_guess)
    letters_guessed = set()


Note how I also conveniently changed the name of your main() function. Naming variables, functions and classes in programming is one of the more difficult things to do. A general rule of thumb is that the names should be clear, distinct and succinct in that order. As others have pointed out main() only achieves the third, which should be the last thing to worry about. 



Handling user input

 

Jokes aside as a rule you should never except the user to use the correct input syntax. Instead you should build in checks, exceptions to handle these errors.

For an example your code only checks once if I have tried the letter before.

if user_guess in LETTERS_GUESSED:
    print 'You already guessed that.. PAY ATTENTION!'
    user_guess = raw_input('Guess a letter dude: ')


One way to fix this is using a while loop instead

while user_guess in LETTERS_GUESSED:
    print 'You already guessed that.. PAY ATTENTION!'
    user_guess = raw_input('Guess a letter dude: ') 


What happens if i decide to try the letter banana or the letter ? Your function does nothing to handle it. What If I try the more common mistake to try A then a? All of this can be fixed by putting the get_user_input into it's own function

ALPHABETH = 'abcdefghijklmnopqrstuvwxyz'

def guess_letter(letters_guessed):
    while True:
        guess = raw_input('Guess a letter dude: ').lower()
        if guess not in ALPHABETH:
            print guess, 'is like totally not in the alphabet dude, try again!'
        elif guess in letters_guessed:
            print 'You already guessed {}.. PAY ATTENTION!'.format(guess)
        else:
            return guess


Do note that I did not have to convert ALPHABETH into a list. Python can compare strings just as well as lists. Also notice how I used Python string lower() method to ensure that the user input was all lowercase.



Continuing on

The next bit of your code could then be written as

while hang_size &lt; MAX:
    print str(hidden)
    user_guess = guess_letter(letters_guessed)
    letters_guessed.add(user_guess)
    if user_guess in word_to_guess:


Where we again used that Python can compare strings just fine. The add is used instead of append since letters_guessed is now a set and not a list. Why am I using sets, you ask?


  When you want to store some values which you'll be iterating over,
  Python's list constructs are slightly faster. However, if you'll be
  storing (unique) values in order to check for their existence, then
  sets are significantly faster.


For words as short as your the difference is negligible, however it is a good practice nevertheless.

for num in range(len(word_arr)):


This can be improved using Pythons enumerate() function that adds a counter to an iterable.

    for i, letter in enumerate(word_to_guess):
        print i, letter
        if user_guess == letter:
            hidden[i] = letter
    if hidden.count('_') == 0:
        print 'You win! Finally you did something right!'
        return 


I really see no point in turning the for-loop into a zip list. Yes, it is shorter. However for a novice it is much harder to understand what is going on.



Winning condition and return

The statement 

if HIDDEN.count('_') == 0:
    print 'You win! Finally you did something right!'
    quit()


Is weird. The if HIDDEN.count('_') == 0: is cryptic at best. A good practice is to check what you really want to check.. You are not interested in how many _'s are left, but rather if the user has guessed the correct word. This can be checked as follows

user_guessed_word_spaced = ' '.join(hidden)
word_to_guess_spaced = ' '.join(word_to_guess)
if user_guessed_word_spaced == word_to_guess_spaced:


At a first glance this might seem convoluted. However in the actual code word_to_guess_spaced = ' '.join(hidden) is only declared once. Secondly using a spaced string ' '.join() instead of ''.join() allows us to directly print the output at the start of the while-loop.

Your way to exit the function is very intuitive for a beginner, however it should be avoided like the plague for a beginner. A more natural way is to use return instead.


  quit raises the SystemExit exception behind the scenes.
  
  Furthermore, if you print it, it will give a message:

&gt;&gt;&gt; print (quit)
Use quit() or Ctrl-Z plus Return to exit
&gt;&gt;&gt;

  
  This functionality was included to help people who do not know Python.
  After all, one of the most likely things a newbie will try to exit
  Python is typing in quit.
  
  Nevertheless, quit should not be used in production code. This is
  because it only works if the site module is loaded. Instead, this
  function should only be used in the interpreter.


I have made some adjustments to accommodate for this. This includes having only a single return statement, as well as some better handling of printing the winning conditions.



There are more things to nitpick, which I did not try to fix/improve


You should always comment your code. You can never be sure you will understand your code 6months down the road. A common way to do this is with PEP 257 -- Docstring Conventions.
Your creation of strings could be improved. 
Your list of words is short, and a simple fix is to use a larger list from the internet. 
You could also make a short menu to congratulate the user, or ask if one wants to play again. 




Code

Using all of these small improvements leads to the following code.

import random


WORD_LIST = [
    'sergeanty',
    'amphipneustic',
    'hypochil',
    'grus',
    'depressed',
    'distortedly',
    'unselected',
    'crakow',
    'preendorsing',
    'convenient'
]


HANGMAN = (
    """
    x-------x
    """,
    """
    x-------x
    |
    |
    |
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |       |
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |      /|\\
    |
    |
    """,
    """
    x-------x
    |       |
    |       0
    |      /|\\
    |      /
    |
    """,
    """
    x-------x
    |       |
    |       0
    |      /|\\
    |      / \\
    |
    """
)


MAX = len(HANGMAN) - 1
ALPHABETH = 'abcdefghijklmnopqrstuvwxyz'


def guess_letter(letters_guessed):
    while True:
        guess = raw_input('Guess a letter dude: ').lower()
        if guess not in ALPHABETH:
            print guess, 'is like totally not in the alphabet dude, try again!'
        elif guess in letters_guessed:
            print 'You already guessed {}.. PAY ATTENTION!'.format(guess)
        else:
            return guess


def play_hangman():
    hang_size = 0
    word_to_guess = random.choice(WORD_LIST)
    word_to_guess_spaced = ' '.join(word_to_guess)

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

    letters_guessed = set()
    user_guessed_word_spaced = ' '.join(hidden)

    print "HANGMAN!"

    while hang_size &lt; MAX:

        print
        print user_guessed_word_spaced

        user_guess = guess_letter(letters_guessed)
        letters_guessed.add(user_guess)

        if user_guess in word_to_guess:

            print "\nYeah yeah.. We're all impressed.. {} is in the word woohoo..".format(user_guess)

            for i, letter in enumerate(word_to_guess):
                if user_guess == letter:
                    hidden[i] = letter

            user_guessed_word_spaced = ' '.join(hidden)
            if user_guessed_word_spaced == word_to_guess_spaced:
                print
                print word_to_guess
                break

        else:
            print "{}.. Really? That's the best you can do.. Not in my word..".format(user_guess)
            hang_size += 1
            print HANGMAN[hang_size]

    return user_guessed_word_spaced == word_to_guess_spaced


if __name__ == '__main__':

    is_winner = play_hangman()
    if is_winner:
        print '\nYou win! Finally you did something right!'
    else:
        print '\nGAME OVER'