I would like to know a way to find a word from a mix of known letters, from a known list

Question:

For context, it is for a pokemon trivia bot every once in a while on a discord server I’m on, and just wanted to try my skills at making one, although my code only works for the first word in my list (simply a list of all pokemon from Bulbasaur onward by number). I have it so that the user enters the known letters in the format of underscores for blanks, and the letters for the known letters. (ex. Bulbasaur could be ‘_ _ l _ a s a _ _’ (NO spaces between) The user also inputs the number of known letters, for use later in the program. It reads in the list, puts it in an array, and then pulls out all names that match the length of the inputted word. It then begins to compare each letter to each inputted letter to find a match, if the letters matched (amount) equals the inputted numbers given, it shows that ‘the pokemon is: ‘ and the pokemon. the issue is that it only works for one pokemon, and the others do not seem to work. I am going to post the code below, and any help would be greatly appreciated 🙂

also, if the list is needed, I can add that as well somehow (over 500 lines)


poke = input("Letters and spaces given: ")

pokelen = input("Number of Letters given:")
print(pokelen)
pokelen = int(pokelen)

############################################
fid = open('pokemon.txt','r')
f = fid.readlines()
g = []
for i in f:
    i = i.replace('n', '')
    g.append(i)
fid.close()

candidates = []
ticker = 0
while ticker != len(g):
    if len(g[ticker]) == len(poke):
        candidates.append(g[ticker])
    ticker +=1
print(candidates)
############################################
start = []
end = []
ticker1 = 0
ticker2 = 0
done = 0
while done == 0:
    while ticker1 != len(candidates):
        if done == 1:
            break
        word = candidates[ticker1]
        tick = 0
        length = len(word)
        a = 0       
        
        for i in word:
            start.append(i)
            tick += 1
        
        tick = 0
        for i in poke:
            end.append(i)
            tick += 1

        while length != ticker2:
            if start[ticker2] == end[ticker2]:
                a += 1
    
            ticker2 += 1

        if a == pokelen:
            print("The Pokemon is:",word)
            break
        ticker1 += 1
    done = 1
    print(done)
    print(a)
############################################
Asked By: Dusty

||

Answers:

I’m not exactly sure what the code inside the while loop is attempting to do. I think you are looking to match the input with the potential pokemon. As @RufusVS and @Barmar suggest, there might be a way to do what you are looking to do without the details in your while loop.

In the end are you going to try to make a "hangman game"?

import re

############################################
## load names of all pokemon
############################################
#with open('pokemon.txt','r') as fid:
#    all_pokemon = [p_name.strip() for p_name in fid.readlines()]
all_pokemon = ["bulbasaur", "charmander", "eevee", "pikachu", "squirtle"]
############################################

############################################
# ask for a name to try to match
############################################
your_pokemon = input("Letters and spaces given (e.g. pik_chu): ").lower()
your_pokemon_re = re.compile("^%s$" % your_pokemon.replace("_", "."))
############################################

############################################
# from all_pokemon find potential matches
############################################
matches = [pokemon for pokemon in all_pokemon if re.match(your_pokemon_re, pokemon)]
############################################

############################################
# See if we can guess
############################################
for match in matches:
    if input("Is your pokemon %s? " % match).lower() in ("y", "yes"):
        print(":-)")
        break
else:
    print("I give up. I don't seem to know your pokemon")
############################################

In the event that you would like to compare stings character by character (as I think was your intent) then here is a modification to do it that way:

############################################
## load names of all pokemon
############################################
#with open('pokemon.txt','r') as fid:
#    all_pokemon = [p_name.strip() for p_name in fid.readlines()]
all_pokemon = ["bulbasaur", "charmander", "eevee", "pikachu", "squirtle"]
############################################

############################################
# ask for a name to try to match
############################################
your_pokemon = input("Letters and spaces given (e.g. pik_chu): ").lower()
############################################

############################################
# Alternate (slight more traditional) way to find matches
# this compares character by character similar to what you did
############################################
# will ensure same length strings later
candidates = [pokemon for pokemon in all_pokemon if len(your_pokemon) == len(pokemon)]
matches = []
for pokemon in candidates:
    for index, character in enumerate(pokemon):
        if your_pokemon[index] not in (character, "_"):
            ## this character is not a match not a "_" so break
            ## if there is no break, a for loop does it's "else"
            break
    else:
        matches.append(pokemon)
############################################

############################################
# See if we can guess
############################################
for match in matches:
    if input("Is your pokemon %s? " % match).lower() in ("y", "yes"):
        print(":-)")
        break
else:
    print("I give up. I don't seem to know your pokemon")
############################################
Answered By: JonSG

If you don’t want to use regular expressions, you could do it by comparing strings too!

First, let’s define a function that does this:

def is_match(pokemon: str, pattern: str) -> bool :
    return len(pokemon) == len(pattern) and 
           all(x == y for x, y in zip(pokemon, pattern) if y != "_")

This function performs the and operation on two conditions:

  • Checks if the lengths of both strings are the same
  • zips the pokemon and pattern strings, and iterates over them character-by-character, checking to see if all pairs of letters are equal. This is potentially an expensive operation if the strings are large.

The return value is the and of these two conditions. Python’s short-circuiting logic ensures that the potentially expensive operation is only performed if necessary (if lengths of the two strings are unequal, there’s no point even trying to match the pattern).

Assuming we have the list of pokemon and pattern string already:

all_pokemon = ["Bulbasaur", "Ivysaur", "Venusaur", "Charmander", "Charmeleon", "Charizard", "Squirtle", "Wartortle", "Blastoise", "Caterpie", "Metapod", "Butterfree"] # and so on...

pattern = "__l_asa__".lower()

We just need to loop through our list of pokemon, run the is_match function on each one, and select only those for which is_match is true.

pokemon = []
for p in all_pokemon:
    if is_match(p.lower(), pattern):
        pokemon.append(p)
print(pokemon) # Output: ['Bulbasaur']

Or, as a list-comprehension,

pokemon = [p for p in all_pokemon if is_match(p.lower(), pattern)]
print(pokemon) # Output: ['Bulbasaur']

If you tried to match a pattern that works for multiple pokemon, that’s cool too!

pattern = "ch_rm_____"
pokemon = [p for p in all_pokemon if is_match(p.lower(), pattern)]
print(pokemon) # Output: ['Charmander', 'Charmeleon']
Answered By: Pranav Hosangadi

we accomplished something like this with our "fishing" (hangman style) cog in our discord bot.
I might be wrong on what you are wanting here but i think its close at least.

we defined a function to scatter the names.

def scatter(iterable):
    new_list = []
    for i in iterable:
        if random.randint(1, 2) == 1 and new_list.count("_") <= len(iterable) // 2:
            new_list.append("_")
        else:
            new_list.append(i)

    return "".join(new_list)

and use it later to call it and wait for their response.

pkid = (await ctx.bot.db[1].forms.find_one({"identifier": poke.lower()}))[
        "pokemon_id"
    ]
    name = poke
    threshold = 5000

    inventory = details["cast_inv"]
    threshold = round(
        threshold - threshold * (inventory.get("shiny-multiplier", 0) / 100)
    )
    shiny = random.choice([False for i in range(threshold)] + [True])
    exp_gain = [
        t["price"] for t in SHOP if t["item"] == rod.lower().replace(" ", "-")
    ][0] / 1000
    exp_gain += exp_gain * level / 2
    base_time = random.randint(3, 7)
    if rod.lower() == "good-rod":
        rod_bonus = random.randint(1, 1)
        final_time = base_time + rod_bonus
        await asyncio.sleep(final_time)
    elif rod.lower() == "great-rod":
        rod_bonus = random.randint(1, 2)
        final_time = base_time + rod_bonus
        await asyncio.sleep(final_time)
    elif rod.lower() == "super-rod":
        rod_bonus = random.randint(1, 3)
        final_time = base_time + rod_bonus
        await asyncio.sleep(final_time)
    elif rod.lower() == "ultra-rod":
        rod_bonus = random.randint(2, 5)
        final_time = base_time + rod_bonus
        await asyncio.sleep(final_time)
    else:
        rod_bonus = random.randint(0, 1)
        final_time = base_time + rod_bonus
        await asyncio.sleep(final_time)
    
    scattered_name = scatter(name)
    e = discord.Embed(title=f"You fished up a... ```{scattered_name}```")
    e.set_footer(text=f"You have 10 Seconds to guess the Pokemon's name to catch it!n")
    try:
        await embed.edit(embed=e)
    except discord.NotFound:
        await ctx.send(embed=e)

    def check(m):
        return (
            m.author == ctx.author
            and poke.lower() in m.content.lower().replace(" ", "-")
        )

    try:
        await ctx.bot.wait_for("message", check=check, timeout=15)
    except asyncio.TimeoutError:
        await ctx.send(f"TIME'S UP!nUnfortunately the {poke} got away...")
        return

    pokedata = await ctx.bot.commondb.create_poke(
        ctx.bot, ctx.author.id, poke, shiny=shiny
    )
    ivpercent = round((pokedata.iv_sum / 186) * 100, 2)

you can find our fishing cog on our open source repo for the full "idea" of what we did and hopefully it can help. on github /skylarr1227/dittobot-open

Answered By: Sky
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.