Hung Truong: The Blog!

I Created a Robot to Solve Wordle For Me

January 10, 2022 | 8 Minute Read

Unless you’ve been living under a rock for the past few days, you’ve heard of this game online called Wordle. It’s been growing like crazy and the New York Times even wrote a profile on the creator. If you know me, you know I like automating things, so it should be no surprise that I decided to automate playing the game. Here’s a blog post on how I did it.

WTF is a Wordle?

I guess I could start out by explaining what Wordle is, in case you’re reading this blog without knowing what it is, and if you are, why not just play it first?

Anyway, the fun part of Wordle is that there isn’t really a way to cheat. You try to guess a five letter word, and the game gives you feedback for each letter in the word you guessed. Either the letter doesn’t exist at all in the target word, the letter exists in the word but not in that position, or the letter does exist in the word in that position. Letters can repeat, e.g. “silly,” and you get 6 guesses before you lose (I think). I’ve actually never lost, because 6 is a pretty good number of guesses unless you get really unlucky.

If you think about it mathematically, 6 tries times 5 letters means you could theoretically narrow down 25 letters before your last guess. But that’s assuming you only use unique letters and there are words you can actually use to guess those letters (the game doesn’t allow non-words).

There is the problem of narrowing the word down to maybe 1 letter difference but there’s a bunch of words that end in the same letters, e.g.

  • frank
  • drank
  • crank
  • spank

etc. If you get the last three letters then you still need to narrow down what the front ones are, which you could do exploratively, but you can also just keep trying likely words.

Anyway, if it isn’t obvious yet, I probably enjoy the metagame of Wordle more than playing the game itself.

Solving Wordle

So how do you go about automating a game like Wordle? I started by trying to inspect the network requests going back and forth between the web frontend and the backend. Suprisingly, there are no network requests made because the whole game is just a javascript file, and (I think) a hash that determines what the word of the day is. The whole game is just a javascript file that you download and run.

So looking at the javascript file, I saw a bunch of hard to read code (because javacript), and a few interesting arrays.

The first had a bunch of pretty normal looking words. The other had words that looked real, but were less common. My guess is that there are words that can actually be used for the game because a normal person would know them, but another list of words that are valid guesses (because the game doesn’t let you put nonsense in). So I decided to take the first list of words and use it as my “dictionary” of possible choices, rather than just use a linux dictionary of all the obscure words that exist.

The two word lists, possible answers on top and valid obscure words on bottom.

As far as playing the game, you have to start somewhere. I started out by keeping a list of all possible answers (copied from the source), and picking a random one. Once that happens, you get hints for which letters to keep trying and which to discard. The game code actually has three values for letter results: “present,” “absent,” and “correct.”

From these, the basic algorithm to remove words from the possible choices is (for each letter):

  • If present, remove all words that have that letter in that specific position (for example if the word is “farts” and you guess “after,” the first ‘a’ would be present, but never in the first position). Also, remove all words that do not have that letter present in any positions.

  • If absent, remove all words that have that letter in any position.

  • If correct, remove all words that do not have that letter in that position.

That’s basically it, and doing this will quickly whittle down the list of possible choices.

There are some other optimizations that I tried out but I don’t think they have much of a significant effect on how quickly the bot can guess the right answer. For example, I made a sorted list of the most common letters used in English words and had the bot prefer words with the most common letters first, to narrow down results faster. It might be possible to improve this by calculating probabilities or something down to the individual word level but I’m way too lazy (for now) to try that.

A Mac App

I ended up writing a Mac app to run the solver because I knew I could use a webview and make it evaluate some javascript commands. I wrote a few functions for doing things like keying in letters and hitting enter, and reading the results using the “shadow dom” which sounds a little bit like forbidden magic but whatever.

I had to throw in some manual sleep() calls because it would go too fast if I didn’t. Also it looks more like a real person is playing if there’s a slight delay in entering letters.

Here’s what the Mac app looks like solving the puzzle from Jan 8, 2021:

I’ve found that watching my bot play the game is more interesting to me than playing the actual game. Since it’s somewhat randomized, it’s fun to see what combinations of words the app tries, and how quickly it can narrow down its results. I log the list of potential answers after each round, and it’s fun to see how the app “thinks.”

One More Thing: The Bot, But IRL

iDraw: the second most advanced pen plotter known to me.

Making an app to automate something is cool, but I felt like something was still missing. I recently bought a mechanical pen plotter (which I should probably make another blog post about) for making generative art, so I thought it would be fun to program the pen plotter to play the game on my iPad using my Apple Pencil.

The pen plotter is basically a robot that has an arm and can raise and lower a pen (or Apple Pencil) and move it along an x-y axis. I bought an off-brand version of the Axidraw, which has a similar but different board. I tried using the axidraw python library to control the iDraw. It sort of works, but there are some weird differences. For example, the pen down movement seems to bring the pen up instead of down, so everything is reversed. Also the scaling is off, so if I want to move the pen 1 inch, it seems to move it by 2 centimeters instead. I just worked around these issues because I saved a lot of money by buying the cheaper one (it was about half the price).

I wrote a script in Python to take one command line argument which is the word to guess, and hardcoded positions for each letter on the iPad screen. I was thinking I could use some kind of computer vision, AI library to detect positions and translate them for the plotter, but that’s also too much work. Maybe for a different project. The Mac app runs as before, except that it runs the local Python script when guessing, and waits for the script to complete before trying the next guess.

I’m pretty happy with this setup, even though it’s a pain in the ass to get it working, because I have to set the pen height and line the iPad up with the robot correctly. I did end up getting the measurements right for the keyboard keys on the first try, which was cool. It’s just that if the iPad slides on the table then everything is off.

I made a YouTube video that describes this more, and embellished some stuff for entertainment. So if you want to watch that, you can see it here, (and don’t forget to obliterate that like and subscribe button)!

I also pushed the solver specific code to Github. It’s pretty ugly but if you’re interested in how it works, you can check it out. I probably should’ve used regular expressions in hindsight instead of blowing up strings into arrays but I figured that with the word list at around 2,300 words, it really doesn’t matter how inefficient I am!