Combing function values into one list

I am trying to create a gameboard with 16 rows and two columns that have game words on each of the rows.

Per the hint system I need to generate words that have 0,1,2,3,5 letters in common with the password.

My problem is, I have generated the functions and now I need to combine them into one list.

I feel that my code is lengthy and wrong. I was wondering if someone could review it and tell me how to combine these lists or a hint in the correct direction.

https://paste.pythondiscord.com/DHZA

I am trying to create a gameboard with 16 rows and two columns that have game words on each of the rows.

Per the hint system I need to generate words that have 0,1,2,3,5 letters in common with the password.

My problem is, I have generated the functions and now I need to combine them into one list.

I feel that my code is lengthy and wrong. I was wondering if someone could review it and tell me how to combine these lists or a hint in the correct direction.

https://paste.pythondiscord.com/DHZA

Now the solution code shows it in a very clear way and the instructions say build it from scratch it does not have to be an exact replica. I want to be able to show this as a project on my portfolio, but just copying and pasting is not enough. How much am I allowed to take from the solution code and still have a viable project?

automate the boring stuff with python solution code:

https://inventwithpython.com/bigbookpython/project33.html

As projects go, we should not include classroom projects in our portfolio unless they are truly written from scratch. They are learning tools, not something we would add to our CV.

Attribution is one way around using code we have not written ourselves. Credit back to the source of the code snippet in both the code itself, and in the readme for the project. Use followable links in the readme, and a URL in the code comment.

Nobody is going to fault you for using someone else’s code as long as you are forthright and transparent about it. This is known in the academic world as citing. Just go the bottom of any scientific paper and there you will find a reference section wherein are listed all the works the author(s) has/have cited in their paper.

Without looking at your code, let’s consider that we have two functions that each return a list. If they are the same length, we can combine them into a single list using zip() which will pair up corresponding values.

both = zip(func_a(args), func_b(args))

The list can either be consumed, or cast to a list (or tuple, even) whereby it is consumed en masse but safely tucked away for re-referencing.

It would be best if we reduce this problem to just this single objective in a sandbox setting, or just post it here so we can copy it to our own environment. Think from scratch how best to illustrate your problem in single, simplified program. Once we arrive at the result you want, you will have code all ready to be plugged into the main program.

import sys 
import random 
# Outline
# Find a file or list of words to import from. sevenletterwords.txt will do.
# I need to have a file opener for that. .readlines()

name = "Python"
# introduce game
def introduce():
    print(f"Agent {name}! The evil dictator Kim Jong Un has decided to launch the nukes. All of humanites hopes rest on your shoulders to hack into the system and stop the launch. You will see a series of possible words that are the password. Use our hint system software to determine if you are closs to guessing the password. You are our last hope....")

# from the text file pull out all of the game words.
def get_full_game_word_list():
    with open('sevenletterwords.txt', 'r') as file:
        full_list_of_words = [line.strip().upper() for line in file]
        # test print(full_list_of_words)
        return full_list_of_words

# here is my global variable holding the full game word list possible. 
full_game_word_list = get_full_game_word_list()

# I set up a container to start adding words that will meet the criteria necessary for the game.
game_word_list = [] 

# This function generates a password for the player to guess. 
def get_password(full_game_words_list):
    password = random.choice(full_game_words_list)
    # print("test: This is a test for the correct word: " + correct_word)
    return password

# I store the password in a varaible. 
password = get_password(game_word_list)

# assigning the new password to the game_word_list
game_word_list.append(password)

print(game_word_list)

# garbage characters system
garbage_chars = ['~!@#$%^&*()_+-={}[]|;:,.<>?/']

# Via the hint system I need to generate a word with 0 matching letters. 
# def no_letters_match_word(password, game_word_list):
#     while len(game_word_list) < 3:
#         if password == game_word_list < 0:
#         game_word_list.append(zero_letter_word)
        

Could we have the sevenletterwords.txt file, please? You should be able to upload it being it is only text. We can then open it or transfer it to our computer.

Going to investigate this further since I’m not convinced the return line needs that much indentation. The open is pretty much closed after the file is consumed in the comprehension. The return has no part in with.

@mtf

andrews_hacking.txt (2.6 KB)

We need the text file to be able to test this code. I’m interested in running it on my machine as that is something I have complete control over.

We still lack the most vital component, the text file.

are you referring to the sevenletterwords text file that holds all of the words?

Or the code itself?

My github has every file of the project included. Let me see if I can download the text file and upload it. I think I have been misunderstanding you this whole time.

The file, itself. We need to base file.

If it is going to be on this forum, then we need to keep it exposed here. What is the point of diving one way and another?

andrews_hacking.txt (3.0 KB)

this would be the program file or the base file

sevenletterwords.txt (3.9 KB)

the word bank from the project

oh my goodness you specified the file name at that point in time!

its uploaded now.

Okay, we have something to work with. It’s dreadfully late here so must trundle off to bed, but will take it up on the morrow. Cheers!

1 Like

Just got the code up and running.

Agent Python! The evil dictator Kim Jong Un has decided to launch the nukes. All of humanites hopes rest on your shoulders to hack into the system and stop the launch. You will see a series of possible words that are the password. Use our hint system software to determine if you are closs to guessing the password. You are our last hope.... 
 Okay agent Python, here are list of the possible passwords. 
 Type in the word from the available list of words that you believe is the password.

Aside
humanites => humanity’s

Here is the first error message:

Traceback (most recent call last):
  File ".../Scripts/others/secret_agent.py", line 84, in <module>
    main()
  File ".../Scripts/others/secret_agent.py", line 16, in main
    get_matching_letter_words(password, word_list, num_matches, count)
NameError: name 'num_matches' is not defined

Checking …

As suspected, the return line needs to be outdented to the same block level as with.

Added a print statement after word_list = in main().

Checking again …

Squeezed text (62 lines)

Okay, that fixes the problem in get_word_list().

Error message:

Traceback (most recent call last):
  File ".../Scripts/others/secret_agent.py", line 92, in <module>
    main()
  File ".../Scripts/others/secret_agent.py", line 17, in main
    get_matching_letter_words(password, word_list, num_matches, count)
NameError: name 'num_matches' is not defined

This line,

get_matching_letter_words(password, word_list, num_matches, count)

has two undefined variables. Plugging in values, 3, and 3.

Checking …

New error:

Traceback (most recent call last):
  File ".../Scripts/others/secret_agent.py", line 92, in <module>
    main()
  File ".../Scripts/others/secret_agent.py", line 18, in main
    game_word_list = combine_word_lists()
TypeError: combine_word_lists() missing 2 required positional arguments: 'password' and 'word_list'

This is line 18:

game_word_list = combine_word_lists()

If we check the function there are two required positional arguments.

This is where I leave off and let you forge ahead to solve the remaining issues. When you have the code running error free, repost it and we’ll try again.

It has to do with, with, which behaves like an iterator (perhaps?) where return is seen internally, not as a return from the function that holds it. That’s why I un-indented it, to get it out of the with block. This should not be discounted as something you can overcome. It didn’t belong there in my own mind, and as it turned out, we were right.

get_matching_letter_words(password, word_list, num_matches=3, count=3)

How is that line working out? Is it actually passing the velues you’ve assigned? One would expect the defaults to be in the parameters, not the arguments.

Separate that function from the program and perfect it in a sandbox with a fixed set of data and predictable results. When it is working correctly, then plug it in. Fix the thing that needs fixing, away from the rest of the program. Just supply the data, don’t take it from user input or another function.

Study the error messages and address them one a time. Don’t change any code unless it is directly related to the error. When a new error appears it may imply that the earlier error was fixed. One error at a time is the only way to really tackle this.

1 Like

Let’s say we start with the word, ‘widgets’. Seven letters, all different. We wish to match this word to other seven letter words that,

  • do not contain any of the same letters
  • contain only one of the same letter
  • contain one or more of only one letter
  • …

With so many scenarios playing out, we should try to get our program working with the first two of the above problems and look at the rest (many) later.

Write some code that will go through your 445 word list and find all the words that have none of the letters in ‘widgets’; then write it to find any words that have exactly one letter match. This in itself is a challenge, so you see the real challenge you have set out for yourself. Break the problem down, not just in pieces, but levels of difficulty. Both of these challenges are difficult, but once you develop the logic to deal with both of them, you will have the scaffold for all the rest.

Work on the problem, not the project. Little picture, as opposed to big picture. I don’t know who said this, right off, “Love the problem, not the solution.” Maybe it will come up in your travels.

This whole thing seems to lean toward Sets. The first problem, above, is a disjoint sets problem. The others I won’t even begin to entertain along these same lines, but the first one, oh yeah.

Going to work on that, now.

a = [word for word in get_word_list()]
b = [word for word in a if set(word).isdisjoint(set('widgets'.upper()))]
>>> b
    ['ALCOHOL', 'COMPANY', 'FORMULA', 'JOURNAL', 'POPULAR']
>>>

This boils down to a returnable object:

>>> [w for w in get_word_list() if set(w).isdisjoint(set("WIDGETS"))]
['ALCOHOL', 'COMPANY', 'FORMULA', 'JOURNAL', 'POPULAR']
>>> 

In the above context, get_word_list() might be able to return a generator object rather a list. We’ll have to try it out. If we don’t really need the list laying around, then why have it? I’d much rather use clock ticks than memory.

def get_word_list():
    with open("sevenletterwords.txt", "r") as file:
        w = (line.strip().upper() for line in file.readlines())
    return w  #  this is a generator not a list or tuple

Note that there is no including the return in the with block, as discussed. We need the secondary variable, no way around it.

>>> [w for w in get_word_list() if set(w).isdisjoint(set("WIDGETS"))]
['ALCOHOL', 'COMPANY', 'FORMULA', 'JOURNAL', 'POPULAR']
>>> 

For a general analogy, consider the, range object. Regardless the arguments/parameters we feed it, the object is always the same size. Generators are like that too, as are iterators. It’s a whole 'nother world once we get our logic to include those devices. I’m not preaching, mind, since this is a gray area for me, too, but I’m reaching for it just the same in this example. Who says we cannot explore?

Aside

It just occurred to me, though it may even have come up before, we should always see file operations as consumable events. In that context, the above code follows along predictable lines, and makes perfect sense. Keep that word, ‘consumable’ in your mind when exploring/discussing/implementing iterators and generators. I’m only barely getting it woven into my logic fabric but have zero objections or misgivings. Once properly implemented it’s like peaches and cream.

If, per chance we’re getting ahead of you, then blame yourself. You brought the problem to us, and we’re trying to make sense of it. Gonna make a right jolly mess of it unless we get it on track. But that may be for down the road. If you are still studying, then return to that. This will keep. It’s a great challenge, I’ll give you that.

We explored the first scenario, and in the end it actually turned out to be a fairly easy solve. The set.isdisjoint() method came to our rescue. Remember I said you would have to solve both the first and second problem? They are not the same, as you will have discovered. This unique problem, the first one, is completely unlike any of the others we will face. It is the only one that has no intersections.

If we are planning to use set methods beyond this point we better be wise about it, and sure of our stuff. It will not be an easy journey.

Some more food for thought. Note that get_word_list() is meant to return a list for this demo, not the earlier implemented generator. The one you have above is the one to use.

>>> from random import choice
>>> word = 'WIDGETS'
>>> words = get_word_list()
>>> sample = [choice(words) for _ in range(10)]
>>> test = []
>>> for w in sample:
...     x = set(word)                  # reset x each iteration
...     x.intersection_update(set(w))  # an in-place operation
...     test.append((w, x))
... 
...     
>>> test
[
 ('CONCERT', {'T', 'E'}),
 ('PREPARE', {'E'}),
 ('WHEREAS', {'S', 'E', 'W'}),
 ('MYSTERY', {'S', 'T', 'E'}),
 ('LEADING', {'G', 'I', 'D', 'E'}),
 ('MYSTERY', {'S', 'T', 'E'}),
 ('MEASURE', {'S', 'E'}),
 ('TOTALLY', {'T'}),
 ('SOLDIER', {'S', 'I', 'D', 'E'}),
 ('TOURIST', {'S', 'I', 'T'})
]
>>> 

the above is edited to fit the window

8.7. sets — Unordered collections of unique elements — Python 2.7.18 documentation

That’s for version 2. Version 3 is likely different to some degree. Still, the old code works in version 3.


It follows that aside from thinking of an optimized algorithm we have a filterable array (the test list).

>>> [*filter(lambda x: len(x[1]) == 1, test)]
[('PREPARE', {'E'}), ('TOTALLY', {'T'})]
>>> 
>>> result = [w[0] for w in [*filter(lambda x: len(x[1]) == 1, test)]]
>>> result
['PREPARE', 'TOTALLY']
>>> 

On the entire word_list, words that have one letter matching our word:

>>> test = []
>>> for w in words:
...     x = set(a)
...     x.intersection_update(set(w))
...     test.append((w, x))
... 
...     
>>> result = [w[0] for w in [*filter(lambda x: len(x[1]) == 1, test)]]
>>> result
[
 'ABANDON', 'ACCOUNT', 'AFRICAN', 'ANALYZE', 'ANYBODY', 'ANYMORE',
 'APPROVE', 'ARRIVAL', 'ATTRACT', 'BALANCE', 'BILLION', 'CAPABLE',
 'CAREFUL', 'CHAMBER', 'CHANNEL', 'CLEARLY', 'COMFORT', 'COMMAND',
 'COMPARE', 'COMPLEX', 'CONCERN', 'CONFIRM', 'CONTACT', 'CONTROL',
 'COUNCIL', 'COUNTRY', 'CRUCIAL', 'ECONOMY', 'EMBRACE', 'ENHANCE', 
 'EQUALLY', 'EXAMPLE', 'EXPLORE', 'FACTORY', 'FACULTY', 'FINALLY',
 'FOREVER', 'FUNERAL', 'HANDFUL', 'HELPFUL', 'HORIZON', 'INQUIRY',
 'JOURNEY', 'LIBRARY', 'MILLION', 'NATURAL', 'NUCLEAR', 'OLYMPIC',
 'OPINION', 'OVERALL', 'PAINFUL', 'PERFORM', 'PORTRAY', 'PREPARE',
 'PRIMARY', 'PRIVACY', 'PROBLEM', 'PROGRAM', 'QUALIFY', 'QUICKLY',
 'RECOVER', 'REPLACE', 'REVENUE', 'ROUGHLY', 'SCHOLAR', 'TOBACCO',
 'TOTALLY', 'UNIFORM', 'UNKNOWN', 'UNUSUAL', 'USUALLY'
]
>>> 

above edited to fit the window

One last pièce de rÊsistance:

>>> from random import choice
>>> word = 'WIDGETS'
>>> words = get_word_list()
>>> test = []
>>> for w in words:
...     x = set(a)
...     x.intersection_update(set(w))
...     test.append((w, x))
... 
...     
>>> points = [*filter(lambda x: len(x[1]) == 1, test)]
>>> result = [w[0] for w in points if w[0].count(w[1].pop()) == 2]
>>> result
[
 'BILLION', 'CONTACT', 'EMBRACE', 'ENHANCE', 'EXAMPLE', 'EXPLORE',
 'FOREVER', 'INQUIRY', 'MILLION', 'OPINION', 'PREPARE', 'RECOVER',
 'REPLACE', 'TOTALLY'
]
>>> 

After testing, it appears the generator works just fine in this model, so we don’t need the list.

def get_word_gen():
    with open("sevenletterwords.txt", "r") as f:
        w = (x.strip().upper() for x in f.readlines())
    return w  # this is a generator
word = 'WIDGETS'
test = []
for w in get_word_gen():
    x = set(word)
    x.intersection_update(set(w))
    test.append((w, x))
    
points = filter(lambda x: len(x[1]) == 1, test)
result = [w[0] for w in points if w[0].count(w[1].pop()) == 2]

print (result)
[
 'BILLION', 'CONTACT', 'EMBRACE', 'ENHANCE', 'EXAMPLE', 'EXPLORE',
 'FOREVER', 'INQUIRY', 'MILLION', 'OPINION', 'PREPARE', 'RECOVER',
 'REPLACE', 'TOTALLY'
]

Given the word, ‘SITDOWN’ we got,

[
 'ALCOHOL', 'ATHLETE', 'BATTERY', 'CHANNEL', 'ENHANCE', 'EXPRESS',
 'MESSAGE', 'PASSAGE', 'THEATER'
]

There is a lot to sift through here, but if you take it slowly and catch up on the reading. and do lots of trial and error theorizing, it will come to fruition, this program you are attempting to write. Keep it simple, work with the pieces, and divide things up, include difficulty stages. At each stage devise testing mechanisms so you don’t miss any special cases along the way. You see what I mean by having a larger challenge than you initially envisioned?


One should add that this code ran blisteringly fast. I didn’t even have my hands free of keyboard. All this complexity… Threaded correctly with a generator. and an iterator… Clock ticks, not memory.