As you probably know, Python is really great at parsing text, filtering and sorting. What you might not know, as I didn’t until yesterday (when I went looking for it), that there is a PyPI library available with lists of English words, making it easy to import a standard word list for all your searching needs.
I have to make a confession here. Over the Christmas holiday, I allowed myself a little indulgence of playing (single-player) word games on my phone, in which I had a limited selection of letters with which I had to make English words of given lengths. Only problem is, sometimes I got stumped by the letters I was given. No matter how long I stared at the screen, I just couldn’t see any more words that the app was expecting me to deliver – and I couldn’t skip the rest of the level, either.
This functionality was of course by design. For a price, the app will provide hints to help you solve the hardest puzzles, but out of principle, I just couldn’t bring myself to pay real money for this. On the other hand, if I couldn’t solve the puzzles without hints, then I was blocked from continuing to play.
You can see where this is going.. 🙂
First, to install the English word list, I used pip (I did it on a Python 3.8.5 environment but I expect it will work equally well on other versions):
C:\Users\Agent Smith>call C:\ProgramData\Anaconda3\Scripts\activate.bat experiment3
(experiment3) C:\Users\Agent Smith>pip install english-words
Collecting english-words
Downloading english-words-1.0.3.tar.gz (352 kB)
|████████████████████████████████| 352 kB 3.2 MB/s
Building wheels for collected packages: english-words
Building wheel for english-words (setup.py) ... done
Created wheel for english-words: filename=english_words-1.0.3-py3-none-any.whl size=352088 sha256=bb189ee3b3c209f8b3608b1dfc75709e794ea6f25ddd1a9a13ea27addb763963
Stored in directory: c:\users\agent smith\appdata\local\pip\cache\wheels\a8\e6\be\028f88f1b1fd742fa710dd72a583813f6245b8528b8e6cbaf8
Successfully built english-words
Installing collected packages: english-words
Successfully installed english-words-1.0.3
(experiment3) C:\Users\Agent Smith>
Then, having launched the Python interpreter, I defined the following (very simple) function, right from the interactive prompt:
>>> from english_words import english_words_lower_alpha_set as words
>>> def findword(letters, length=0):
... return [word for word in words if (length in [0, len(word)]) and (set(letters) >= set(word)) and all([list(letters).count(letter) >= list(word).count(letter) for letter in letters]) ]
...
>>>
What the findword() function does is, given a text string (the letters variable), it returns a subset (in the form of a list) of the english_words.english_words_lower_alpha_set word set provided by the installed package. The subset is composed of the words that match all three criteria:
- length in [0, len(word)]: If a nonzero length is included as an argument in the function call, then we want to capture only those words that have the exact length specified
- set(letters) >= set(word): words captured by the filter should not include letters that are not available, though not all available letters have to be in the words captured
- all([list(letters).count(letter) >= list(word).count(letter) for letter in letters]): here we ensure that letters are not used more than they are allowed to be used. (If your game allows re-use of the letters you have been given, then you may leave out this last condition.)
Here are a few examples of findword() in action:
>>> findword('skhay')
['a', 'ah', 'ak', 'as', 'ash', 'ashy', 'ask', 'h', 'ha', 'has', 'hay', 'hs', 'k', 'kay', 'ks', 'ky', 's', 'sa', 'say', 'shaky', 'shay', 'shy', 'sky', 'y', 'yah', 'yak', 'ys']
>>> findword('lodge', 4)
['doge', 'dole', 'geld', 'gold', 'loge', 'ogle']
>>> findword('grica')
['a', 'ac', 'air', 'ar', 'arc', 'c', 'ca', 'car', 'cia', 'cigar', 'crag', 'craig', 'g', 'ga', 'gar', 'i', 'ia', 'ir', 'ira', 'r', 'rag', 'rca', 'ri', 'rica', 'rig', 'riga']
>>> findword('tupes')
['e', 'es', 'est', 'et', 'p', 'pest', 'pet', 'ps', 'pus', 'put', 's', 'se', 'sept', 'set', 'setup', 'st', 'step', 'sue', 'suet', 'sup', 't', 'ts', 'u', 'up', 'upset', 'us', 'use', 'ut']
>>> findword('skeet')
['e', 'eke', 'es', 'est', 'et', 'k', 'ks', 's', 'se', 'see', 'seek', 'set', 'skeet', 'st', 't', 'tee', 'ts']
>>> findword('stluo')
['l', 'lo', 'lost', 'lot', 'lotus', 'lou', 'ls', 'lust', 'o', 'os', 'oust', 'out', 's', 'slot', 'so', 'sol', 'sou', 'soul', 'st', 't', 'to', 'ts', 'u', 'us', 'ut']
>>> findword('blame')
['a', 'abe', 'abel', 'able', 'al', 'alb', 'ale', 'am', 'amble', 'b', 'bale', 'balm', 'bam', 'be', 'beam', 'bel', 'bela', 'bema', 'blame', 'e', 'el', 'elba', 'elm', 'em', 'l', 'la', 'lab', 'lam', 'lamb', 'lame', 'lea', 'm', 'ma', 'mabel', 'mae', 'male', 'mba', 'me', 'meal', 'mel']
>>> findword('blame', 4)
['abel', 'able', 'bale', 'balm', 'beam', 'bela', 'bema', 'elba', 'lamb', 'lame', 'male', 'meal']
>>> findword('damel', 4)
['dale', 'dame', 'deal', 'lame', 'lead', 'made', 'male', 'mead', 'meal', 'meld']
>>>
Of course, there is an ethical component to all this. Personally, I don’t feel bad about using my own code to get past the blocks in the app that (IMHO) ought not be there to begin with. On the other hand, one could imagine using code like this to obliterate one’s friends (or enemies?) while playing something like Words with Friends. I personally wouldn’t do that because I would want the satisfaction of knowing that I won fairly, but I can’t stop you. 🙂
Leave a Comment