using .find for multiple letters in a string - string

I'm working on a hobby project. I'm attempting to make a hangman game in Python. So far everything works nice. There's just one problem. If I type a letter that appears in the word two times, I can't get the second letter to appear. I've been toying around with string.find and string.count methods but to no avail. Does anyone have an idea how I would go about doing this? I'm stumped.
#!bin/bash/python
import os
import time
word = 'megalopolis'
l = len(word)
list = []
n=0
while n!=l:
list.append('-')
n+=1
os.system('cls' if os.name=='nt' else 'clear')
print list
i=3
while i!=0:
x = raw_input('Enter a letter: ')
if x in word and x!='':
print 'Good choice!'
count=word.count(x)
loc=word.find(x)
print count
print loc
list[loc]=x
os.system('cls' if os.name=='nt' else 'clear')
if '-' not in list:
break
print list
else:
print 'Sorry...'
i-=1
if i==2:
print 'You have '+`i`+' more chances.'
if i==1:
print 'You have '+`i`+' more chance!'
time.sleep(1)
os.system('cls' if os.name=='nt' else 'clear')
print list
if '-' not in list:
print 'YOU WIN!!'
else:
print 'GAME OVER!!'
x = raw_input('press enter')

If you just need the index of every character occurence:
indexes = [idx for idx, ch in enumerate(word) if ch == x]
Perhaps you should use Unidecode to keep the accents in words, it might be useful depending on the language (if not English). Also, you can use str.lower() or str.upper() methods to ensure every word and trial is in the same case.
The string module has useful constants for you (e.g. ascii_uppercase).
However, in this game you don't need to worry about any index. I've made another version for you:
#!/usr/bin/env python
from string import ascii_uppercase
word = "megalopolis".upper() # Top-secret!
trial = 3 # Total trials available (number of mistakes to lose the game)
typed = set() # Typed characters
word_letters = set(word)
while trial:
print
print "".join(ch if ch in typed else "-" for ch in word)
# Winning condition
if typed.issuperset(word_letters):
break
# Data input
x = raw_input("Enter a letter: ").upper()
# Error cases
if len(x) != 1:
print "Invalid input."
continue
if x in typed:
print "Already typed."
continue
if x not in ascii_uppercase:
print "What you typed isn't a letter."
continue
# Valid data cases
typed.add(x)
if x in word:
print "Good choice!"
else:
print "{} not found!".format(x),
trial -= 1
if trial == 1:
print "You have one more chance!"
elif trial > 1:
print "You have {} more chances.".format(trial)
else:
print 'Sorry...'
# Ending message
print
if trial:
print "YOU WIN!!"
else:
print "GAME OVER!!"
Hashbang: Your shebang should usually start with "#!/". You're probably using Windows, so the "bin" as a relative directory wasn't used by you.
"l" / l as a variable name should be avoided! It might be seen as one or lower "L" (PEP8), or even a pipe "|". PS: At the beginning of this item, I typed the same letter here twice.
There's no need to use "list" as a variable name here, and you shouldn't do, as that's a built-in name.
Multiplication like "txt" * 3 returns "txttxttxt" (it repeats the data) for both strings and lists
Neither "cls" nor "clear" worked here, showing
"TERM environment variable not set."
instead of clearing the console screen. I replaced these with an empty
"print", and removed the time sleep. Look for subprocess if you want to call something from console (although I'd also look for curses if there's a need to do some CLI visualization).
Suppose x is a string. When x == "", bool(x) is False, else bool(x) is True.
Suppose x is an integer. When x == 0, bool(x) is False, else bool(x) is True.
Avoid backticks (`). No one uses them today in Python, they doesn't exist in Python 3 and you can use the repr built-in instead. However, you probably wanted something like str(trial), "%d" % trial or "{}".format(trial).
The last "press enter" probably has to do with an operating system "auto-close-after-finish" behaviour, but you [at least] didn't need to store it in x.
I've used a generator expression. You should read here about list comprehensions if the "for" in the middle of one line is confusing for you. Python developers use generator expressions and list comprehensions all the time, you shouldn't avoid learning about them.
I replaced the original winning evaluation to a comparison between the set of characters the word originally has and the set of typed characters (both uppercase).
If there's something here you didn't understand, please ask a new question.

This SO question ought to cover it for you:
Finding multiple occurrences of a string within a string in Python
It should work just as well for individual characters as strings, considering how easy it is to form the second from the first.

So in the end, I wound up doing it this way:
if x in word and x!='':
count=word.count(x)
loc=0
while count==1 or count>1:
loc=word.find(x,loc)
list[loc]=x
loc+=1
count-=1
print 'Good choice!'
Thanks for your help everyone. I definitely learned something.

Related

What is going on with scope in this beginners Python program?

I'm a fairly experienced C# programmer taking a look at Python. I'm working from a book called Invent Your Own Computer Games with Python. Chapter two features this game...
# This is a Guess the Number game.
import random
# guessesTaken = 0
print ("Hello! What is your name?")
myName = input()
number = random.randint(1,10)
print("Well, " + myName + ", I am thinking of a number between 1 and 10.")
for guessesTaken in range(6):
print ("Take a guess.") # Four spaces in front of "print"
guess = input()
guess = int (guess)
testNumber = 5
if guess < number:
print("Your guess is too low.") # Eight spaces in front of "print"
if guess > number:
print ("Your guess is too high.")
if guess == number:
break
if guess == number:
guessesTaken = str(guessesTaken + 1)
print ("Good job, " + myName + "! You guessed my number in " + guessesTaken + " guesses!")
if guess != number:
number = str(number)
print ("Nope. The number I was thinking of was " + number + ".")
testNumber = str(testNumber)
print (testNumber)
I was a little confused about the guessesTaken variable, so I commented out it's instantiation line - and was surprised to find the code still behaved as normal.
In C#, I would expect guessesTaken in the For declaration to be out of scope for the rest of the program. However, later uses of the variable have no problem accessing the number in question.
To test this, I added the variable 'testNumber' within the For block and assigned it '5'. Sure enough, attempting to print the number outside of it's scope just works...!
I'm a little rusty with programming, but this is still a bit confusing to me - is the For loop not considered a different scope to the block surrounding it? This page seems to suggest otherwise, but I guess it could be that For loops in Python are considered in scope with their surroundings.
Just seems a bit weird. Can anybody put me right here? I'll get back into C# to test this in that language later, but any responses now would be much appreciated. Thanks!
The for guessesTaken in range(6): creates the variable guessesTaken without you needing to explicitly do it, that is why the following code would also work (results in 9):
for x in range(0, 10):
pass
print(x)
However, this would result in a NameError:
print(x)
for x in range(0, 10):
pass
This is likely hidden away somewhere in the documentation.
The reason you can access testNumber is that it's assigned local scope by default; unless it's explicitly set, which in your code, happens to be local to the entire script. The for loop is not considered to be in a different scope.

Write a function that removes all occurrences of a given letter from a string [duplicate]

This question already has answers here:
Strange result when removing item from a list while iterating over it
(8 answers)
Closed 3 years ago.
Write a function that removes all occurrences of a given letter from a string:
remove_letter("a", "apple") == "pple"
remove_letter("a", "banana") == "bnn"
remove_letter("z", "banana") == "banana"
I have tried to make a list from the string and then deleting the element. Below is my code for this problem. Is this correct? Or should I think of something else? Can you tell a better method than this and why is that method better?
def rmv(word, letter):
word = list(word)
for ele in word:
if ele == letter:
word.remove(ele)
print(word)
This code does not show any error messages. It gives the expected output for the words like "banana" but for word like "bananna" it would not give the expected output.
For rmv("banana", "n") output is "baaa".
For rmv("bananna", "n") output is "baana".
Your code approach with fix:
def rmv(word, letter):
word = list(word)
newWord=[]
for i in range(len(word)):
if word[i]!= letter:
newWord.append(word[i])
print(newWord)
rmv("bananna", "n")
What you were doing wrong:
You were iterating your list and deleting as and when your if-else met the condition:
if ele == letter:
word.remove(ele)
But the problem with this approach is that your pointer moves ahead and the deletion causes the list to shift accordingly at the same time and so when you see the case of
rmv("bananna", "n")
By the time pointer could get the opportunity to scan and remove the last n the list caused it to shift at an index that was already scanned by your list and hence it got ignored.
On a side note -
I generally advise people to use Python-tutor to debug their code. It's GUI based code visualizations helps to know where you went wrong.
Your problem is that you modify (remove) a list while you iterate over it. Just put a few print lines into your code to see what it does.
It's better to create a new list and adding only characters that you want to keep:
def rmv(word, letter):
out = []
for char in word:
if char != letter:
out.append(char)
return ''.join(out)
or, even more compactly:
def rmv(word, letter):
return ''.join(char for char in word if char != letter)
Why not just:
def rmv(word, letter):
return word.replace(letter, '')
print(rmv("apple", "a"))
There is no need for you to iterate through each character of the string when one function call will do it for you.
Why don't you try the string replace function?
Try this code:
def rmv(word, letter):
return word.replace(letter, "")

Implement a while loop in my hangman type/puzzle game

I'm new to python programming. I would like to display a win message after every correct letter input and no message if an incorrect letter is input.
I've written my code such that it will only accept one letter at a time and reduce an attempt by 1, regardless of if it is wrong or right.
How would I be able to implement a while loop into this so that I don't keep getting this error:
builtins.TypeError: 'str' object does not support item assignment
word="banana"
word_list=list(word)
length=len(word_list)
word_list= set(word_list)
word_list=list(word_list)
answer=["_"]*length
answer=list(answer)
guess=[]
count = 4
win=False # boolean so we do not use an identifier in our if statements
user_guess=window.input_string("Guess a letter: ", x, y)
y = y + font_height
guess.append(user_guess)
while count > 0:
# Removes guesses if they are not in the word so that the blanks do not fill in with incorrect letters
for letter in guess:
if letter not in word_list:
guess.remove(letter)
else:
win=True
# Replaces blanks in empty list with the letter guess
for place,letter in enumerate(list(word)):
for i in range(len(guess)):
if letter == guess[i]:
answer[place]=guess[i]
answer=" ".join(answer)
update_message = 'The answer so far is: '
window.draw_string(update_message + answer,x,y)
y = y + font_height
#End Game
win_message = 'Good job! You got the word.'
lose_message = 'Not quite, the correct word was: '+word +' Better luck next time'
if win:
window.draw_string(win_message,x,y)
y = y + font_height
count -=1
else:
window.draw_string(lose_message,x,y)
y = y + font_height
count -=1
Please notice this assignment: answer=" ".join(answer). Before the assignment, answer is a list of string. After the assignment, answer becomes a string.
So, in the next iteration of the while loop, answer[place]=guess[i] turns invalid, because python does not allow modifying a string by assigning a "character" to some place of the string.
It really takes some time to find the fault. You'd better provide the information, like, "which line in the program targeted the error message", when asking questions in future.

finding largest and smallest number in python

I am very new to programming, please advise me if my code is correct.
I am trying to write a program that repeatedly prompts a user for integer numbers until the user enters 'done'. Once 'done' is entered, print out the largest and smallest of the numbers. If the user enters anything other than a valid number catch it with a try/except and put out an appropriate message and ignore the number.
largest = None
smallest = None
while True:
num = input("Enter a number: ")
if num == 'done':
break
try:
fnum = float(num)
except:
print("Invalid input")
continue
lst = []
numbers = int(input('How many numbers: '))
for n in range(numbers):
lst.append(num)
print("Maximum element in the list is :", max(lst), "\nMinimum element in the list is :", min(lst))
Your code is almost correct, there are just a couple things you need to change:
lst = []
while True:
user_input = input('Enter a number: ')
if user_input == 'done':
break
try:
lst.append(int(user_input))
except ValueError:
print('Invalid input')
if lst:
print('max: %d\nmin: %d' % (max(lst), min(lst)))
Also, since you said you're new to programming, I'll explain what I did, and why.
First, there's no need to set largest and smallest to None at the beginning. I actually never even put those values in variables because we only need them to print them out.
All your code is then identical up to the try/except block. Here, I try to convert the user input into an integer and append it to the list all at once. If any of this fails, print Invalid input. My except section is a little different: it says except ValueError. This means "only run the following code if a ValueError occurs". It is always a good idea to be specific when catching errors because except by itself will catch all errors, including ones we don't expect and will want to see if something goes wrong.
We do not want to use a continue here because continue means "skip the rest of the code and continue to the next loop iteration". We don't want to skip anything here.
Now let's talk about this block of code:
numbers = int(input('How many numbers: '))
for n in range(numbers):
lst.append(num)
From your explanation, there is no need to get more input from the user, so none of this code is needed. It is also always a good idea to put int(input()) in a try/except block because if the user inputs something other than a number, int(input()) will error out.
And lastly, the print statement:
print('max: %d\nmin: %d' % (max(lst), min(lst)))
In python, you can use the "string formatting operator", the percent (%) sign to put data into strings. You can use %d to fill in numbers, %s to fill in strings. Here is the full list of characters to put after the percent if you scroll down a bit. It also does a good job of explaining it, but here are some examples:
print('number %d' % 11)
x = 'world'
print('Hello, %s!' % x)
user_list = []
while True:
user_input = int(input())
if user_input < 0:
break
user_list.append(user_input)
print(min(user_list), max(user_list))

using a looop to add user input to the set and dict in order discovered

So I'm trying to write a small program that does a few things. First is:
Write a while loop that repeatedly creates a list of words from a line of input from the user. So I did this:
s = input("Please enter a sentence: ")
while True:
pos = 0
for c in s:
if c == " ":
print(s[:pos])
s = s [pos+1:]
break
pos += 1
else:
print(s)
break
I need to add the user inputted words to the set and dict and then display their value in the order in which the program discovered them. I believe I need another loop but I'm not sure. I'm pretty lost at this point and the above is as far as I can seem to come on this program. Any help is appreciated as I am (obviously)new at python.

Resources