Why does this function result in ['h', '-', '-', '-', '-']? - python

The function hangman_guessed(guessed, secret) is supposed to take a string of guessed characters and a list of "secret" characters.
The function checks every character in the secret list and compares it with each character in the guessed character string to check if the character is in both. If the characters are not the same then the function places a - in a temporary list equal to the secret list (so that we can still compare other characters in the guessed list to the original secret list later).
def hangman_guessed(guessed, secret):
modified = secret
for i1 in range(len(secret)):
for i2 in range(len(guessed)):
if secret[i1] == guessed[i2]:
modified[i1] = secret[i1]
break
else:
modified[i1] = '-'
return modified
For example, when I run hangman_guessed('hl', ['h','e','l','l','o']), it should return ['h', '-', 'l', 'l', '-'], but currently it returns ['h', '-', '-', '-', '-'].
The problem here is that only the first character in the guessed list is considered, but I do not know why. It this case, it is expected that the program checks over the 'l' characters in ['h','e','l','l','o']) and sets the corresponding characters in the temporary list modified to -, but to my understanding after the for loop runs again and checks the original secret list for l characters it should overwrite the - in the modified list and the result should have the 'l' characters rather than the - characters.

A list-comprehension is perfectly suited to what you want to do. We want to create a list of each character (let this be i) in secret if i is in guessed else we want to have a hyphen ("-").
def hangman_guessed(guessed, secret):
return [i if i in guessed else "-" for i in secret]
and a test to show it works:
>>> hangman_guessed('hl', ['h','e','l','l','o'])
['h', '-', 'l', 'l', '-']
As you get more used to the flow of Python, you will find that comprehensions in general are extremely useful as well as being very readable for a whole variety of things.
If for some reason however, you had to use nested for-loops and weren't allowed to use the really simple in operator, then you need to / can make a couple of corrections to your current code:
make a copy of the secret list first
iterate through the characters in guessed, rather than the indexes
After making these two corrections, the function will look something like:
def hangman_guessed(guessed, secret):
modified = secret[:]
for i in range(len(secret)):
for g in guessed:
if secret[i] == g:
modified[i] = secret[i]
break
else:
modified[i] = '-'
return modified
which now works:
>>> hangman_guessed('hl', ['h','e','l','l','o'])
['h', '-', 'l', 'l', '-']

Related

How to edit individual character formats in string (beyond just upper())?

I am trying to create a sort of version of Wordle in python (just for practice).
I am having difficulty communicating to the player which letters in their guess match (or closely match) the letters in the target word.
I can highlight matches (i.e. where the letter is in the right place) using uppercase, but I don't know how to differentiate between letters which have a match somewhere in the target word and letters which do not appear at all. The relevant code is below:
def compare_words(word,guess):
W = list(word)# need to turn the strings into list to try to compare each part
G = list(guess)
print(W) # printing just to track the two words
print(G)
result =[ ] # define an empty list for our results
for i in range(len(word)):
if guess[i] == word[i]:
result.append(guess[i].upper())
elif guess[i] in word:
result.append(guess[i])
else:
result.append(" ")
print (result)
return result
# note, previous functions ensure the length of the "word" and "guess" are the same and are single words without digits
x = compare_words("slide","slips")
['s', 'l', 'i', 'd', 'e']
['s', 'l', 'i', 'p', 's']
['S', 'L', 'I', ' ', 's']
As you can see, the direct matches are upper, the other matches are unchanged and the "misses" are left out. This is not what I want, are usually the whole guess is spat back out with font change or colours to indicate the matches.
I have looked into bolding and colours but it all at the point of printing. I need something built into the list itself, but I am unsure if I can do this. Any ideas?
Cheers

Recreating the strip() method using list comprehensions but output returns unexpected result

I am trying to 'recreate' the str.split() method in python for fun.
def ourveryownstrip(string, character):
newString = [n for n in string if n != character]
return newString
print("The string is", ourveryownstrip(input("Enter a string.`n"), input("enter character to remove`n")))
The way it works is that I create a function that passes in two arguments: 1) the first one is a string supplied, 2) the second is a a string or char that the person wants to remote/whitespace to be moved from the string. Then I use a list comprehension to store the 'modified' string as a new list by using a conditional statement. Then it returns the modified string as a list.
The output however, returns the entire thing as an array with every character in the string separated by a comma.
Expected output:
Boeing 747 Boeing 787
enter character to removeBoeing
The string is ['B', 'o', 'e', 'i', 'n', 'g', ' ', '7', '4', '7', ' ', 'B', 'o', 'e', 'i', 'n', 'g', ' ', '7', '8', '7']
How can I fix this?
What you have set up is checking each individual character in a list and seeing if it matches 'Boeing' which will never be true so it will always return the whole input. It is returning it as a list because using list comprehension makes a list. Like #BrutusForcus said this can be solved using string slicing and the string.index() function like this:
def ourveryownstrip(string,character):
while character in string:
string = string[:string.index(character)] + string[string.index(character)+len(character):]
return string
This will first check if the value you want removed is in your string. If it is then string[:string.index(character)] will get all of the string before the first occurrence of the character variable value and string[string.index(character)+len(character):] will get everything in the string after the first occurrence of the variable value. That will keep happening until the variable value doesn't occur in the string anymore.

How to safely truncate a quoted string?

I have the following string:
Customer sale 88% in urm 50
Quoted with urllib.parse.quote, it becomes:
Customer%20sale%2088%25%20in%20urm%2050%27
Then I need to limit its length to a maximum of 30 characters and I use value[:30].
The problem is that it becomes "Customer%20sale%2088%25%20in%" which is not valid:
The last % is part of %20 from quoted string and makes it an invalid quoted string.
I don't have control over the original string, and the final result needs to have a maximum 30 length, so I can't truncate it beforehand.
What approach would be feasible?
urllib.quote uses percent-encoding as defined in RFC 3986. This means that encoded character will always be of the form "%" HEXDIG HEXDIG.
So you simply can delete any trailing rest of the encoding by looking for a % sign in the last two characters.
For example:
>>> s=quote("Customer sale 88% in urm 50")[:30]
>>> n=s.find('%', -2)
>>> s if n < 0 else s[:n]
'Customer%20sale%2088%25%20in'
What about looking for dangling percentage marks?
value = value[:30]
if value[-1] == "%":
value = value[:-1]
elif value[-2] == "%":
value = value[:-2]
print(value)
The encoded string will be always in the format of %HH. You want the string length to be maximum of 30characters with a valid encoding. So, probably the best solution I can think of:
from urllib.parse import quote
string= "Customer sale 88% in urm 50"
string=quote(string)
string=string[:string[:30].rfind("%")]
print(string)
Output:
string=string[:string[:30].rfind("%")]
Solution:
After the encoding, you may get a string of any length, the following one line of code will be enough to achieve your requirement in a very optimized way.
string=string[:string[:30].rfind("%")]
Explanation:
It first extracts 30 characters from the quoted string then searches for % from the right end. The position of % from the right end will be used to extract the string. Voilaa!! You got your result.
Alternate approach:
Instead of string=string[:string[:30].rfind("%")] you can do like this too string=string[:string.rfind("%",0,30)]
Note: I extracted the string and stored it back to showcase how it works, if you do not want to store then you can simply use like print(string[:string[:30].rfind("%")]) to display the results
Hope it helps...
How about putting the individual characters in a list and then count and strip?
Rough example:
from urllib import quote
s = 'Customer sale 88% in urm 50'
res = []
for c in s:
res.append(quote(c))
print res # ['C', 'u', 's', 't', 'o', 'm', 'e', 'r', '%20', 's', 'a', 'l', 'e', '%20', '8', '8', '%25', '%20', 'i', 'n', '%20', 'u', 'r', 'm', '%20', '5', '0']
print len(res)
current_length = 0
for item in res:
current_length += len(item)
print current_length # 39
while current_length > 30:
res = res[:-1]
current_length = 0
for item in res:
current_length += len(item)
print "".join(res) # Customer%20sale%2088%25%20in
That way you will not end up cutting in the middle of a quoting character. And in case you need a different length in the future, you just need to modify the while-loop. Well, code can be made more clean as well ;)

Python 3, for a in list and a in word:

How would I write the following?
a='somelongword'
for r in a_list and for r in a:
if r==x: new_list.append(r)
else: return 1
Obviously, the above for statement isn't correct. How can I rewrite it?
I'm sorry, I tried to make it easier to read!
I want to check, from a list of letters, say a_list, if any of those letters are also in the word a.
At the moment, my code checks this:
for r in reveal_word(a):
if r==guess: new_list.append(r)
else: new_list.append('*')
So, for some letter in the word a, if r is equal to some guessed letter, then append that letter to a list. Otherwise, when that letter is not equal to that guessed letter, append asterisks to the new list.
It's for a piece of code for hangman.
So, I want it to cycle through a list of guessed letters, check if the 'secret' word contains any of those letters and if it does, add them to a new list and where it doesn't contain those letters, add asterisks.
I'll then add this to a dictionary object of key 'guessed_word' or something...
Assuming this is what you want
>>> word ='somelongword'
>>> guesses = ['a', 'd', 'm', 'e', 's']
>>> [c if c in guesses else '*' for c in word]
['s', '*', 'm', 'e', '*', '*', '*', '*', '*', '*', '*', 'd']
Printed as a string
>>> print(''.join([c if c in guesses else '*' for c in word]))
s*me*******d
That looks like what you would want for hangman.
Try:
a='somelongword'
for r in a_list:
if r in a:
if r==x: new_list.append(r)
else: return 1

If a string ends in a vowel of the following characters?

If a string doesn't end in s, x, ch, sh or if it doesn't ends in a vowel, then I need to add a 's'. This works for tell and eat but doesn't work for show. I am not able to figure out why.
This is the code I wrote:
if not re.match(".*(s|x|ch|sh)",s):
if re.match(".*(a|e|i|o|u)",s):
s = s+'s'
return s
else:
return s
Use endswith instead. It takes a single string, or a tuple of strings, and returns True if the string has any of the given arguments as a suffix.
cons = ('s', 'x', 'ch', 'sh')
vowels = ('a', 'e', 'i', 'o', 'u')
if not s.endswith(cons + vowels):
s += 's'
return s
You need $ at the end of your regex if you only want to match the end of the string.

Categories