Cheating at Wordle? A Python and Streamlit Based Solver
Introduction
Wordle, by the New York Times, is an ultra-popular game that hundreds of thousands (or more) people play daily. I've personally been playing it for what feels like years now, and it has become a daily ritual.
This weekend, I had the itch to code and created a tool that automatically solves puzzles. I know this has been done many times, but that doesn't mean there isn't something to learn along the way.
Let's take a look at how it turned out!
Word Dictionary
The first thing I needed to source was a list of all the valid words. A simple Google search leads you to many Wordle dictionary sources. I tried a few different versions but ultimately ended up with one that contained just under 13,000 unique five-letter words. Note that this is not the official acceptable solution list but rather a long list of valid choices.
What are the Most Common Letters?
One of the strategies of the game is to pick common letters. The more common they are, the more potential you have for getting a hit. Let's say I chose the word Vixen; the letters V and X are uncommon, meaning you will have less chance of eliminating possibilities. Let's look at how the different letters stack up by counting the frequency of each letter.
The visualization shows that S, E, and A are the top three, appearing 6,000 times or more, while J, X, and Q have under 300 occurrences each. We'll use this information more in a moment.
Game Rules and Logic
To assist with solving the puzzles, I needed to create a system for storing the different guess options from the game. The rules of the game are simple. Select a five-letter word, and it tells you with three colors:
- Green ๐: The letter is in the correct spot.
- Yellow ๐: The letter is in the word but in the wrong spot.
- Grey ๐ฉถ: The letter is not in the word in any spot.
I came up with a few simple data structures.
- know_letters: A Python dictionary. These are the green guesses.
- exclude_positions_for_letters: A Python dictionary. Here, we not only need to store the letter as the key but a small list as the value so that when we guess a correct letter in the wrong spot more than once, we can add it to the logic. These are our yellow guesses.
- exclude_letters: This is a python set. A set in Python guarantees that you do not have duplicates in the list. These are the grey guesses.
Here is what they look like in action.
know_letters = {0: 's', 1: 'e'}
exclude_positions_for_letters = {'a': [2], 'r': [0, 4]}
exclude_letters = {'f', 'i', 'y', 'c', 'o'}
Puzzle Solving Algorithm
The algorithm to solve was the most fun to create. It took a lot of fussing to get it to where it is today. I wasn't sure if there was a way to create a machine-learning model out of this, but without any historical game outcome data, I went with the old-fashioned way, hard-coded rules. Here are the rules and weights that I came up with
- Base Score: This is the sum of the letter frequencies from above. A set of more frequent letters in the dataset will increase this number.
- Duplicate Letters: Duplicate letters in Wordle, especially early on, can mean you eliminate fewer words. The penalty here is a 0.5 multiplier on the base score
- Ends with S: Plurals are never a Wordle answer. It doesn't mean you can't have a word that ends in an S; it just means you typically won't select these words. I apply an additional 0.1 multiplier on the score to push these words down.
- Vowel Diversity: Words with many different vowels are typically great guesses. ADEIU is often a favorite pick of players for this reason. Here, I add 1,000 additional points to the score for every unique vowel. (not duplicates like SPOON).
- Top 100,000 English Word Rank: This turned out to be one of the best additions to the model. Using the Python library wordfreq, I could import the function top_n_list and build a raking model of the most commonly occurring words in the English language. This allowed me to sort uncommon words that were lower in ranking. The formula for this is 100,000 / rank number in the top 100k * 1,000. This is then added to the total to reward more common words.
Let's take a look at it in action. Two results from the game below are as follows:
The word WORTS had a larger base score than FORTY because it contains the most common letter in the set, S. However, WORTS ends with an S, which highly penalizes it, and FORTY was ranked 5,130 in the English language, where WORTS didn't appear in that list. The resulting set is a negative score for WORTS, pushing it to the bottom of this list. FORTY is the winner!
User Interface
Streamlit, if you're unfamiliar with it, is a Python-based rapid UI development framework that is extremely popular with data professionals. They have native support for connecting to data warehouses, built-in Pandas Data Frame support, and the power to build visualizations with your favorite tools like Plotly and Vegas. It makes producing quick applications a snap! They even have a great hosting option for free, which is where I deployed this app.
I didn't spend too much time making this nice; I just wanted to make it functional for now. I chose a simple 5x5 grid of input boxes and radio buttons to enter your picks and select which color box Wordle returned. It worked well as a rapid prototype.
In fact, this entire project took only about 8-10 hours to complete, from idea to hosted application.
WordleBrian in Action
As a fan of the game, I don't want to use it on "Today's" puzzle, but there is no reason we can't test it out on the newly released Archives. We'll test this out on March 1st, 2024.
Starter Words
I used the same algorithm above to choose the best starter words based on the criteria above. Upon inspection, these are mostly strong starters. High vowel counts and common letters throughout. There are a few I wouldn't use: anything with repeating letters. However, going wrong with the rest of them would be hard. Here they are.
About, their, there, which, would, other, after, first, think, could, these, where, right, being, years, going, still, those, never, world, great, while, every, state, three.
Let's start with the very first option, ABOUT. This is as good as nearly any I have used.
Next, you can head to the solver and see what is suggested. The first step is to match the Wordle results in the application and submit to see the suggestions. We enter the five letters along the top and then match the colors of the squares to what the game shows you.
After submitting the results, we're left with 306 words, down from 13,000, or only about 1.5% of the dictionary. That's not too shabby! The app now gives you the top 25 suggested next words. Here they are for this option:
other, often, north, month, worth, hotel, costs, throw, noted, notes, voted, tower, topic, motor, posts, votes, forth, metro, voter, toxic, forty, towel, token, intro, opted
Like starter words, the algorithm suggests a set of most probable words. You'll notice the pattern that O and T are included in each word but not in the positions where the yellow squares are marked. We can again select the top choice if we like or any of these options if we feel they fit our mood better. Let's choose OTHER.
We got one yellow letter, R, and eliminated two additional positions on the board for O and T. Let's see what the solver says as we enter the next row.
This brought our possible choices down to 36, and here are the next top guesses:
forty, intro, nitro, vitro, sorts, rosti, torsi, torso, ports, torsk, triol, toric, trigo, torii, dorty, porty, rorty, forts, roton, trios, torts, toros, rotis, tiros, rotls
What stands out here is how the "top 100,000" English word weighting on the algorithm has paid off. You can see common words like FORTY and intro at the start of the list; however, at the end, you have ROTIS, TIROS, and ROTLS. Words that I've personally not heard or used. Let's keep with the flow and use FORTY as our choice.
Tada! ๐ That's it. We got the solution in three guesses using the application. I tested this on 30 or so games from the archive, and I would say most end up solving in four guesses, some in three, and some in five. That follows the same distribution as my play over the years. I'm pretty happy with that!
You can see it for yourself here: https://wordlebrian.streamlit.app, and, of course, you can check out the entire repository on GitHub here: https://github.com/broepke/wordle.
Conclusion
Wordle is a fun and challenging game. It's also a great coding challenge. Figuring out how you approach this takes a little thought and iteration. We saw everything from finding an appropriate target word list to creating a data model to hold our guesses as we iterated through the puzzle effectively. We then built the scoring algorithm that helps us pick the best word and built a user interface to test this on some games. I'm very much looking forward to iterating on this app. As I mentioned above, it only took about 8-10 hours to get here, and now that I'm satisfied with the results, I think it's worth investing a little more time in it.
What would you change? How would you approach solving this problem? Happy Wordle-ing!