I Created a Bot to Solve Wordle so I Never Have to Ever Again
Tags: javascript, fun, beginners
Published: 2022-02-12 05:05:17

---
title: I Created a Bot to Solve Wordle so I Never Have to Ever Again
published: true
description: I hear everyone loves Wordle, that's cool. However I suck at this. I'm better at code then English - so I wrote code to solve it for me, everyday.
tags: #javascript #fun #beginners
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u6xi43spef27ss519qh2.png
canonical_url: https://uilicious.com/blog/automate-wordle-via-uilicious/
series: Solving WORDLE with Uilicious
---

TLDR: I wrote a Wordle solver bot with Javascript and UIlicious. You can run [this snippet](https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL) any day to get your daily Wordle solution. Try and see if you can get a better score than the bot! **Feel free to edit it and optimise the solver algorithm!**

> The wordler solver is covered in 3 parts, which covers the
> - UI interaction code (this article)
> - Wordle Statistical model, and the math behind it [(link here)](https://dev.to/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396)
> - Unit testing and benchmarking of the wordle solver (@ TODO) 

<a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL">
![Uilicious solving a wordle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nhdfah09z5ywrtu0tr9.gif)</a>
<figcaption>You can edit or rerun the script, by clicking on the GIF</figcaption>

If you haven't already heard of Wordle, it's a fun puzzle game that challenges you to guess a valid five-letter word in six tries or less.

My wife is really addicted to Wordle, and she's been bugging me to try it every day. I suppose for her, there's a thrill in getting the right word in the least amount of tries. And because we're both gamers and math-nerds, naturally we talk about strategies to solve Wordle in the least number of guesses. But when it comes to actually putting those theories to use and trying my hand at Wordle, I came to realise...

**I suck at Wordle!**

English isn't my strong suite per say...

I speak Javascript better.

![harry-potter-allora](https://uilicious.com/blog/content/images/2022/02/harry-potter-allora.gif)<figcaption>I speak many languages, including Python ;-)</figcaption>

So what better way to solve Wordle, then to write a program to solve it for me. HA!

![Bill Nye shouting "Science!"](https://uilicious.com/blog/content/images/2022/02/bill-nye-mind-blown.gif)<figcaption>Let's science this @*#$!</figcaption>

After a weekend of hacking I finshed coding up my Wordle solver bot using JS and UIlicious, and I would say it works pretty well, and has managed to make the correct guess mostly within 3-4 tries so far. 

![A solved Wordle using UIlicious](https://uilicious.com/blog/content/images/2022/02/Screenshot-2022-02-10-at-4.42.13-PM-2.png)
<figcaption>Tada, solved wordle with a bot, on UIlicious</figcaption>

It has mostly stirred up my wife's competitive spirit and annoyed her, since the bot does better than her on most days. But at least she's not bugging me everyday to play Wordle anymore.

![Women yelling at cat](https://uilicious.com/blog/content/images/2022/02/womanyellingcat-1573233850.jpg)<figcaption>Is it cheating to write a bot? No, I'd say it's totally fair game!</figcaption>

Curious to find out how the Wordle solver work? Let me explain!

## Basic setup

We're going to use [UIlicious](https://uilicious.com) to interact with the Wordle website, fill in our guesses, and evaluate the hints. While UIlicious is primarily a cross-browser UI testing tool, nobody said that's all you can use it for, so why not solve some Wordles with it. 

Let's setup our solver bot on [UIlicious Snippets](https://snippet.uilicious.com ) - think of it as Codepen, but for UI testing, and it's entirely free to use for executing tests on Chrome. UIlicious lets you run Javascript, so I'm going to be writing the solver in JS.

The first thing we'll have to do get the bot to navigate to the Wordle site. We use the `I.goTo` command to go to the official Worldle site (https://www.powerlanguage.co.uk/wordle/).

```javascript
I.goTo("https://www.powerlanguage.co.uk/wordle/")
```

Edit: Since the acquisition of Wordle by NY Times, there's now a cookie banner, which we need to either accept or dismiss, using the `I.click` command.

```javascript
I.click("Reject") // to reject cookies and close the cookie banner
```

Let's just make sure the app has loaded, with quick check for the phrase "Guess the Wordle" using the `I.see` command:

```javascript
I.see("Guess the Wordle")
```

Finally, let's dismiss the tutorial popup by using `I.click` with a pixel offset to click outside the popup:
```javascript
// clicks 40px down and 80px right from the top-left corner of the webpage
I.click('/html/body', 40, 80)
```

Now, we'll just hit that "Run" button to run the browser automation script, and make sure this brings us to an empty Wordle board to play with.

![Navigating to Wordle with UIlicious](https://uilicious.com/blog/content/images/2022/02/Screenshot-2022-02-12-at-1.26.30-AM.png)<figcaption>Now we're ready to Wordle!</figcaption>

## Next steps

Ok, now let's dive into writing more complex parts of the solver.

The bot will need to be able to:
1. Enter letters into Wordle
2. Read the hints from Wordle
3. Find the solution based on the hints (I'll explain this in a follow up post)

![Man snapping fingers, saying "Simple"](https://c.tenor.com/uYqsM9uIyuYAAAAC/simple-easy.gif)<figcaption>This will be a piece of cake!</figcaption>

## 1. Entering letters into Wordle

We've only got up to six tries to guess the Wordle, so we'll start with a simple `for` loop to make 6 attempts to guess the word:

```javascript
for(let r=0 ; r<6 ; ++r) {
  // solver code will go in here
}
```

Now we're going to generate a couple of words and enter our guesses into the game. I'm not going to dive into how `solver.suggestWord(gameState)` works at the moment, but what it basically does is suggest a word based on the previous guesses and the revealed hints. I'll explain how the solver works in a follow-up post, stay tuned!

```javascript
let guessWord = null
for(let r=0 ; r<6 ; ++r) {

  // guess the next word
  guessWord = solver.suggestWord(gameState)
  
}
```

After computing the `guessWord`, I need the bot to enter the word into the game board, which is pretty straightforward. I use the command `I.type` to enter the guess word, and `I.pressEnter` to submit the guess. I've also added a small wait for the game board to finish its animation and allow input for the next guess, because the solver can be too fast sometimes and might attempt its next guess before the game board is ready.

```javascript
let guessWord = null
for(let r=0 ; r<6 ; ++r) {
  // ... 
  // Does some stuff to get gameState

  // guess the next word
  guessWord = solver.suggestWord(gameState)
  
  // enter and submit the guess word into the Wordle game board
  I.type(guessWord);
  I.pressEnter();

  // add a small wait for the animation to finish before entering the next guess
  I.wait(2)
  
}
```

And that's it!

## 2. Reading the hints from Wordle

The next important step is after each guess, we need to evaluate the hints to make our next guess. This is a bit trickier, as we'll need to do a bit of scraping to get our hints from the HTML. 

First, lets grab each row of the game board. The Wordle app is entirely written using Web Components (impressive!), so we'll need to make use of `document.querySelector` and `.shadowRoot` to dive into the `<game-app>` element and grab each of the `<game-row>` element which stores important information about the revealed hints.

```javascript
let rowList = document.querySelector("game-app").shadowRoot. //
		querySelector("game-theme-manager"). //
		querySelector("#board").querySelectorAll("game-row");
```

Here's what we'll get when we run this query in the browser console:

![Inspecting the Wordle game-row element](https://uilicious.com/blog/content/images/2022/02/Screenshot-2022-02-12-at-1.39.42-AM.png)<figcaption>Oh look what we have here!</figcaption>

Each of these `game-row` element has two very useful data attributes that we want: 

- `_letters`: tells you guess that you've made for this row, e.g. `"hello"`
- `_evaluation`: an array of hints for each letter, e.g. `["absent", "present", "correct", "absent", "absent"]`. `"correct"` if the letter is in the correct position. `"present"` if the letter is in the word, but is in the wrong position. And `"absent"` if the letter isn't in the word.

Now we're going to build a `state` object to store all of these information and pass it to our solver. The code below is nothing too fancy. We'll wrap this in a function because we want to run it after each guess to get the updated hints.

```javascript
function() {

    // We prepare the state object which we would want to return
    let state = { history: [], pos:[
        { hintSet:[] },{ hintSet:[] },{ hintSet:[] },{ hintSet:[] },{ hintSet:[] }
    ] };

    // Lets get the list of result rows from the UI DOM
    // unfortunately this is more complicated then needed due to shadowDoms
    let rowList = document.querySelector("game-app").shadowRoot. //
    querySelector("game-theme-manager"). //
    querySelector("#board").querySelectorAll("game-row"); //

    // Lets build the state by reading each row
    for(let r=0; r<rowList.length; ++r) {
        let row = rowList[r];
        // a row can be blank, if there was no guess made
        if( row._letters && row._letters.length > 0 ) {
            let word = row._letters;
            // Add it to history list
            state.history.push( word );

            // Iterate each char in the word
            let allCorrect = true;
            for(let i=0; i<5; ++i) {
                let c = word[i];
                if( row._evaluation[i] == "absent" ) {
                    // does nothing
                    allCorrect = false;
                } else if( row._evaluation[i] == "present" ) {
                    state.pos[i].hintSet.push( c );
                    allCorrect = false;
                } else if( row._evaluation[i] == "correct" ) {
                    state.pos[i].foundChar = c;
                } else {
                    throw "Unexpected row evaluation : "+row._evaluation[i];
                }
            }

            // Configure the final "allCorrect" state
            state.allCorrect = allCorrect;
        }
    }

    // And return the state obj
    return state;
}
```

This piece of code above needs to be executed the browser, so I need to tell the Wordle bot (which runs on a sandboxed NodeJS instance) to run the funciton in the browser using `UI.execute`. 
```javascript
function getWordleStateFromUI(){
  return UI.execute(function(){
    // put the above code snippet in here to get the game state
  })
}
```

Okay, now we want to use this function to get the game state after each guess, so let's go back to our `for` loop and update it:
```javascript

let guessWord = null;
for(let r=0; r<6; ++r) {

    // get the game state
    let gameState = getWordleStateFromUI();

    // if we got all the letters correct, we've won, hurray!
    // exit the loop in that case
    if( gameState.allCorrect ) {
        break;
    }

    // guess the next word
    guessWord = solver.suggestWord(gameState);

    // enter and submit the guess word into the Wordle game board
    I.type(guessWord);
    I.pressEnter();

    // add a small wait for the animation to finish before entering the next guess
    I.wait(2);
}
```

Whew! Now that the bot can enter its guesses into the board and get the revealed hints, we can work on writing the algorithm to solve the Wordle in six attempts or less!

## 3. Finding the solution based on the hints... in a follow up post!

Writing the solver algoritm is the biggest challenge, and a whole lot of fun. I spent quite a bit of time reading up on optimal strategies (there's even papers written about this), and tweaking the algoritm. But this deserves a post on it's own, so I'm going to write a follow up post after, stay tune!

### Putting everything together

Finally, let's put together the wordle solver:

Before the start of the `for` loop, we'll initialise the solver with:
```javascript
const solver = new WordleSolvingAlgo(fullWordList, filteredWordList);
```

And after we exit the `for` the loop, we want to query the game state again. At this point, we've either guessed the correct word in less than 6 attempts, or have made 6 guesses but don't know what's the final outcome. We'll get the final game state one more time, and report the outcome using `TEST.log.pass` if the bot won, or `TEST.log.fail` if the bot lost game.

```javascript

// this comes after exiting the for loop...

// get the final game state
let gameState = getWordleStateFromUI();

// is it all correct? 
if( gameState.allCorrect ) {
    // CONGRATS!!!
    I.see("NEXT WORDLE");
    TEST.log.pass("==== END OF GAME ====")
    TEST.log.pass("## The wordle is : "+guessWord.toUpperCase())
} else {
    // DARN! Try better next time!
    TEST.log.fail("==== END OF GAME ====")
    TEST.log.fail("## The last guess was : "+guessWord.toUpperCase())
}
```

Here's what everything looks altogther (except for the solver algo part!)

```javascript

// Open Wordle!
I.goTo("https://www.powerlanguage.co.uk/wordle/")

// to reject cookies and close the cookie banner
I.click("Reject") 

// Is Wordle loaded?
I.see("Guess the Wordle")

// Solve Wordle!
runTheWordleSolver()

//----------------------------------
// This function runs the wordle solver,
// which will make up to 6 attempts to guess the word
function runTheWordleSolver() {
	
    // initialise the solver
    // stay tune for more about the solving algorithm!
	const solver = new WordleSolvingAlgo(fullWordList, filteredWordList)
	
	let guessWord = null;
	
	// make up to 6 guesses
	for(let r=0; r<6; ++r) {
		
        // get the game state (includes the revealed hints)
		let gameState = getWordleStateFromUI();
		
		// if we got all the letters correct, we've won, hurray!
        // exit the loop in that case
		if( gameState.allCorrect ) {
			break;
		}
		
		// guess the next word
		guessWord = solver.suggestWord(gameState);
		
        // enter and submit the guess word into the Wordle game board
		I.type(guessWord);
		I.pressEnter();
		
		// add a small wait for the animation to finish before entering the next guess
		I.wait(2);
	}
	
	// at this point, we either guessed the correct word in less than 6 attempts, or have made 6 guesses and don't know what's the outcome yet.
	
	// get the final game state
	let gameState = getWordleStateFromUI();

	// is it all correct? 
	if( gameState.allCorrect ) {
        // CONGRATS!!!
		I.see("NEXT WORDLE");
		TEST.log.pass("==== END OF GAME ====")
		TEST.log.pass("## The wordle is : "+guessWord.toUpperCase())
	} else {
        // DARN! Try better next time!
		TEST.log.fail("==== END OF GAME ====")
		TEST.log.fail("## The last guess was : "+guessWord.toUpperCase())
	}

}
```

## Drumroll... The moment of Truth!

Let's smash the "Run" button and see how our Wordle bot does!

<a href="https://snippet.uilicious.com/embed/test/public/N5qZKraAaBsAgFuSN8wxCL?stepNum=22&autoplay=0">![Solved Wordle](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fkk0t42h4ns0nmwpyxz8.png)</a>

<figcaption style="margin-top:15px;">Hey, not bad, my bot solved today's Wordle in 4 attempts!</figcaption>

![Jim Carrey says "It's Alive!"](https://c.tenor.com/vMCk9U93WkcAAAAC/jim-carrey-aceventura.gif)<figcaption>Mmmmm, sweet sweet glory!</figcaption>

Stay tuned for part 2. I'll explain the solver engine works, and the math behind it. Or if you want to dive into the full code yourself, you can check it out [over here](https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL), and maybe even replace `WordleSolvingAlgo` with your own algorithm to solve Wordle: 

https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL 

At the mean time ….
**Happy Wordling! 🖖🏼🚀**