wordle
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:
- Enter a word as a guess for the hidden target word.
- Any letters which are within the hidden target word are coloured in yellow.
- Any letters which match exactly the letter in the hidden target word are coloured green
- 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
WordleR6 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 theword_fileargument 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).$wordsto 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 latestwordwas, 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
Wordleclass is a stateful wrapper aroundfilter_words()
- The
Installation
You can install from GitHub with:
# install.package('remotes')
remotes::install_github('coolbutuseless/wordle')
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'))
puzzle$words
#> [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'))
puzzle$words
#> [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'))
puzzle$words
#> [1] "berry" "ferry" "jerry" "kerry" "perry" "verry"
Guess ferry

Success!
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
vandzsomewhere, but not as the first letter - containing only one
z - without an
aor anoin it
words <- readLines("/usr/share/dict/words")
filter_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"