Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Python Collections (2016, retired 2019) Sets Out Of This Word Game

The Better with Set(ter) Letter Game: Following along with video and receiving syntax error

Hello. i am trying to follow along with kenneth but when I execute the following code:

from __future__ import print_function
import random
import os
import sys

def clear():
    if os.name == 'nt': #windows
        os.system('cls')
    else: #everything else
        os.system('clear')


def draw(bad_guesses, good_guesses, secret_word):
    print('Strikes: {}/7'.format(len(bad_guesses)))
    print('')

    for letter in bad_guesses:
        print(letter, end=' ')
    print('\n\n')

    for letter in secret_word:
    #if they've already guessed the letter, show that instead of blank (end allow it all concurrently on same line)
        if letter in good_guesses:
            print(letter, end='')
        else:
            print('_', end='')

limit = 7
#make list of words
words = [
    'apple',
    'banana',
    'orange',
    'coconut',
    'strawberry',
    'lime',
    'grapefruit',
    'lemon'
    'kumquat',
    'blueberry',
    'melon'
]

def play(done):
    clear()
    secret_word = random.choice(words)
    bad_guesses = set()
    good_guesses = set()
    word_set = set(secret_word)

    while True:
        draw(bad_guesses, good_guesses, secret_word)
        guess = getGuess(bad_guesses | good_guesses)

        if guess in word_set:
            good_guesses.add(guess)
            if not word_set.symmetric_difference(correct):
                print("You win!")
                print("The secret word was {}".format(secret_word))
                done = True
        else:
            bad_guesses.add(guess)
            if len(bad_guesses) == limit:
                    draw(bad_guesses,good_guesses,secret_word)
                    print("You lost!")
                    print("The secret word was {}".format(secret_word))
                    done = True
        if done:
            play_again = raw_input("Play again? Y/N").lower()
            if play_again != 'n':
                return play(done=False)
            else: 
                sys.exit()

def welcome():
    start = raw_input("Press enter/return to start or Q to quit ").lower()
    if start == 'q':
        print("Bye!")
        sys.exit()
    else:
        return True
def getGuess(guess):
    guess = raw_input(" Guess a letter: ").lower()

    if len(guess) != 1:
        print("You must guess a single letter!")
    elif guess in bad_guesses or guess in good_guesses:
        print("You've already guessed that letter!")
    elif not guess.isalpha():
        print("You can only guess Alpha characters/letters!")

    return guess
print('Welcome to Letter Guess!')

done = False

while True:
    clear()
    welcome()
    play(done)

I receive the following errors:

  File "letter_game.py", line 103, in <module>
    play(done)
  File "letter_game.py", line 56, in play
    guess = getGuess(bad_guesses | good_guesses)
  File "letter_game.py", line 90, in getGuess
    elif guess in bad_guesses or guess in good_guesses:
NameError: global name 'bad_guesses' is not defined

Any insight into what I may have done incorrectly would be greatly appreciated.

Thanks in advance!

2 Answers

Hi there!

Ah just a couple of things! First one's just a habit learned from your previous python experience I believe? raw_input in python 2 is now called input in python 3, so you just have to delete the "raw_" part of your input calls to run on 3.. unless you were aiming for 2 :) There's also an undefined name problem in this line:

if not word_set.symmetric_difference(correct):

As far as I can see, that's the only time correct is mentioned in the file.

Now down to the error at hand. It's a scoping problem. In python, a function only has a few places it can look for a variable name you tell it about. It can look in the parameters in it's definition, if it's being declared inside another function it can look inside the parent function, and it can look in global. It can't look inside another function that's not it's parent - it just wont know they exist. This is very useful and allows us to use helpful names like "output" or "i" in hundreds of functions in the same program without a terrible mess happening. The problem is that if python can't find the variable you're talking about, it will do one of two things. If you're assigning a value to that variable, it will make a new variable which is scoped (only exists) inside that function. If you're trying to access the variable ( print it, compare it to something, etc) then python has nothing to access and raises a NameError telling you the variable you are trying to use is undefined.

Here's a slightly confusing example:

first_name = "Bob"
middle_name = "Robert"
family_name = "Robertson"

def cousin():
    cousin_name = "Ruth"

def print_name():
    middle_name = "Thomas"

    def inner():
        first_name = "James"

        def really_inner():
            print(first_name)
            print(middle_name)
            print(family_name)
            print(cousin_name)

        really_inner()

    inner()

print_name()

Here we have three global variables, the first, middle and family name of a guy named bob. We then have two global functions cousin_name and print_name. If you consider the really_inner function, when that is run, it doesn't know about any of the variables it has to print, so it looks outside itself. It Finds a value for first_name in it's parent function, and a value for middle_name in it's parent's parent, it even finds the family name in the global scope, however it can't see the cousin_name variable in the cousin() function, so the output will look like this:

James
Thomas
Robertson
Traceback (most recent call last):
  File "python", line 24, in <module>
  File "python", line 22, in print_name
  File "python", line 20, in inner
  File "python", line 18, in really_inner
NameError: name 'cousin_name' is not defined

Remember that none of the global values for Bob's names are changed here. Each function creates a new locally scoped variable with the same name. In python if a global variable holds a mutable value, like list, set, dict then you can change values within that collection inside the function, but you can't assign a new value to the variable without using the global keyword. for example:

immutable_variable = "Original Value"
mutable_variable = ["original values", "in", "list"] 

def get_values():
    for each in immutable_variable, mutable_variable:
        print(each)

def set_values(value):
    mutable_variable[0] = value
    immutable_variable = value  

def set_global_values(value):
    global immutable_variable
    global mutable_variable
    mutable_variable = value
    immutable_variable = value

get_values()
set_values("New Value")
get_values()
set_global_values("Global Variable Overiden!")
get_values()

Hope it helps! :)

Thank you very much for the thorough explanation and the examples. I should have specified in the post but i'm currently using python 2.7, since it was installed with Anaconda (so that I could access the mySQL connector - which appears to be impossible for Python 3.4 on Windows 10 64-bit!!!). I was able to make the appropriate changes (change correct to good_guesses and pass the appropriate arguments in my function definition) and the program now works.

Thank you for sample program that shows the scope. That makes perfect sense and I think I now have a better understanding of immutable and mutable variables (can be changed rather than just replaced and can be changed globally from within a function). I don't fully understand why a variable is immutable/mutable as far as the datatype architecture goes but I think I understand the impact of a variable being mutable/immutable.

Thanks again!

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,425 Points

The traceback gives all the info you need:

  File "letter_game.py", line 90, in getGuess
    elif guess in bad_guesses or guess in good_guesses:
NameError: global name 'bad_guesses' is not defined

Line 90... 'bad_guesses' is not defined

bad_guesses is defined in the function play. If you want to refer to it in the function getGuess, you'll need to pass it as an argument in the function call.

Thank you very much! I was able to rewrite the function definition so that it could accept bad_guesses and good_guesses parameters.

Thanks!