Needing help with a while loop function call

I am struggling with getting the below function call to execute the way I need it to in the below while loop.

def is_word_guessed(secret_word, letters_guessed):

    secret_word_copy = list(secret_word[:])
    for e in secret_word_copy:
        if e not in letters_guessed:
            return(False)
        else:
            all(e)

Once I have guessed all letters correctly this returns None.
Until all letters are guessed correctly this returns True.
If I replace the all(e) with return(False). It returns false as soon as one input(e) is found in secret_word_copy.

It is intended to return a usable return such as None or False when all ‘e’ in secret_word_copy appear in letters_guessed, so that I can exit the loop. A clear condition.

I may simply be using all(e) wrong but as return(False) is not working as I thought it should, it has been the only way I have found to get a change in the return when I need it.

I clearly understand this may not be the most pythonic code, the cleanest code, or overall even remotely how you would like to see this code implemented. I simply wanted to try to get the functional result of this code on my own before looking at other examples of and polishing this but I’ve hit a roadblock on something probably all together simple. :sweat_smile:

I also tried it like this

while is_word_guessed(secret_word, letters_guessed) == True:

just to see if the change from True to None would break the loop.

Anyway, any help pertaining to the mentioned function call or its use within the code as a whole would be greatly appreciated!


import string
secret_word = 'apple'
letters_guessed = []

def is_word_guessed(secret_word, letters_guessed):

    secret_word_copy = list(secret_word[:])
    for e in secret_word_copy:
        if e not in letters_guessed:
            return(True)
        else:
            all(e)
            
def get_guessed_word(secret_word, letters_guessed):

    currect_guess = []
    secret_word_copy = list(secret_word[:])
    for e in secret_word_copy:
        if e in letters_guessed:
            currect_guess.append(e)
        else:
            currect_guess.append('_ ')
    print(''.join(currect_guess))
    
    
def get_available_letters(letters_guessed):

    for e in available_letters_copy:
        if e in letters_guessed:
            available_letters_copy.remove(e)
    print(("Available Letters: "),' '.join(available_letters_copy))

def hangman(secret_word):
    num_guesses = 6
    print("Welcome to the game Hangman!")
    print("I am thinking of a word that is", len(secret_word), "letters long.")
    print("You have", num_guesses,"guesses left.")
    while is_word_guessed(secret_word, letters_guessed):
        while num_guesses != 0:
            player_turn = input("Enter Guess: ")
            letters_guessed.append(player_turn)
            if player_turn not in secret_word:
                num_guesses -= 1
                print("Number of guesses remaining: ", num_guesses)
                get_available_letters(letters_guessed)
                get_guessed_word(secret_word, letters_guessed)
                print("I'm sorry, that letter was not in the Secret Word.")
                print(is_word_guessed(secret_word, letters_guessed))
            else:
                print(is_word_guessed(secret_word, letters_guessed))
                get_available_letters(letters_guessed)
                print("Number of guesses remaining: ", num_guesses)
                get_guessed_word(secret_word, letters_guessed)
                print("Good Guess!")
        print("You have lost")
        break
    

available_letters = string.ascii_lowercase
available_letters_copy = list(available_letters[:])
#secret_word = choose_word(wordlist)
hangman(secret_word)

I am struggling with getting the below function call to execute the way
I need it to in the below while loop.

def is_word_guessed(secret_word, letters_guessed):

   secret_word_copy = list(secret_word[:])
   for e in secret_word_copy:
       if e not in letters_guessed:
           return(False)
       else:
           all(e)

Once I have guessed all letters correctly this returns None.

Don’t do that! It is a Boolean function - return False.

Also, you do not need brackets on the return statement - it is not a
function call. You can just go:

return True

Until all letters are guessed correctly this returns True.
If I replace the all(e) with return(False). It returns false as soon as one input(e) is found in secret_word_copy.

Some things:

Just calling all(e) checks that all the values in e are true, but
otherwise has no effect. You’re just running a function and not doing
anything with its returned value.

The value e is an individual letter of secret_word_copy, but all()
expects a sequence (well, an iterable - anyway a thing which can have
several values in it, all of which should be checked). If you were
working with a list of ints, for example, all() would raise a TypeError
for e because an int cannot be iterated. But strings are funny:
there is no “character” type in Python, for iterating over a string just
gets you a series of single character strings, and strings are iterable.
SO all() does not raise an exception. For your code, this is a little
misleading.

Finally, all(e) checks the truthiness of everything in e. The
elements if e are just a single string containing, again, what was in
e, because e is a single character string. Strings (like most Python
“containers”) are true if not empty and false if empty. This means that
all(e) will always return True.

The usual way of doing what you’re doing is very close to what you have
done: loop over secret_word_copy checking that all its characters
appear in letters_guessed. Return False immediately if that is not
the case. And finally, at the end, rreturn True, meaning that you
have tested everything and found no falsehood, therefore the assertion
is true:

def is_word_guessed(secret_word, letters_guessed):
    secret_word_copy = list(secret_word[:])
    for e in secret_word_copy:
        if e not in letters_guessed:
            return False
    return True

Also, why are you copying secret_word? Twice in fact? There seems to be
no reason to do so: you are not changing it, so you can work on the
original value.

Note:

The expression secret_word[:] is a copy of secret_word.
The expression list(secret_word[:]) makes a new list from that copy,
so you are making a second copy (though this one is a list and not a
str).

But since strings are iterable anyway you can just for on the original
string supplied:

for e in secret_word:

Cheers,
Cameron Simpson cs@cskk.id.au

Hi Rufus,

You have this:

list(secret_word[:])

which makes a copy of secret_word, then makes a copy of the copy as a
list. You can simplify that by only making one copy:

secret_word_copy = list(secret_word)

But even that is more work than you need to do. There’s no real reason
to make a copy of the secret word, since we’re not changing it. We’re
just iterating over each letter in it.

So we can then iterate over each letter of the secret word:

for e in secret_word:  # no need for a copy
     if e not in letters_guessed:
         return False

If any letter is not guessed, then the word isn’t guessed and we can
return False there and then.

What happens if we get to the end of the secret word? Then all of the
letters must have been guessed, and we can return True:

for e in secret_word:  # no need for a copy
     if e not in letters_guessed:
         return False
return True

Note carefully that the return True must be outside of the for-loop.
It must not be indented inside the loop, but level with the for ....

Originally I was iterating over “secret_word” instead of making a copy. When I realized my function call was not working as intended I started trouble shooting anything I could think to make it work. As it doesn’t need to be mutable, I see why that was unnecessary.
Still, when I copy and paste the code in your reply the game ends instantly. I guess this is because the False is the return of the first Boolean check of the while loop. When I switch the words True and False in your suggested code, the game goes on as intended but the clear condition(discussed function call) still does not break the while loop when guessing all letters correctly. Upon running this in python tutor it appears that when it gets through the hangman function, it simply skips the while loop containing is_word_guessed, and goes straight to the num_guesses while loop.

I’m guessing this is a structing issue contained in the def hangman section but I’m unsure on that part. I will continue trying and thank you very much for your help!

I can make the code functional by adding multiple breaks and a continue. Places where changes were made to the original code are noted below by the asterisks in the code. Some of these seem unnecessary but maybe not.

secret_word = 'apple'
letters_guessed = []

def is_word_guessed(secret_word, letters_guessed):
    for e in secret_word:
        if e not in letters_guessed:
            **return True**
**    print("You Have guessed the secret word!")**
**    return False**
            
def get_guessed_word(secret_word, letters_guessed):

    currect_guess = []
    secret_word_copy = list(secret_word[:])
    for e in secret_word_copy:
        if e in letters_guessed:
            currect_guess.append(e)
        else:
            currect_guess.append('_ ')
    print(''.join(currect_guess))
    
    
def get_available_letters(letters_guessed):

    for e in available_letters_copy:
        if e in letters_guessed:
            available_letters_copy.remove(e)
    print(("Available Letters: "),' '.join(available_letters_copy))

def hangman(secret_word):

    num_guesses = 6
    print("Welcome to the game Hangman!")
    print("I am thinking of a word that is", len(secret_word), "letters long.")
    print("You have", num_guesses,"guesses left.")
    while is_word_guessed(secret_word, letters_guessed):
        while num_guesses != 0:
            player_turn = input("Enter Guess: ")
            letters_guessed.append(player_turn)
            if player_turn not in secret_word:
                num_guesses -= 1
                print("Number of guesses remaining: ", num_guesses)
                get_available_letters(letters_guessed)
                get_guessed_word(secret_word, letters_guessed)
                print("I'm sorry, that letter was not in the Secret Word.")
                print(is_word_guessed(secret_word, letters_guessed))
                **break**
            else:
                print(is_word_guessed(secret_word, letters_guessed))
                get_available_letters(letters_guessed)
                print("Number of guesses remaining: ", num_guesses)
                get_guessed_word(secret_word, letters_guessed)
                print("Good Guess!")
                **break**
**        if num_guesses == 0:**
**            print("You have lost!")**
**            break**
**        else:**
**            continue**
    

available_letters = string.ascii_lowercase
available_letters_copy = list(available_letters[:])
#secret_word = choose_word(wordlist)
hangman(secret_word)

When swapping the two while statements in the original code I verified that it seems to always discount the first while loop. num_guesses continues to count down into the negatives when these two statements are swapped in the original code posted.


    while is_word_guessed(secret_word, letters_guessed):
        while num_guesses != 0:

Apparently it’s never making it back around to check this condition on the first while loop in the original code. Just looping through the second condition.

If there is a better way of exiting these loops than I currently have implemented in the revised code please let me know.

As always I appreciate the help!

I revised portions of the hangman(secret_word) function to include warnings and other various print statements.

I still can’t get the clear condition and the lose condition to function simultaneously without this section below which kind of defeats the purpose of the num_guesses while loop.

        if num_guesses == 0:
            print("You have lost!")
            print("The secret word was", secret_word)
            break
        else:
            continue

If I remove it and solely add print/break statements in its place the is_word_guessed function(First while loop) still returns False when it is supposed to, the game just doesn’t end unless you run out of guesses and it makes it through the print and break statements for a lose condition.

If anyone has any suggestions or advice on why it would be returning False appropriately but not ending the game I would love to hear.

Thank you!

def hangman(secret_word):

    num_guesses = 6
    num_warning = 3
    print("Welcome to the game Hangman!")
    print("I am thinking of a word that is", len(secret_word), "letters long.")
    print("You have", num_guesses,"guesses left.")
    while is_word_guessed(secret_word, letters_guessed):
        while num_guesses != 0:
            player_turn = input("Enter Guess: ")
            letters_guessed.append(player_turn)
            for l in player_turn:
                if l not in available_letters:
                    num_warning -=1
                    print("You may only enter lowercase alphabetic letters")
                    get_available_letters(letters_guessed)
                    get_guessed_word(secret_word, letters_guessed)
                    if num_warning <= 0:
                        num_guesses -= 1
                        print("You have 0 warnings remaining")
                        print("You have", num_guesses,"guesses left.")
                        break
                    else:
                        print("You have",num_warning,"warnings remaining")
                        print("You have", num_guesses,"guesses left.")
                elif player_turn not in secret_word:
                    num_guesses -= 1
                    get_available_letters(letters_guessed)
                    get_guessed_word(secret_word, letters_guessed)
                    print("You have", num_guesses,"guesses left.")
                    print("I'm sorry, that letter was not in the Secret Word.")
                    break
                else: 
                    get_available_letters(letters_guessed)
                    get_guessed_word(secret_word, letters_guessed)
                    print("Good Guess!")
                    print("You have", num_guesses,"guesses left.")
                    break
            break
        if num_guesses == 0:
            print("You have lost!")
            print("The secret word was", secret_word)
            break
        else:
            continue

Okay, I realize this is probably a dead post as of now lol, but I finally found something that works.

 while is_word_guessed(secret_word, letters_guessed):
        **if num_guesses != 0:**

If I change the second while loop to an if I can remove the portion of code at the bottom which I found to be unnecessary(The “while num_guesses == 0”).

                else: 
                    get_available_letters(letters_guessed)
                    get_guessed_word(secret_word, letters_guessed)
                    print("Good Guess!")
                    print("You have", num_guesses,"guesses left.")
            continue
        print("I'm sorry you have lost!")
        print("The secret word was",secret_word)
        break

As I am new to python, if someone could please explain why this works with an if and not a while statement I would appreciate it. Thank You!