Boost your Wordle streak with Go
Programming Snapshot – A Go Wordle Cracker
Wordle, a simple online word game, took the world by storm in February. Mike Schilli has developed a command-line tool to boost his Wordle streak using some unapproved tactics.
It's often difficult to predict what will fail and what will generate massive hype, but no one would have expected the Scrabble-style Wordle [1] game to become an Internet hit in 2022. Entire magazine articles deal with the phenomenon [2], people at work show off their results every day, and the New York Times acquired the game from the developer for millions of dollars [3].
The game involves guessing a five-letter word. If the player enters the word correctly, they win the game. If the player's guess doesn't quite get the word, the game marks letters that are in the word and in the right place in green and letters that are in the word but in the wrong place in yellow. Wordle marks guessed letters that do not appear in the target word in gray. To win, you have to guess the word in no more than six attempts, using the app's clues in the smartest way possible.
Automate Me!
Worlde picks words from the Scrabble dictionary, but excludes conjugations or plurals as solutions. In the English version, CAMEL would appear, but not CAKES. Alas, Wordle will accept all five-letter Scrabble words, including plurals, as guesses, of which there are 12,972 in the English language [4].
The game's simple rules make writing a computer program to run against the puzzles a no-brainer. After all, Wordle color-codes the guesses, and a program can use these clues to filter out entries from a complete word list, whittling down the options in each round. The player just needs to pass in the Wordle clues to a helper program in a coded format: I'll use 2
for a letter match, 1
for a letter in the wrong place, and
for letters that do not occur at all. For example, if the puzzle solution is CAMEL and I guessed LAMER, the code would be 12220
: The L at the beginning of the guess is in the wrong place, the middle letters AME are spot on, and the final R does not occur in the target word.
In Figure 2, my wordle
cracker program suggests OATER as the first word. The reason for this unusual word is that it contains a bunch of common vowels, and checking them right at the start often quickly takes you to a solution – but I'll return to that subject later. On the official Wordle page, Figure 1 shows that the word suggested by the program returns three yellow partial matches for O, A, and R, which means these letters occur in the target word, but in another location. If I now pass in this information to the cracker as oater 11001
, the Go program then lists 110 matches
(i.e., there are still 110 words left on the list). Everything else has been eliminated based on the evaluation. At this point, you could type l
to list these 110 words and select one manually, but instead I'll just blindly follow the cracker's choice, ABORD.
The Wordle web page in Figure 1 evaluates this new guess with two green full hits for A and O, while R is still in the wrong place as shown by the yellow highlighting. If I now pass in this result to the cracker as abord 20210
, the cracker immediately decides that only two words are left from the initial list, AROHA and AROMA, and it displays them without further ado. Because it is unlikely that the New York Times would use the New Zealand Maori word for love and affection (aroha) as the solution, I go with AROMA and ignore the cracker's suggestion. Figure 1 confirms that this is the right choice.
Rating Algorithm Exposed
How does the Go program whittle down the word list? First of all, my Wordle cracker needs a reference list of valid words to choose from. A list of Scrabble words [4] is a good starting point. Words in the file are in all caps, one per line, of which there are 279,496. Because Wordle only allows five-letter words, line 23 in Listing 1 filters out everything else, leaving us with 12,972 entries. If you're wondering why the Go program determines the length of each word using the function RuneCountInString()
from the utf8 package, that's because I might want to support foreign languages one day, and their multibyte characters. If you limit the scope to English, a call to length()
will do instead.
Listing 1
dict.go
01 package main 02 03 import ( 04 "bufio" 05 "os" 06 "strings" 07 "unicode/utf8" 08 ) 09 10 const WordleLen = 5 11 12 func dictRead(file string) ([]string, error) { 13 words := []string{} 14 15 f, err := os.Open(file) 16 if err != nil { 17 return words, err 18 } 19 20 s := bufio.NewScanner(f) 21 for s.Scan() { 22 word := strings.ToLower(s.Text()) 23 if utf8.RuneCountInString(word) != WordleLen { 24 continue 25 } 26 words = append(words, word) 27 } 28 err = s.Err() 29 if err != nil { 30 return words, err 31 } 32 33 return words, nil 34 }
Listing 1 defines Wordle's supported word length in a constant named WordleLen
in line 10. In other words, it could easily tackle six-letter Wordles if you just changed one line. The dictRead()
function starting in line 12 expects the name of the file with the word list. It opens the file for reading in line 15 and uses a line-by-line scanner in line 20 to read all the words one by one starting in line 21. The lists contain words in uppercase, just like the Scrabble tiles, which explains why line 22 converts the words to lowercase to allow you to enter them in lowercase, relieving you from holding down the Shift key for input.
Start Cracking
The main()
program in Listing 2 defines the Scrabble dictionary file as scrabble.txt
in the current directory. You can download the file online [4]. The startWord
variable in line 8 defines the word that the algorithm will suggest first in absence of any hints, and I'll use "oater" because it has a nice assortment of vowels.
Listing 2
wordle.go
01 package main 02 03 import ( 04 "fmt" 05 ) 06 07 const dictFile = "scrabble.txt" 08 const startWord = "oater" 09 10 func main() { 11 dict, err := dictRead(dictFile) 12 if err != nil { 13 panic(err) 14 } 15 16 newWord := startWord 17 18 for round := 0; ; round++ { 19 list(dict, false) 20 21 if round != 0 { 22 newWord = bestBang(dict) 23 } 24 fmt.Printf("Try next: '%s'\n", newWord) 25 fmt.Printf("hints(%d)> ", len(dict)) 26 27 var word, score string 28 fmt.Scanf("%s %s", &word, &score) 29 if len(score) == 0 { 30 if word == "l" { 31 list(dict, true) 32 } else { 33 fmt.Printf("Invalid input\n") 34 } 35 continue 36 } 37 dict = filter(dict, word, score) 38 } 39 } 40 41 func list(matches []string, full bool) { 42 if len(matches) > 30 && !full { 43 fmt.Printf("%d matches ('l' to list).\n", len(matches)) 44 } else { 45 for _, w := range matches { 46 fmt.Printf("%s\n", w) 47 } 48 } 49 }
The for
loop starting in line 18 guides the user through the rounds of guesses. The Scanf()
function in line 28 waits for user input. The user can either type l
to list the words that are still in the race or enter the result of a guess in the previously discussed oater 01201
format. For the list
function, the code calls list()
starting in line 41 to output the remaining words in the array slice dict
line by line. The full
flag here determines whether list()
displays massively long lists or only those that contain 30 words or fewer. wordle()
outputs short lists automatically in each round; the full list is only displayed if explicitly requested by the user pressing l
.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
So Long Neofetch and Thanks for the Info
Today is a day that every Linux user who enjoys bragging about their system(s) will mourn, as Neofetch has come to an end.
-
Ubuntu 24.04 Comes with a “Flaw"
If you're thinking you might want to upgrade from your current Ubuntu release to the latest, there's something you might want to consider before doing so.
-
Canonical Releases Ubuntu 24.04
After a brief pause because of the XZ vulnerability, Ubuntu 24.04 is now available for install.
-
Linux Servers Targeted by Akira Ransomware
A group of bad actors who have already extorted $42 million have their sights set on the Linux platform.
-
TUXEDO Computers Unveils Linux Laptop Featuring AMD Ryzen CPU
This latest release is the first laptop to include the new CPU from Ryzen and Linux preinstalled.
-
XZ Gets the All-Clear
The back door xz vulnerability has been officially reverted for Fedora 40 and versions 38 and 39 were never affected.
-
Canonical Collaborates with Qualcomm on New Venture
This new joint effort is geared toward bringing Ubuntu and Ubuntu Core to Qualcomm-powered devices.
-
Kodi 21.0 Open-Source Entertainment Hub Released
After a year of development, the award-winning Kodi cross-platform, media center software is now available with many new additions and improvements.
-
Linux Usage Increases in Two Key Areas
If market share is your thing, you'll be happy to know that Linux is on the rise in two areas that, if they keep climbing, could have serious meaning for Linux's future.
-
Vulnerability Discovered in xz Libraries
An urgent alert for Fedora 40 has been posted and users should pay attention.