Even Simple Wordle Solvers are Pretty Good

You're likely pretty familiar with Pareto principle, even if you don't associate it with that name. It's often referred to as the 80-20 rule and describes the phenomenon that about 80% of results come from about 20% of the work.

When prioritizing software projects, it's often very good to think hard about what percentage of your potential customers will care about a feature. If you're building a tool to list files matching some pattern, are most of your customers going to care about file types or file sizes? Are they going to care about permissions on files or not? If you can figure out what most of your audience wants, you can build that part of the product first and have users providing feedback earlier in your development process.

Once you have a base set of users, you'll likely want to "finish" the project and include all the features you thought of when you started working on things. You'll want to be able to filter by file owners and last modified timestamps, even if most of your users don't care most of the time. But as the Pareto principle implies, you'll spend 80% of your time working on all these niche use cases when you've already provided significant value to your use base. Your return-on-effort decreases significantly as work advances.

Applying the Pareto Principle to a Wordle Solver

I wanted to write a Wordle solver for myself. Not because I particularly care about my Wordle scores, but because I think it's a fun problem and a well defined problem space. I can copy-paste the valid answers and guesses from the Wordle page and come up with a strategy for solving any passible word.

The problem is that I quickly decided to do a pretty complex solution. I wanted to write a program that would check the results of every possible guess and see which guess reduced the answer space the most (on average). I had grand plans to write out a depth-based search so I could search 2 or 3 or 6 guesses ahead and make sure the guess I was making was the optimal one.

But first I had to write a program that could correctly calculate the results of guesses. I had to write in a way to filter down the set of valid answers as guesses eliminated various options. And, importantly, I had to test that these things were working.

So I wrote a simple Wordle solver. And it's shockingly good.

#[test]
fn test_words() {
    let mut failed_words: Vec<&str> = Vec::new();

    for word in Dictionary::default().valid_answers {
        let mut dictionary = Dictionary::default();
        let guesses = &CharacterIndexStrategy::default().guess_word(word, &mut dictionary);
        if guesses.len() > 6 {
            failed_words.push(word);
        }
    }
    failed_words.sort();

    let expected_failed_words = vec![
        "goner", "graze", "jaunt", "jazzy", "jolly", "mover", "patch", "power", "prank",
        "putty", "saucy", "shave", "swore", "tight", "tower", "tried", "vaunt", "vouch",
        "waste", "watch", "wight", "willy", "winch", "wound", "wrath",
    ];

    assert_eq!(failed_words, expected_failed_words);
}

My simple solver only fails on 25 words out of 2309 in the answer dictionary, which is likely better than what I can accomplish as a human. And this was just the simplest solution I thought of: counting the number of times characters appear at an index in a word, then using those numbers to assign each word a score.

Yes, the answers are a bit strange sometimes. saree is an odd choice of word to start with, to be sure. And this thing loves double letters as early guesses. But the machine is doing what I told it to do, and as a result it's doing the task better than I can. For all intents and purposes, the project is done!

But I want to keep working on it. I'd love to get a better algorithm working, but that requires more thinking and a lot more processing time. It currently takes about 10 minutes to run the test above to verify the algorithm works in a consistent manner, and any more complicated systems will almost certainly take longer. At this point it's a passion project, where I tackle the remaining 20% knowing that the project has already accomplished most of its goals.