How to break in this situation - python

So I am almost done making a Hangman Program (just the framework), but I am having trouble with breaking a loop. This is because I am supposed to allow the user to have unlimited trials, so I couldn't use something like:
x = 10
while x != 0
do something
x -= 1
However, I figured out another way - to make a guess_list that contains every single letter that is tried and say:
if final_letter in guess_list:
break
I thought this would work fine (and it did for a couple trials), but I just checked the program again and it didn't work (I'll insert a picture here to show the details).
The thing I've noticed here is that I need to type everything in order (if 'friend'; 'f' then 'r' then 'i'...) to get the while loop break. How can I possibly get it to break when there are no more asterisks left??
My code is:
fr = input("What file would you like to open: ")
f = open(fr, 'r')
text = f.read()
x = 0
textsplit = text.split()
guess_list = ''
import random
rand_letter = random.choice(textsplit)
for letter in range(len(textsplit)):
final_letter = rand_letter.lower()
final_letter = ''.join(x for x in final_letter if x.isalpha())
for i in range(len(final_letter)):
print ('*', end = '')
print ()
while x == 0:
guess = input("Guess a character: ")
guess_list += guess
if guess not in final_letter:
for char in final_letter:
if char in guess_list:
print (char, end = ''),
if char not in guess_list:
print('*', end = '')
print('')
print("Incorrect. Try again!")
else:
for char in final_letter:
if char in guess_list:
print (char, end = ''),
if char not in guess_list:
print('*', end = '')
if final_letter in guess_list:
print('')
print('YOU GOT IT!!!! NIICEE')
break
Any help/comment/advice/thoughts would be greatly appreciated!!

Firstly, reading a file in for sample words is hard to replicate. Here, I've just used a predetermined list and split it into words.
Next, I've used sets here, as sets are useful for checking if something's already been guessed or if a character is in the goal word. Also, variable names are changed for clarity's sake, final_letter being a prime example.
If someone guesses a word which has characters that are only partially in the goal word (guessing "if" for "fond"), this will add to current progress but report incorrect. If you want to make sure that characters are in order for words ("niw" not being a valid guess for "win"), this will require some changes*.
s.update(iterable) adds all the things in an iterable (list, set, generator, etc) to a set s. s.issubset(other_set) checks that all elements of s are in other_set.
import random
textsplit = "friend win lose hangman".split()
guesses = set()
goal_word = random.choice(textsplit)
goal_set = set(goal_word)
print("".join(["*" for _ in goal_word]))
while True:
guess = input("Guess a character: ")
set_guess = set(guess)
if set_guess.issubset(guesses):
print("Please try again; you already guessed", guess)
continue
guesses.update(list(guess))
if goal_set.issubset(guesses):
print('YOU GOT IT!!!! NIICEE')
break
progress = ""
for c in goal_word:
if c in guesses:
progress += c
else:
progress += "*"
print(progress)
if not all((c in goal_set for c in set_guess)):
print("Incorrect. Try again!")
*Sets are unordered, so s.issubset(other_set) only cares that all the elements of s are in other_set, as s and other_set both don't know their own order. To support ordering, consider checking for equality between the guessed word and the goal word.

Related

Looping over characters (and their indices) in Python [duplicate]

So I'm making a hanging man game and I have run into a problem regarding indexes. Basically, I want to find the index of a letter inside a secret word, the problem is that if the secret word includes two letters that are the same, for instance, "guacamole", where the letter a has the index of 2 and 4 but when I want to find the index of a, it only prints "2" and not "4". Is there a way around this? Thanks in advance!
Part of code where problem occurs:
for letter in secret_word:
if user_guess == letter:
current_word_index = secret_word.find(letter)
print(current_word_index) #Not in full program, only to test errors.
Full code:
#Hanging man
import string
space = "\v"
dbl_space = "\n"
secret_word = str(input("Enter a secret word: "))
guess_low = list(string.ascii_lowercase)
used_letters = []
user_errors = 0
user_errors_max = 1
secret_word_index = int(len(secret_word))
secret_word_placeholder = list(range(secret_word_index))
while user_errors != user_errors_max:
user_guess = str(input("Enter a letter: "))
if len(user_guess) != 1:
print("You have to pick one letter")
if user_guess in guess_low:
guess_low.remove(user_guess)
used_letters.extend(user_guess)
print(used_letters)
for letter in secret_word:
if user_guess == letter:
current_word_index = secret_word.find(letter)
if user_errors == user_errors_max:
print("You lost the game, the secret word was: " + secret_word)
This is an example of what you are trying to achieve. use list comprehension.
string='hello'
letter='l'
[idx for idx,ch in enumerate(string) if ch==letter]
Python's string find accepts a start parameter that tells it where to start searching:
>>> "guacamole".find('a')
2
>>> "guacamole".find('a', 3)
4
Use a loop, and use the index of the last hit you found + 1 as the start parameter for the next call.
Another more verbose solution might be:
str1 = "ooottat"
def find_all_indices(text, letter):
indices_of_letter = []
for i, ch in enumerate(text):
if ch == letter:
indices_of_letter.append(i)
return indices_of_letter
print(find_all_indices(str1, 'o'))
Side note:
Indexes is the nontechnical plural of index. the right technical plural for index is indices
Yes, if you instantiate a new_variable to be the secret_word variable before the for loop, the in the line current_word_index = secret_word.find(letter) change secret_word.find(letter) to new_variable .find(letter) then below the if statement write new_variable = new_variable [1:] which will remove the letter just found.
so your code would look something like:
new_variable = secret_word
for i in secret_word
if user_guess == letter:
current_word_index = new_variable.find(letter)
#I would do print(current_word_index) this will show the index
#Or current_word_index = current_word_index + ',' + new_variable.find(letter)
new_variable= new_variable[1:] #this will take away your letter.

How to replace the specified dash with the letter

I wish to write a hangman program and in order to do so, I have to replace the hash ('-') letter(s) with the user's guessed letter (guess). But when I run the code, it replaces all the hashes with the user's guess letter.
The code seems okay but I don't get the desired result.
words is a list of words I have written before the function.
def word_guess():
random.shuffle(words)
word = words[0]
words.pop(0)
print(word)
l_count = 0
for letter in word:
l_count += 1
# the hidden words are shown a '-'
blank = '-' * l_count
print(blank)
guess = input("please guess a letter ")
if guess in word:
# a list of the position of all the specified letters in the word
a = [i for i, letter in enumerate(word) if letter == guess]
for num in a:
blank_reformed = blank.replace(blank[num], guess)
print(blank_reformed)
word_guess()
e.g: when the word is 'funny', and guess is 'n', the output is 'nnnnn'.
How should I replace the desired hash string with guess letter?
it replaces all the hashes
This is exactly what blank.replace is supposed to do, though.
What you should do is replace that single character of the string. Since strings are immutable, you can't really do this. However, lists of strings are mutable, so you could do blank = ['-'] * l_count, which would be a list of dashes, and then modify blank[num]:
for num in a:
blank[num] = guess
print(blank)
A couple things to note:
inefficient/un-pythonic pop operation (see this)
l_count is just len(word)
un-pythonic, unreadable replacement
Instead, here's a better implementation:
def word_guess() -> str:
random.shuffle(words)
word = words.pop()
guess = input()
out = ''
for char in word:
if char == guess:
out.append(char)
else:
out.append('-')
return out
If you don't plan to use the locations of the correct guess later on, then you can simplify the last section of code:
word = 'hangman'
blank = '-------'
guess = 'a'
if guess in word:
blank_reformed = ''.join(guess if word[i] == guess else blank[i] for i in range(len(word)))
blank_reformed
'-a---a-'
(You still have some work to do make the overall game work...)

How does one go about making a hangman game where the computer shows you your progress in Python?

I'm trying to make a Hangman game in Python but I lack the knowledge of several things it seems. This code below is a rough draft on what I got so far. The idea here is using words from a text file, separating them into a list and then taking a random entry on that list to use as the word for the game. After that, it takes an input from the user on a letter: if the input is in the list, it should print a the word, but turning the letters that weren't guessed yet into "_". So, for example: ____a _. The problem is, I don't know how to do this and it's very confusing. As you can see below, I was trying to use a "for" loop.
import random
import string
# Opening the text file that contains the words that are going to be used in the game
with open('words.txt') as file:
text = file.read()
words = list(map(str, text.split()))
result = random.choice(words)
list_of_letters = list(result)
attempts = 1
for attempts in range(6):
pick = input("Pick a letter: ")
if pick in list_of_letters:
print(pick)
else:
print("_")
#Here is supposed to be what you get as a result when you lose, I want to keep track of the progress like __a__a__, for example.
else:
print("You lost! The word was", result, "\n Here's your progress: ", )
You should do a few more tutorials in python you have made some minor errors in terms of efficiency and methodology. But to answer your question I made this example of a hangman game. Hopefully you can learn from it
My words.txt file looks like this these are some words
# String or str() is a primitive type in python. You don't need to import it.
import random
# Opening the text file that contains the words that are going to be used in the game
with open('words.txt') as file:
# Here I read the file, replace newlines ('\n') with a space and then split the entire thing.
words = file.read().replace('\n', ' ').split(' ')
# function to grab a new word and list of letters
def new_choice(words):
res = random.choice(words)
l_of_letters = list(res)
return res, l_of_letters
# function to evaluate the pick and manage progress
def check_pick(l_of_letters, pick, progress):
new_progress = []
match = False
# if you have already guessed the letter it returns immediately
if pick in progress:
print('You have already guessed that letter!')
return progress, False, True
for letter in l_of_letters:
# if the letter has not been guessed previously
if letter not in progress:
# see if it matches the pick
if pick == letter:
# if it does return a successful match and add it to new_progress
match = True
new_progress.append(pick)
else:
# otherwise add the default underscore
new_progress.append('_')
# if it has been guessed before then add it
else:
new_progress.append(letter)
# you win if new_progress is all letters
w = '_' not in new_progress
return new_progress, w, match
# function to continually ask for input until it is appropriate
def pick():
p = input("Pick a letter: ").strip(' ')
if p == '':
print('Guess cannot be blank!')
pick()
return p
# Beginning of main loop. This will loop forever until break is called in the main loop
while True:
# get a word choice
result, list_of_letters = new_choice(words)
print('_' * len(list_of_letters))
# progress is started as a list of underscores the length of the word
progress = ['_'] * len(list_of_letters)
win = False
attempts = 6
# this nested while loop asks the user to input their choice, evaluates and tracks progress. It does this until you run out of attempts
while attempts is not 0:
print('Attempts: ', attempts)
# ask for a pick
p = pick()
# check the pick and the progress
progress, win, match = check_pick(list_of_letters, p[0], progress)
# if check_pick() returns win == True the game is over
if win:
print(f'You win! The word was \"{result}\"')
# this breaks out of the nested while loop
break
# if you haven't won it prints your progress
print(''.join(progress))
# if your pick was not a match you lose an attempt
if not match:
attempts -= 1
# if the loop finishes and you haven't won it prints this message
if not win:
print(f"You lost! The word was \"{result}\"\nHere's your progress: {''.join(progress)}")

Replacing every instance of a character in Python string

I have a game where the user guesses letters. They are shown a blank version of the mystery work (_____ for example, the _'s are equal to number of characters in the word). The program knows the word, and needs to replace every index in the blanked out version of the word if the letter they guess is present in the mystery word.
For example, if the player guesses "p" and the word is "hippo" they will be shown __pp_. But, my code will only replace the first instance of "p", giving __p__ instead.
Would this be easier to tackle as a list problem?
mistakes = 0
complete = False
t = False
words = ['cow','horse','deer','elephant','lion','tiger','baboon','donkey','fox','giraffe']
print("\nWelcome to Hangman! Guess the mystery word with less than 6 mistakes!")
# Process to select word
word_num = valid_number()
word = words[word_num]
#print(word)
print("\nThe length of the word is: ", str(len(word)))
attempt = len(word)*"_"
# Guesses
while not (mistakes == 6):
guess = valid_guess()
for letter in word:
if guess == letter:
print("The letter is in the word.")
position = word.index(guess)
attempt = attempt [0:position] + guess + attempt [position + 1:]
print("Letters matched so far: ", attempt)
t = True
while (t == False):
print("The letter is not in the word.")
print("Letters matched so far: ", attempt)
mistakes = mistakes + 1
hangMan = ["------------", "| |", "| O", "| / |", "| |", "| / |\n|\n|"]
hang_man()
t = True
t = False
answer = 'hippo'
fake = '_'*len(answer) #This appears as _____, which is the place to guess
fake = list(fake) #This will convert fake to a list, so that we can access and change it.
guess = raw_input('What is your guess? ') #Takes input
for k in range(0, len(answer)): #For statement to loop over the answer (not really over the answer, but the numerical index of the answer)
if guess == answer[k] #If the guess is in the answer,
fake[k] = guess #change the fake to represent that, EACH TIME IT OCCURS
print ''.join(fake) #converts from list to string
This runs as:
>>> What is your guess?
p
>>> __pp_
To loop over everything, I did not use index, because index only returns the first instance:
>>> var = 'puppy'
>>> var.index('p')
0
So to do that, I analyzed it not by the letter, but by its placement, using a for that does not put k as each letter, but rather as a number so that we can effectively loop over the entire string without it returning only one variable.
One could also use re, but for a beginning programmer, it is better to understand how something works rather than calling a bunch of functions from a module (except in the case of random numbers, nobody wants to make their own pseudo-random equation :D)
Based on Find all occurrences of a substring in Python:
import re
guess = valid_guess()
matches = [m.start() for m in re.finditer(guess, word)]
if matches:
for match in matches:
attempt = attempt[0:match] + guess + attempt[match+1:]
print("Letters matched so far: ", attempt)
else:
.
.
.

How to replace characters in a string using iteration and variables? (Python)

I am writing a hangman game in python as part of a college project, and I am trying to use string.replace(old, new) to substitute the blanks (_) with letters. Instead of using actual string characters though, I am trying to use variables for 'old' and 'new'. Here's what I've got so far for this bit:
if validGuess == 'true':
if guess in word:
for letter in word:
if letter == guess:
word.replace(letter, guess)
else:
missNum = (missNum + 1)
else:
tryNum = (tryNum - 1)
However, it isn't working. I don't get any errors, it simply will not replace the blanks.
What am I doing wrong here? Is there a better way to achieve what I am doing?
-EDIT-
I tried to implement #Peter Westlake's solution (which seemed to me the most elegant) but I have run into an issue. I have a section of code which converts a randomly selected word into underscores:
#converting word to underscores
wordLength = len(word)
wordLength = (wordLength - 1)
print(wordLength) #testing
for i in range(0,wordLength):
wordGuess = (wordGuess + '_')
print(wordGuess)
And this seems to work fine. Here is the code for letter substitution:
if validGuess == 'true':
wordGuess = ''.join([letter if guess == letter else wordGuess[pos]
for pos, letter in enumerate(word)])
if guess not in word:
tryNum = (tryNum - 1)
print(wordGuess)
However, here is the output:
Guess a letter: a
test
Traceback (most recent call last):
File "G:\Python\Hangman\hangman.py", line 60, in <module>
for pos, letter in enumerate(word)])
File "G:\Python\Hangman\hangman.py", line 60, in <listcomp>
for pos, letter in enumerate(word)])
IndexError: string index out of range
String index out of range? What does that mean?
str.replace() returns the new string, store the new value:
word = word.replace(letter, guess)
Python strings are immutable and cannot be altered in-place.
However, you are replacing letter with the exact same value; letter == guess is only True if both are the same character.
I'd keep a separate set of correctly guessed letters instead, and rebuild the displayed underscores and correct guesses each time:
correct_guesses = set()
incorrect_guesses = set()
if guess in correct_guesses & incorrect_guesses:
print('You already guessed that letter')
elif guess in word:
# correct guess!
correct_guesses.add(guess)
display_word = ''.join(char if char in correct_guesses else '_' for char in word)
else:
# incorrect guess!
incorrect_guesses.add(guess)
print('Oops, incorrect guess!')
missNum += 1
I think I understand what you're getting at here.
I would probably rebuild the word-so-far on the spot instead of having a persistent string for it, keeping the tested letters separately. When the user tries a new character, make two checks:
See if the guess character has been guessed already: if guess in tried. If so, proceed however you like (penalize or ignore), but don't add the character to the tried-characters list.
If not, see if the character is in the target word: elif guess in word. If not, assess some penalty and add the guess to the tried-characters list.
For any other result: else. Add the guess to the tried-characters list.
To display the user's progress, make a blank string. Go through the target word character-at-a-time: for char in word, like you have been. But instead of trying to modify an extant string, just add the character to the end of the blank string if it's in the tried-characters string, or an underscore if not: show += char if char in tried else "_". Once that for loop is exhausted, display what you've got!
Alternatively, use .join with a slightly different iterator: show = "".join(char if char in tried else '_' for char in word). It'll iterate through word, keeping each letter if it's in your tried-characters string, or substituting an underscore if not, putting whatever is in "" between them (or nothing, if you leave it as ""). It looks like you already know that, though.
At the hazard of completely rewriting your code, this is what it might look like:
## init
word = "mauritius" # the word you're looking for. I chose this one.
tried = str() # initialize a list of tested characters
tryNum = 3 # however many wrong guesses the user gets
...
## in your run loop...
if tryNum: # evaluates 0 as Fasle, if you didn't already know
guess = UserInput() # some means to get the guess from the user; a single-character string.
if guess in tried:
print "Tried that letter already!"
elif guess not in word: # the letter hasn't been tested yet, but isn't in the word, either.
print "Wrong! %d guesses left!" % tryNum
tryNum -= 1
tried += guess
else: # the guess is new (not tried) and it's valid (in the word)
tried += guess
show = str() # this is the string you will display. make a blank one each time.
for char in word:
show += char if char in tried else "_" # if the character has been tried, add it to the output. Otherwise, an underscore.
print show # display the word so far
if show == word:
print "You win!" # Congratulations! You hung a man.
else: # Out of tries; the convict lives another day.
print "Game Over!" # I am not sure how committed to this vocabulary-based execution you really are...
You can swap if tryNum: with while tryNum: and it should work all by itself, after initialization. If you do, there are fun things you can do with continues and breaks, but that goes a bit beyond the scope of your question.
You can swap show = str() and the for char in word: block out with the .join singleton in this next example, too. Change ''.join(..) to ' '.join(..) to add a space between characters/underscores!
This compressed version is probably a bit less Pythonic:
# post-init...
if tryNum:
guess = UserInput()
if guess in tried: pass
elif guess not in word:
print "Wrong! %d guesses left!" % tryNum
tryNum -= 1
tried += guess
else: tried += guess
show = ''.join(char if char in tried else '_' for char in word)
if show == word: print "You win!"
else: print "Game Over!"
This does not answer your first question of "What am I doing wrong?" but I think it might be a better way of going about what you intend? It might be a bit easier to maintain and expand for you, too.
Note: Go ahead and replace UserInput() with something like str(raw_input("Guess a letter!"))[0] if you want to try this thing out on its own.
Replacing a letter with an identical guess isn't going to do anything! I think you want to find the position in the word where the guessed letter appears, and replace the _ in that position with the letter. For that you will need to find every position where the letter occurs, e.g. using the index() method.
For instance, to replace the first occurrence of the guess:
# Illustration of the principle, not the complete answer.
word = 'faq'
display = '___'
# Put in a loop, one iteration for each guess input.
guess = 'a'
display = ''.join([letter if guess == letter else display[pos]
for pos, letter in enumerate(word)])
print display
Which will print _a_.

Categories

Resources