Wordle (an update)

Guy Lipman
3 min readJan 16, 2022

--

A week ago, after a few days of playing the online game Wordle, I wrote a blog post and coded a strategy for playing the game.

Unfortunately, almost immediately, my strategy showed its weaknesses!

So, I have adjusted my strategy over the week, and thought I’d write an updated post on what has changed.

First, I will diagnose what went wrong last Saturday. My initial guess was TRACE. Using my algorithm from last week, I knew that it could only be one of seven words: [‘CRACK’, ‘CRAMP’, ‘CRANK’, ‘CRASH’, ‘CRAWL’, ‘CRAZY’, ‘FRANC’]. Unfortunately, any of the first six of these is equally good, or more to the point, equally bad. I essentially was unlucky, and took give guesses to get the right word.

In hindsight, my weakness was limiting myself to using one of the remaining words. Instead, had I guessed SLINK or SNAKY, my results would have most likely confirmed the right answer.

As a result, I updated my algorithm to let my guesses include all the words.

def get_score_counts_for_each_guess(guesswords, words):
outer_results = {}
for guess in guesswords:
results = list(get_scores(guess, words).values())
outer_results[guess] = sum(results)/len(results)
return outer_results

Next, I uncovered a misunderstanding of how the scores are calculated. I had assumed that Wordle will colour a cell yellow whenever the letter appears in the word elsewhere. It turns out that the scoring is more helpful than this.

  • If the word in BEATS and you guess SEATS, it won’t mark the first letter as yellow, as it has already confirmed that the final letter is S.
  • If you guess SEATS and the word is FEAST, it won’t mark both of your S’s as yellow, just the first one.

As a result, I had to recode my score_word function:

def score_word(guess, actual):
outcome = [0,0,0,0,0]
tempword = list(actual)
for i, c in enumerate(guess):
if tempword[i]==c:
outcome[i]=2
tempword[i]='0'
for i, c in enumerate(guess):
if c == 0:
if (c in tempword):
outcome[i]=1
tempword[tempword.index(c)] = '0'
return tuple(outcome)

Next, I had a problem on Wednesday, in which the answer was FAVOR, which is an American spelling, so isn’t in my list of words. Interestingly, I did learn that the actual list of accepted words is included in the webpage, but replacing my dictionary with their list seemed a bit like cheating.

In today’s puzzle, I had a dilemma after the second word.

I was lucky enough at this point to know that the word could only be POLAR or MOLAR or SOLAR, each of which I thought was equally likely. And, if I got it wrong, I still wouldn’t know which of the other two it was, meaning I’d have a 33% chance of taking 5 turns to complete the puzzle (or it might even be another word that wasn’t in my dictionary). As a result, I decided to use a word that included a P, an M and an S. Thankfully, it confirmed that S was in my word and M and P weren’t, so I correctly got SOLAR on my fourth turn.

One remaining weakness of my current algorithm is that I judge each of my choices based on how good they are in the current turn. Ideally I would use a recursive strategy, seeing how each word performed in subsequent turns. In fact I tried coding up such an approach, but it took too long to run, for insufficient benefit.

A final comment on the game. I’m still not sure whether my strategy should be to maximise my chance of solving it in a small number of turns, or to minimise my chance of not solving it in 6. At the moment I’m taking the latter, more conservative strategy. But it does mean that I shouldn’t be upset if other people solve a particular day in fewer guesses than me — after all, the people who don’t solve it rarely announce their failure.

--

--

Guy Lipman

Fascinated by what makes societies and markets work, especially in sustainable energy. http://guylipman.com. Views not necessarily reflect those of my employer.