Introducing {wordle} - a package for helping solve Wordle puzzles



The {wordle} package contains code to assist in finding good candidate words for Wordle.

“Wordle” itself is a guess-a-word puzzle playable online.

The game plays like the old ‘mastermind’ board game, but with letters instead of coloured pins. The gameplay is as follows:

  1. Enter a word as a guess for the hidden target word.
  2. Any letters which are within the hidden target word are coloured in yellow.
  3. Any letters which match exactly the letter in the hidden target word are coloured green
  4. Figure out a new candidate word as a guess for the hidden target word, and go back to Step 1.

In the following game of Wordle, the first guess was eaten, the second was arise, and then the third guess really only has one good option given the constraints revealed so far: aside. This was the hidden target word, which means the puzzle is solved!

The process of finding good candidate words given letters which have been seen so far is a good match for regular expressions. This package aims to help you find these good candidate words.

What’s in the box

  • Wordle R6 Class is the primary way of finding candidate words. It has the following methods:
    • $new() to start a new object to help with a new puzzle. Use the word_file argument to set the location of a list of words to start with. This defaults to /usr/share/dict/words (which should work on macOS and many unix-ish systems).
    • $words to get a list of candidate words given the words and responses that have been seen so far
    • $update() to notify the object of what the latest word was, and the colour responses received back from the game for each letter.
  • filter_words() is a stateless function for filtering a list of words by various constraints on letter position.
    • The Wordle class is a stateful wrapper around filter_words()


You can install from GitHub with:

# install.package('remotes')

Solving a puzzle with Wordle

puzzle <- wordle::WordleHelper$new(nchar = 5)
head(puzzle$words, 20)
#>  [1] "aahed" "aalii" "aargh" "aarti" "abaca" "abaci" "aback" "abacs" "abaft"
#> [10] "abaka" "abamp" "aband" "abase" "abash" "abask" "abate" "abaya" "abbas"
#> [19] "abbed" "abbes"

Guess eaten

There are a lot of uncommon words in the word list, and the online puzzle doesn’t accept some of them, so you’ll need to pick out a likely word - in this case I picked: eaten

Update puzzle state with the word played and the response:

puzzle$update("eaten", c('yellow', 'grey', 'grey', 'grey', 'grey'))
head(puzzle$words, 20)
#>  [1] "bebop" "becks" "bedim" "befog" "begum" "beigy" "belch" "bells" "belly"
#> [10] "below" "bemix" "bemud" "bergs" "berko" "berks" "berms" "berob" "berry"
#> [19] "beryl" "besom"

Guess rodeo

Update puzzle state with the word played and the response:

puzzle$update("rodeo", c('yellow', 'grey', 'grey', 'yellow', 'grey'))
#>   [1] "bergs" "berks" "berms" "berry" "beryl" "biers" "birle" "birse" "breis"
#>  [10] "brers" "brews" "breys" "bribe" "brise" "brize" "brule" "brume" "burke"
#>  [19] "burse" "cerci" "ceric" "clerk" "crems" "creps" "crepy" "cress" "crews"
#>  [28] "crime" "cripe" "crise" "cruse" "cruve" "curie" "curse" "curve" "femur"
#>  [37] "ferly" "fermi" "ferms" "ferry" "fibre" "fiers" "fiery" "firie" "fleur"
#>  [46] "fresh" "frise" "frize" "furze" "gebur" "germs" "germy" "grews" "greys"
#>  [55] "grice" "grike" "grime" "gripe" "grise" "grize" "grufe" "grume" "gryce"
#>  [64] "gryke" "grype" "gurge" "heirs" "herbs" "herby" "herls" "herms" "herry"
#>  [73] "huers" "icers" "jerks" "jerky" "jerry" "jirre" "kefir" "keirs" "kerbs"
#>  [82] "kerfs" "kerky" "kerry" "kiers" "kurre" "kyrie" "lehrs" "leirs" "lemur"
#>  [91] "lerps" "liers" "livre" "lucre" "lurve" "merch" "mercs" "mercy" "meril"
#> [100] "meris" "merks" "merls" "merry" "murre" "perch" "percs" "peril" "peris"
#> [109] "perks" "perky" "perms" "perps" "perry" "pervs" "pervy" "piers" "preif"
#> [118] "prems" "premy" "preps" "press" "prexy" "preys" "price" "prime" "prise"
#> [127] "prize" "pryse" "puers" "purge" "purse" "query" "quire" "sehri" "seirs"
#> [136] "serfs" "seric" "serif" "serks" "serrs" "serry" "serum" "shire" "sieur"
#> [145] "skers" "skyre" "smerk" "speir" "sperm" "spire" "sprue" "spyre" "sucre"
#> [154] "suers" "surge" "sweir" "swerf" "swire" "umbre" "ureic" "users" "usure"
#> [163] "verbs" "verry" "vezir" "viers" "virge" "weirs" "wersh" "wreck" "xeric"
#> [172] "xerus" "yerks" "zerks"

Guess shire

Update puzzle state with the word played and the response:

puzzle$update("shire", c('grey', 'grey', 'grey', 'green', 'yellow'))
#> [1] "berry" "clerk" "ferry" "jerry" "kerry" "merry" "perry" "query" "verry"

Guess merry

Update puzzle state with the word played and the response:

puzzle$update("merry", c('grey', 'green', 'green', 'green', 'green'))
#> [1] "berry" "ferry" "jerry" "kerry" "perry" "verry"

Guess ferry


Expert Users: filter_words()

The Wordle R6 class is just a stateful wrapper around a core function called filter_words().

In general you wouldn’t need to call this function for solving a Wordle puzzle but it might come in handy for other word puzzles.

In this example, I’m searching for a word:

  • with 9 letters
  • starting with p
  • containing v and z somewhere, but not as the first letter
  • containing only one z
  • without an a or an o in it
words <- readLines("/usr/share/dict/words")

  words            = words,
  exact            = "p........",
  wrong_spot       = c("vz", "", "", "", "", "", "", "", ""),
  min_count        = c(v = 1),
  known_count      = c(z = 1, a = 0, o = 0)
#> [1] "pulverize"