Formatting two lists into a single dictionary - python

I'm coding a hangman game for my Computer Science class, and I'm trying to create a dictionary that has each character of the word given and a 0 to notate whether it's been guessed or not.
gamestart = 0
guesses = 5
gamewin = 0
while gamestart == 0:
word = input("Welcome to hangman!" + "\nEnter a word: ")
if word.find(" ") > -1:
print("\nSorry Please use one word only!\n")
elif word.find(" ") == -1:
gamestart = 1
for i in range(len(word)):
wordspacing = [0] * i
wordstore = list(word)
wordstore = dict(zip(wordspacing, wordstore))
print(wordstore)
however when I try to put the two lists I have together I get the output:
Welcome to hangman!
Enter a word: word
{0: 'r'}
Could someone identify the reason for this happening. I would also like a little bit of criticism in terms of efficiency.

The problem is that you are making a dictionary of 0 to letters, and each key can only have one value.
Try just using a dictionary comprehension
wordstore = {letter: 0 for letter in word}

To achieve the desired output using your method you need to switch the order of the zipped objects
wordstore = dict(zip(wordstore, wordspacing))
Also for wordspacing, you don't want to continually assign a value to wordspacing, plus you only end up with [0,0,0] instead of [0,0,0,0] since the last value of i is 3 and not 4. So go with the following
wordspacing = [0] * len(word)

Related

Why does my wordle logic give the wrong result when the character in the guess is repeated too many times?

Following the principles of wordle, each guess is checked and a five letter output is given. G represents green for letters that are the same and in the right place, yellow for letters that the same in the wrong place, and B for no matches.
def output_guess(guess:str, word: str, dictionary: list):
"""
Checks guess for game errors, and outputs current "score" to user
parameters: user's guess, word from dictionary, dictionary
return: none
"""
output = ""
if len(guess) == 5 and guess in dictionary:
for i in range(len(word)):
if guess[i] == word[i]:
output += "G"
elif guess[i] in word:
output += "Y"
else:
output += "B"
else:
print("Word not found in dictionary. Enter a five-letter english word.", end = "")
print(output)
If the answer for example were the five letter word eerie, I would want to receive these outputs for the following guesses:
Guess 1: epees GBYYB
Guess 2: peeve BGYBG
Guess 3: order BYBYB
Guess 4: eerie GGGGG
All of my guesses for my code receive the correct output aside from Guess 3. For Guess 3: order, I get BYBYY instead of BYBYB.
Your logic is incorrect. It is not sufficient to check simply that the character exists in the word, you also need to count the characters in the input and make sure there are an equal or smaller number in the word
import collections
word = "eerie"
guess = "order"
word_counts = collections.Counter(word) # Count characters in the word
guess_counts = collections.defaultdict(int) # Create an empty dict to count characters in guess
output = []
for g, w in zip(guess, word):
guess_counts[g] += 1 # Add to count of current character
if g == w:
# Correct character, correct position
output += "G"
elif guess_counts[g] <= word_counts[g]:
# The minimum that `guess_counts[g]` can be is 1
# Why? We set it in the first line of this loop
# For this condition to be true, word_counts[g] must be > 0
# (i.e `word` must contain `g`)
# And we must have seen `g` fewer (or equal) times than
# the word contains `g`
output += "Y"
else:
output += "B"
print(output)
I used collections.Counter instead of manually counting the letters, and collections.defaultdict(int) so I don't need to check if the dict contains the character before incrementing it.

How to count words that end with a letter? (python)

I am a beginner and this is what I came up with so far. However, it does not output the correct number of words that end with "a" or "b." Any tips on how to correct this code?
names = input("Enter list of names: ")
name = names.split(" ")
num = len(name)
ab = ""
print("Number of words:", num)
for i in range(num):
if name[i] == ' ':
if name[i-1] == a:
ab.append() + " "
elif name[i-1] == b:
ab.append() + " "
a_b = ab.split(' ')
print("Number of words that end with a or b: ",len(a_b))
In Python boolean (True and False) are the same as the integers 1 and 0. This means you can use sum() to count True booleans. str.endswith() returns a boolean. This means you can just do this:
words = ["stab", "drama", "hello", "magma", "test"]
a_b = sum(word.endswith(('a', 'b')) for word in words)
# 3
z_c = sum(word.endswith(('z', 'c')) for word in words)
# 0
Any tips on how to correct this code?
Others have answered alternative, working solutions, but I want to try to point out the specific things wrong in your code snippet and tips to correct.
First here's a copy of your snippet with some simple debugging prints along with their output (running in python 3.10).
names = "Barry Lima Bab"
name = names.split(" ")
print(f"{name=}") # name=['Barry', 'Lima', 'Bab']
num = len(name)
print(f"{num=}") # num=3
ab = ""
print("Number of words:", num)
for i in range(num):
print(name[i]) # Barry -> Lima -> Bab
if name[i] == ' ':
if name[i-1] == a:
ab.append() + " "
elif name[i-1] == b:
ab.append() + " "
print(f"{ab=}") # ab=''
a_b = ab.split(' ')
print(f"{a_b=}") # a_b=['']
Breaking things down step by step like this is a great starting point for better understanding what's going on.
if name[i] == ' ': Notice the value of name[i] so this check never resolves to True meaning the code inside never runs
if the inner code did run, you'd hit a NameError: name 'a' is not defined. Did you mean: 'ab'? because a is undefined. Probably meant 'a' here. same for b
In my example, name[i-1] would be 'Bab' -> 'Barry' -> 'Lima' which is probably not what you're expecting. This is because you're getting the -1th, 0th, 1st items in name. to get the last letter from the name, you want name[i][-1] here
if you then get into either of the furthest inside conditions, you'd encounter AttributeError: 'str' object has no attribute 'append' which happens because append is for list not str. You couldchange ab to be a list to use this in which case you'd want ab.append(name[i]) or you can use string concatenation like ab += " " + name[i] or using str.concat
1 last note of interest, you may have noticed your code as-is really likes to return and say that there's 1 item. This is because the above issues always (if the program doesn't break) leaves ab == '' and ''.split(' ') => [''] and thus len(ab.split(" ")) => 1
1 tip that I think would help in code comprehension is that the name variable here is not a single name string like it implies. It's actually a list[str]. I'd probably denote the variables something more like names_str: str vs names: list[str] or raw_names vs names. Then just use something like for name in names: and not worry about indexes. You can also use name.endswith('a') instead of name[-1] == 'a' for better readability.
Eventually you can combine these into a list comprehension for maximal succinctness -> len([name for name in names if name.endswith('a') or name.endswith('b')]).
words = ["ab", "bab", "pa", "pap"]
result = 0
for word in words:
if word[-1] in "ab":
result += 1
print(result)
As a list comprehension:
words = ["word1a", "word2b", "word3", "word4", "word5a"] # ['word1a', 'word2b', 'word5a']
filtered_words = [word for word in words if word[-1] in "ab"]
filtered_words = [word for word in words if word.endswith(("a", "b"))] # better, if you have multiple endings you want to search for with different lengths
len(filtered_words) # 3
name[i-1] is the previous name in the list, not the last character of the current name in the loop.
There's no need to append the matching names to a string. If you just need the count of matches, increment a counter variable.
You need to put a and b in quotes to make them strings, not variables.
names = input("Enter list of names: ")
name = names.split(" ")
matches = 0
print("Number of words:", len(name))
for cur_name in name:
if cur_name.endswith('a') or cur_name.endswith('b'):
matches += 1
print("Number of words that end with a or b: ", matches)
Use the endswith string method to detect if the last letter is "a" or "b"
names = ["bob","boba","larry","curly","moe"]
count = 0
for name in names:
if name.endswith("a") or name.endswith("b"):
count += 1
print(f"{count=}") # count=2

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 can I insert the same word from a list that was randomly chosen earlier in Python?

I am creating a random sentence generator for a course assignment. I have everything pretty much figured out but there is a spot in the sentence template we have to use where I need to first randomly choose a word, then repeat that randomly chosen word again in the string. The problem occurs after random.choice(place) when I need to immediately use whatever word was chosen again. How can I grab that same word that was randomly chosen and use it again?
import random
verb = ["dancing", "driving", "running"]
place = ["rain", "forest", "city"]
adjective = ["heavy", "long", "hard"]
count = 1
while count > 0:
generate = int(input('Press 1 to generate a new sentence or 2 to exit.\n'))
if generate == 1:
print('I like to go',random.choice(verb),'in the',random.choice(place),',but',place,'should not be too',random.choice(adjective),'.')
count = 1
else:
print('Ok, Goodbye!')
count = 0
The simple solution is to use a variable to store your random.choice(place) before printing it, so you avoid randomizing it twice.
if generate == 1:
myplace = random.choice(place)
print('I like to go',random.choice(verb),'in the',myplace,',but',myplace,'should not be too',random.choice(adjective),'.')
count = 1
Just for fun, you could also use format to substitute names in a string without needing a variable.
if generate == 1:
print('I like to go {verb} in the {place}, but {place} should not be too {adjective}.'.format(verb=random.choice(verb),place=random.choice(place),adjective=random.choice(adjective))
count = 1

Placing my integers into a list in python

I am creating a program that uses the Caesar cipher to encode and decode messages. So far I am building the groundwork, I am trying to get my list of characters the user gives me and make a list of integers using ord...So far all I am having trouble with is getting my integers that I have received back into a list.
import random
encode_decode = input("Do you want to encode, or decode? (e/d)")
shift_amount = input("Please enter the shift amount for your message")
if encode_decode == "e" or encode_decode == "E":
user_words_unrefined = input("Enter your message to encode!")
elif encode_decode == "d" or encode_decode == "D":
user_words_unrefined = input("Enter your message to decode!")
user_words_refined = list(user_words_unrefined)
Alphabet = [chr(i) for i in range(ord('a'), ord('z') + 1)]
Counter = 0
for i in range(len(user_words_refined)):
user_words_numbers = (ord(user_words_refined[Counter]))
user_numbers_list = [user_words_numbers]
print(user_numbers_list)
Counter += 1
Input ("Hello, Party people!")
output
it prints them all on seperate lines with square brackets around them...any ideas?
[72]
[101]
[108]
[108]
[111]
[44]
[32]
[80]
[97]
[114]
[116]
[121]
[32]
[112]
[101]
[111]
[112]
[108]
[101]
Instead of using a for loop that ranges over the length of the input string, you could use a list comprehension.
user_numbers_list = [ord(letter) for letter in user_words_refined]
This should sort you out. see the comments in the code. I will be happy to provide further explanation if need be
user_numbers_list = [] #initialise the list
Counter = 0
for i in range(len(user_words_refined)):
user_words_numbers = (ord(user_words_refined[Counter]))
user_numbers_list.append(user_words_numbers) #add to the end of the list
print(user_numbers_list)
Counter += 1
The best bet is actually list comprehension... see Jordan Lewis's answer for a neater approach
Change the last part to something like
user_numbers_list = []
Counter = 0
for i in range(len(user_words_refined)):
user_words_numbers = (ord(user_words_refined[Counter]))
user_numbers_list.append(user_words_numbers)
print(user_numbers_list)
Counter += 1
Also, consider iterating directly through user_words_refined, like
for word in user_words_refined:
user_words_numbers = ord(word)
user_numbers_list.append(user_words_numbers)
print(user_numbers_list)
Just as a side note, instead of
encode_decode = input("Do you want to encode, or decode? (e/d)")
you could use
encode_decode = input("Do you want to encode, or decode? (e/d)").lower()
so you don't have to use too many or's in your if statement.

Categories

Resources