What’s Wordle?
Wordle is a small online game that has blown up in popularity recently. It’s premise is simple, you have six tries to guess a five letter word. I’m going to call this the target word. Each guess reveals information about the target word. If a letter you guessed is at the same place in the target word, it’s square in the grid is colored green. If the letter is in the target word but it in the incorrect place, the square is colored yellow. And finally, if the letter is not in the target word the square is blank with no color.
In the example image above, the first word I guessed was beach. “B” is colored green, so the target word starts with a “B”. “E” is colored yellow, so the letter “E” is in the word, but is not the second letter in the word. “A”, “C”, and “H” are not found anywhere in the target word. The next word I guess is brief. “B” is the first letter and the letter “E” is in the word and not in the second place. I got lucky and go too more letters that are in the correct space: “R” and “I”. The “E” is yellow, so the only other place it can be is the last place. Now we only have to guess one more letter. And from the other guesses we know that “B”, “E”, “A”, “C”, “H”, and “F” are not letters that can be in the next to last place. My final guess was the word brine. All the squares are green, and that was in fact the target word.
Frequency analysis?
Frequency analysis is used to determine the frequency of individual letters in the English language. This is shows us what letters are used more frequently than others, for example, the letter “A” is more common in words than “Z”. This can be used for solving a Wordle by guessing the most likely word with the information returned from previous guesses. Thankfully, a frequency analysis of all English letters already exists.
Using this information, we can generate a ‘score’ for each five letter word that is a possible guess or possible Wordle. This score can than be used to determine the most likely guess with the given information. Simple really.
The code
Wordle Solver class
If you want to see the full source code, it’s at the bottom of the post or can be found here. Now, lets walk through the code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from sys import argv
from time import time
# Wordle Solver class
class WordleSolve:
def __init__(self, file_name, start_word):
# Alphabet letters sorted by frequency
self.letter_freq = ["s", "e", "a", "r", "o", "i", "l", "t", "n", "u", "d", "c", "y", "p", "m", "h", "g", "b", "k", "f", "w", "v", "z", "x", "j", "q"]
self.letter_freq_value = [46.1, 44.8, 40.5, 30.9, 29.5, 28.1, 25, 24, 21.4, 18.6, 18.1, 15.7, 15.4, 14.6, 14.2, 13.3, 11.8, 11.5, 10.2, 7.9, 7.7, 5.2, 2.5, 2.4, 2.1, 0.9]
self.letter_pairs = ["th", "he", "an", "in", "er", "nd", "re", "ed", "es", "ou", "to", "ha", "en", "ea", "st", "nt", "on", "at", "hi", "as", "it", "ng", "is", "or", "et", "of", "ti", "ar", "te", "se", "me", "sa", "ne", "wa", "ve", "le", "no", "ta", "al", "de", "ot", "so", "dt", "ll", "tt", "el", "ro", "ad", "di", "ew", "ra", "ri", "sh"]
self.letter_pairs_freq = [33, 30.2, 18.1, 17.9, 16.9, 14.6, 13.3, 12.6, 11.5, 11.5, 11.5, 11.4, 11.1, 11, 10.9, 10.6, 10.6, 10.4, 9.7, 9.5, 9.3, 9.2, 8.6, 8.4, 8.3, 8, 7.6, 7.5, 7.5, 7.4, 6.8, 6.7, 6.6, 6.6, 6.5, 6.4, 6, 5.9, 5.7, 5.7, 5.7, 5.7, 5.6, 5.6, 5.6, 5.5, 5.5, 5.2, 5, 5, 5, 5, 5]
self.target_word = ["_", "_", "_", "_", "_"]
self.yellow_green_list = []
self.not_list = []
self.word_history = []
self.score_list = []
self.start_word = start_word
# Open a list of 5 letter words and save in memory
word_list_file = open(file_name, "r")
self.word_list = word_list_file.read().split()
word_list_file.close()
self.word_list_total = len(self.word_list)
First, argv
is imported for command line arguments and time
is imported to time the script later on. Next, a class WordleSolve
is created and inside it is the constructor function __init__
. In the constructor, several variables are created and initialized. The constructor accepts a file name and the starting word for Wordle. Once in __Init__
, firstly, two variables to help with letter frequency analysis: letter_freq
, letter_freq_value
, letter_pairs
, and letter_pairs_freq
. letter_freq
contains a list of English letters sorted form highest frequency to lowest frequency. letter_freq_value
contains the actual frequency values from largest value to smallest. letter_pairs
and letter_pairs_freq
are the same but for common pairs of letters. Next, some variables that are used later are initilized. target_word
, yellow_green_list
, not_list
, word_history
, score_list
, and start_word
.
For this program, a word list of five letter words is used. Next, the word list provided is opened and the contents are read and stored in a list.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Print a list of words in a box like Wordle does
def box(self, words):
# Box top and bottom
box_top = " ___________________ "
box_spacer = " ------------------- "
output = box_top
for word in words:
output += "\n" + ' | ' + ' | '.join(list(word)) + ' | ' + "\n" + box_spacer
return output
# Get user input
def get_input(self):
text = input("Enter results: ")
input_list = []
for letter in text:
input_list.append(letter)
return input_list
Next, are two simple hepler functions. One for printing out Wordle guesses in a wordle ‘box’ and a function for user input.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# If we know a letter is or isn't in a word, adjust the word list apropriatly
def letter_modify(self, letter, remove):
i = 0
while i < len(self.word_list):
if remove:
if letter in self.word_list[i]:
if letter not in self.yellow_green_list:
self.word_list.pop(i)
self.score_list.pop(i)
continue
if not remove:
if letter not in self.word_list[i]:
self.word_list.pop(i)
self.score_list.pop(i)
continue
i += 1
# If we know what place a letter is or isn't, adjust the word list apropriatly
def letter_place(self, place, letter, remove):
i = 0
while i < len(self.word_list):
if remove:
if letter in self.word_list[i][place]:
self.word_list.pop(i)
self.score_list.pop(i)
continue
if not remove:
if letter not in self.word_list[i][place]:
self.word_list.pop(i)
self.score_list.pop(i)
continue
i += 1
These functions handle modifying the word list after information is received from a guess. letter_modify
accepts a letter and the boolean remove
. If remove
is True
, any words in the wordlist that contain letter
are removed from thre list. If remove
is False
, Any words that don’t have letter
in the word are removed from the wordlist. For example, if a word is guessed and a letter is blank, that means the letter isn’t anywhere in the target word. So, all words containging that letter are removed from the list. Or, if a letter is is definetly in a word, e.g. a yellow blank, remove
is False
and all words without the letter are removed from the list. yellow_green_list
is a list of previous letters that have have already been removed from the list.
letter_place
is used for green or yellow squares. If a word is guessed and a letter is in a green square, than all words without that letter in that place are removed. So, if remove
is True
, all words in the list with letter
in place
are removed. If remove
is False
, all words in the word list that don’t have letter
in place
are removed from the list.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Add known letters in the word to a list
def add_known_letters(self, letter):
if letter not in self.yellow_green_list:
self.yellow_green_list.append(letter)
# Add known letter not in the word to a list
def add_known_not_letters(self, letter):
if letter not in self.not_list:
self.not_list.append(letter)
# Print a few stats
def stats(self):
print("\nWordle: %s" % ' '.join(self.target_word))
print("Known letters in word: %s" % ' '.join(self.yellow_green_list))
print("Known letters not in word: %s" % ' '.join(self.not_list))
print("{0} words remaining, down to {1:.2%} of words.\n".format(len(self.word_list), (len(self.word_list)/self.word_list_total)))
add_known_letters
is used to add letters to a list of letters that we know are in the target word. add_known_not_letters
is used to add letters to a list of list of letetrs that are definetly not in the word. This is for optimization. stats
just prints out some stats on the current Wordle.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Sort word list based on a letter frequency score
def sort_by_freq(self):
self.score_list = []
for word in self.word_list:
i = 1
score = 0
letters = []
for letter in word:
# Add to score for top signifigant letters by position
if i == 1 and letter == "s": score += 11.5
if i == 2 and letter == "a": score += 12.5
if i == 3 and letter == "r": score += 11.7
if i == 4 and letter == "e": score += 12.5
if i == 5 and letter == "s": score += 13.1
if letter not in letters:
# Add the frequency score of a letter to the score.
score += float(self.letter_freq_value[self.letter_freq.index(letter)])
else:
# If a letter is in a word more than once, reduce value of any other occurences. Magic number seems freq/5.
score += float(self.letter_freq_value[self.letter_freq.index(letter)])/5
letters.append(letter)
i += 1
# Now, we add a value based upon frequency of common letter pairs.
for pair in self.letter_pairs:
if pair in word:
score += float(self.letter_pairs_freq[self.letter_pairs.index(pair)])
self.score_list.append(score)
# Sort the list of scores.
zipped_list = sorted(zip(self.score_list, self.word_list), reverse=True)
self.word_list = [self.word_list for score_list, self.word_list in zipped_list]
self.score_list = sorted(self.score_list, reverse=True)
return zipped_list
The function sort_by_freq
is used to sort the wordlist based on a score of the words letter frequency. The function loops over each letter in every word in the wordlist. First, the function checks what position in the word it as at and checks if a certian letter is in that place. For the first letter place, the most common letter is s. If s is in the first place, it adds to the score. Second, it takes the frequency value of the current lettert and adds it to the score. It aslo chcks if the letter is in the word more than once. To optimize Wordle, words with duplicates of letters are less useful. For each duplicate letter, the freqency is divided by 5 then added to the score. Finally, it checks for common letter pairs and adds to the score based on how many andf what pairs are in the word. This creates a frequency score and is stored in score_list
. Once each word has a score, the wordlist and score list are zipped and sorted by the score. word_list
is then sorted and the function returns the zipped_list
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Process input and adjust the word list starting with known letters in the target word
def process_guess(self, input_list, iteration):
# Known letter and position [Green square]
i = 0
for letter in input_list:
if letter == "g":
self.add_known_letters(self.word_history[iteration][i])
self.letter_place(i, self.word_history[iteration][i], False)
self.target_word[i] = self.word_history[iteration][i]
i +=1
# Known letter but not position [Yellow square]
i = 0
for letter in input_list:
if letter == "y":
self.add_known_letters(self.word_history[iteration][i])
self.letter_place(i, self.word_history[iteration][i], True)
self.letter_modify(self.word_history[iteration][i], False)
i +=1
# Known not a letter in the target word [Blank square]
i = 0
for letter in input_list:
if letter == "b":
self.letter_modify(self.word_history[iteration][i], True)
self.add_known_not_letters(self.word_history[iteration][i])
i += 1
Next, a function to process a user guess. The function takes input_list
and iteration
as arguments. input_list
is a list of the results after a guess in the form of bbyygg
. b
is for a blank square, y
is for a yellow square, and g
is for a green square. iteration
is what guess the user is on. This function loops over the results and makes the appropriate changes to the word list, letter lists, and word history.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Wordle logic for running tests
def wordle_logic(self, guess, wordle):
i = 0
output = []
for letter in guess:
if letter == wordle[i]:
output.append("g")
elif letter in wordle:
output.append("y")
elif letter not in wordle:
output.append("b")
else:
print("Something bad happened.")
i += 1
if len(output) != 5:
print("Something bad happened x2.")
return output
And finally, there is a bot function to use for testing optimization and start words. The function takes a guess
and a wordle
and returns a string as if a user was playing. (e.g., in the form of bbyygg
) And that’s the end of the Wordle solving class.
Using the WordleSolver class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# Automatically play Wordle for testing code, start words, etc.
def play_bot(solver, target_list, start_word, verbose):
succsess = []
solver.word_list = []
word_list_sorted = []
score_list_sorted = []
fails = 0
for word in target_list:
solver.word_list.append(word)
# Sort by frequency and backup those values
scores = solver.sort_by_freq()
for word in solver.word_list:
word_list_sorted.append(word)
for score in solver.score_list:
score_list_sorted.append(score)
for wordle in target_list:
if verbose: print("Wordle: %s" % wordle, end='\r')
solver.word_list = []
solver.word_history = []
solver.yellow_green_list = []
solver.score_list = []
# Copy the saved frequency values instead of calculating them again
for word in word_list_sorted:
solver.word_list.append(word)
for score in score_list_sorted:
solver.score_list.append(score)
i = 0
for i in range(0, 6):
if i == 0:
solver.word_history.append(start_word)
guess = solver.wordle_logic(start_word, wordle)
else:
solver.word_history.append(solver.word_list[0])
guess = solver.wordle_logic(solver.word_list[0], wordle)
solver.process_guess(guess, i)
if ''.join(guess) == "ggggg":
succsess.append(i + 1)
break
if len(solver.word_list) == 1:
succsess.append(i + 1)
break
if i == 5:
fails += 1
if solver.word_list[0] in solver.word_history:
solver.word_list.pop(0)
count = 0
for i in succsess:
count += i
return count, succsess, fails
Next, there is function to have a bot ‘solve’ a Wordle. This is used for testing start words and optimization testing. This function takes a WordleSolver
class, list of Wordles, starting guess word, and a verbose boolean as input. The code loops over the Wordles in target_list
and then prints the Wordle if verbose
is true. Next, since the bot function is passed a single instance of WordleSolver
but loops over many Wordles, certain variables like word_list
and word_history
are reinitialized. This is done to avoid having to reload the wordlist for every Wordle. Then, all of the target Wordles are copied into word_list
. Then, the wordlist is sorted with sort_by_freq()
. Then, six guesses are done. If it’s the first guess (i==0
) then the start_word
is used. Otherwise, it uses the first word in the word_list
. This is the most likely word based on letter frequency and the list has had any words removed that will not work. The guesses and Wordle are passed to wordle_logic
which returns the result. The result is processed with process_guess
. If wordle_logic
returns ggggg
or there is only one word left in word_list
, then the Wordle has been solved! It keeps track of the number of guesses, fails, and succsess and returns them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Using each word in the word list as a Wordle, try to solve it and see what the average number of guesses is
def test():
start_word = "least"
successes = []
fails = 0
target_list = []
solver = WordleSolve("5_letter_word_list.txt", start_word)
for word in solver.word_list:
target_list.append(word)
count, successes, fails = play_bot(solver, target_list, start_word, True)
print("Number of wordles: %s" % len(target_list))
print("Number of succsess: %s" % len(successes))
print("Average guesses for successes: %s" % (count/len(successes)))
print("Fails: %s" % fails)
Now the play_bot
function can be used to test the solver with one start word on all possible Wordles! Simply initialize the WordleSolve
class with a starting guess, and a wordlist, then pick and a Wordle list, pick a verbosity and call play_bot
. Next, the test
function simply prints the results of the test. Here is an example of it running:
1
2
3
4
5
Number of wordles: 2315
Number of succsess: 2302
Average guesses for successes: 3.094265855777585
Fails: 13
Time: 17.315351486206055
It’s also possible to test all of the possible start words on all possible Wordles to determine what the best start word is.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Using each word in the word list as a Wordle, try to solve it and see what the average number of guesses is
def test_start_words():
averages = []
target_list = []
start_word_list =[]
start_word = "least"
solver = WordleSolve("5_letter_word_list.txt", start_word)
for word in solver.word_list:
start_word_list.append(word)
for word in solver.word_list:
target_list.append(word)
for start_word in start_word_list:
successes = []
fails = 0
count, successes, fails = play_bot(solver, target_list, start_word, False)
averages.append((count/len(successes), start_word))
print((count/len(successes), start_word), len(successes))
result = sorted(averages)
print("The best start word is %s with a average of %f Wordle guesses." % (result[0][1], result[0][0]))
The function test_start_words
copies all of the words in word_list
to a start_word_list
. Then, it uses the play_bot
function to test every single starting word and record the results. After that finally finishes, it zips the results and sorts them to see what start word yielded the lowest average number of guesses to solve all Wordles. (Spoiler: The best start word with this script is least) And finally, there is a function for a user to use the Solver and play on the website.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Solve a Wordle with a user
def play():
start_word = "least"
solver = WordleSolve("5_letter_word_list.txt", start_word)
solver.stats()
print("Begin with:")
scores = solver.sort_by_freq()
print(solver.box([start_word]))
solver.word_history.append(start_word)
i = 0
for i in range(0, 6):
user_input = solver.get_input()
solver.process_guess(user_input[:5], i)
solver.stats()
print("Top words:")
try:
for x in range(0, 4):
print("""%s (Freq score: %.2f)""" % (solver.word_list[x], round(solver.score_list[x], 2)))
except Exception as e:
pass
if len(solver.word_list) == 1:
if ''.join(user_input[:5]) == "ggggg":
print(solver.box(solver.word_history))
else:
print(solver.box(solver.word_history + [solver.word_list[0]]))
print("Done! Hooray!!")
exit()
if solver.word_list[0] in solver.word_history:
solver.word_list.pop(0)
print(solver.box(solver.word_history + [solver.word_list[0]]))
solver.word_history.append(solver.word_list[0])
print("Oof. Good luck picking final word!")
This function is similar to the play_bot
functionality, but the user’s input from the website replaces wordle_logic()
. The stats are also printed as well as the guess history in the ASCII text box. The last bit of the script gives some command line args for different test or playing as a user.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Main
if __name__ == "__main__":
startTime = time()
if len(argv) == 1:
print("Try again.")
elif argv[1] == "test":
test()
elif argv[1] == "play":
play()
elif argv[1] == "test_start_words":
test_start_words()
else:
print("Try again.")
executionTime = (time() - startTime)
print('Time: ' + str(executionTime))
Full Code
The code can be found here on GitHub and below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#!/usr/bin/python3
# Quick and dirty wordle solver
# b is blank square, y is yellow square, g is green square.
from sys import argv
from time import time
# Wordle Solver class
class WordleSolve:
def __init__(self, file_name, start_word):
# Alphabet letters sorted by frequency
self.letter_freq = ["s", "e", "a", "r", "o", "i", "l", "t", "n", "u", "d", "c", "y", "p", "m", "h", "g", "b", "k", "f", "w", "v", "z", "x", "j", "q"]
self.letter_freq_value = [46.1, 44.8, 40.5, 30.9, 29.5, 28.1, 25, 24, 21.4, 18.6, 18.1, 15.7, 15.4, 14.6, 14.2, 13.3, 11.8, 11.5, 10.2, 7.9, 7.7, 5.2, 2.5, 2.4, 2.1, 0.9]
self.letter_pairs = ["th", "he", "an", "in", "er", "nd", "re", "ed", "es", "ou", "to", "ha", "en", "ea", "st", "nt", "on", "at", "hi", "as", "it", "ng", "is", "or", "et", "of", "ti", "ar", "te", "se", "me", "sa", "ne", "wa", "ve", "le", "no", "ta", "al", "de", "ot", "so", "dt", "ll", "tt", "el", "ro", "ad", "di", "ew", "ra", "ri", "sh"]
self.letter_pairs_freq = [33, 30.2, 18.1, 17.9, 16.9, 14.6, 13.3, 12.6, 11.5, 11.5, 11.5, 11.4, 11.1, 11, 10.9, 10.6, 10.6, 10.4, 9.7, 9.5, 9.3, 9.2, 8.6, 8.4, 8.3, 8, 7.6, 7.5, 7.5, 7.4, 6.8, 6.7, 6.6, 6.6, 6.5, 6.4, 6, 5.9, 5.7, 5.7, 5.7, 5.7, 5.6, 5.6, 5.6, 5.5, 5.5, 5.2, 5, 5, 5, 5, 5]
self.target_word = ["_", "_", "_", "_", "_"]
self.yellow_green_list = []
self.not_list = []
self.word_history = []
self.score_list = []
self.start_word = start_word
# Open a list of 5 letter words and save in memory
word_list_file = open(file_name, "r")
self.word_list = word_list_file.read().split()
word_list_file.close()
self.word_list_total = len(self.word_list)
# Print a list of words in a box like Wordle does
def box(self, words):
# Box top and bottom
box_top = " ___________________ "
box_spacer = " ------------------- "
output = box_top
for word in words:
output += "\n" + ' | ' + ' | '.join(list(word)) + ' | ' + "\n" + box_spacer
return output
# Get user input
def get_input(self):
text = input("Enter results: ")
input_list = []
for letter in text:
input_list.append(letter)
return input_list
# If we know a letter is or isn't in a word, adjust the word list apropriatly
def letter_modify(self, letter, remove):
i = 0
while i < len(self.word_list):
if remove:
if letter in self.word_list[i]:
if letter not in self.yellow_green_list:
self.word_list.pop(i)
self.score_list.pop(i)
continue
if not remove:
if letter not in self.word_list[i]:
self.word_list.pop(i)
self.score_list.pop(i)
continue
i += 1
# If we know what place a letter is or isn't, adjust the word list apropriatly
def letter_place(self, place, letter, remove):
i = 0
while i < len(self.word_list):
if remove:
if letter in self.word_list[i][place]:
self.word_list.pop(i)
self.score_list.pop(i)
continue
if not remove:
if letter not in self.word_list[i][place]:
self.word_list.pop(i)
self.score_list.pop(i)
continue
i += 1
# Add known letters in the word to a list
def add_known_letters(self, letter):
if letter not in self.yellow_green_list:
self.yellow_green_list.append(letter)
# Add known letter not in the word to a list
def add_known_not_letters(self, letter):
if letter not in self.not_list:
self.not_list.append(letter)
# Print a few stats
def stats(self):
print("\nWordle: %s" % ' '.join(self.target_word))
print("Known letters in word: %s" % ' '.join(self.yellow_green_list))
print("Known letters not in word: %s" % ' '.join(self.not_list))
print("{0} words remaining, down to {1:.2%} of words.\n".format(len(self.word_list), (len(self.word_list)/self.word_list_total)))
# Sort word list based on a letter frequency score
def sort_by_freq(self):
self.score_list = []
for word in self.word_list:
i = 1
score = 0
letters = []
for letter in word:
# Add to score for top signifigant letters by position
if i == 1 and letter == "s": score += 11.5
if i == 2 and letter == "a": score += 12.5
if i == 3 and letter == "r": score += 11.7
if i == 4 and letter == "e": score += 12.5
if i == 5 and letter == "s": score += 13.1
if letter not in letters:
# Add the frequency score of a letter to the score.
score += float(self.letter_freq_value[self.letter_freq.index(letter)])
else:
# If a letter is in a word more than once, reduce value of any other occurences. Magic number seems freq/5.
score += float(self.letter_freq_value[self.letter_freq.index(letter)])/5
letters.append(letter)
i += 1
# Now, we add a value based upon frequency of common letter pairs.
for pair in self.letter_pairs:
if pair in word:
score += float(self.letter_pairs_freq[self.letter_pairs.index(pair)])
self.score_list.append(score)
# Sort the list of scores.
zipped_list = sorted(zip(self.score_list, self.word_list), reverse=True)
self.word_list = [self.word_list for score_list, self.word_list in zipped_list]
self.score_list = sorted(self.score_list, reverse=True)
return zipped_list
# Process input and adjust the word list starting with known letters in the target word
def process_guess(self, input_list, iteration):
# Known letter and position [Green square]
i = 0
for letter in input_list:
if letter == "g":
self.add_known_letters(self.word_history[iteration][i])
self.letter_place(i, self.word_history[iteration][i], False)
self.target_word[i] = self.word_history[iteration][i]
i +=1
# Known letter but not position [Yellow square]
i = 0
for letter in input_list:
if letter == "y":
self.add_known_letters(self.word_history[iteration][i])
self.letter_place(i, self.word_history[iteration][i], True)
self.letter_modify(self.word_history[iteration][i], False)
i +=1
# Known not a letter in the target word [Blank square]
i = 0
for letter in input_list:
if letter == "b":
self.letter_modify(self.word_history[iteration][i], True)
self.add_known_not_letters(self.word_history[iteration][i])
i += 1
# Wordle logic for running tests
def wordle_logic(self, guess, wordle):
i = 0
output = []
for letter in guess:
if letter == wordle[i]:
output.append("g")
elif letter in wordle:
output.append("y")
elif letter not in wordle:
output.append("b")
else:
print("Something bad happened.")
i += 1
if len(output) != 5:
print("Something bad happened x2.")
return output
# Automatically play Wordle for testing code, start words, etc.
def play_bot(solver, target_list, start_word, verbose):
succsess = []
solver.word_list = []
word_list_sorted = []
score_list_sorted = []
fails = 0
for word in target_list:
solver.word_list.append(word)
# Sort by frequency and backup those values
scores = solver.sort_by_freq()
for word in solver.word_list:
word_list_sorted.append(word)
for score in solver.score_list:
score_list_sorted.append(score)
for wordle in target_list:
if verbose: print("Wordle: %s" % wordle, end='\r')
solver.word_list = []
solver.word_history = []
solver.yellow_green_list = []
solver.score_list = []
# Copy the saved frequency values instead of calculating them again
for word in word_list_sorted:
solver.word_list.append(word)
for score in score_list_sorted:
solver.score_list.append(score)
i = 0
for i in range(0, 6):
if i == 0:
solver.word_history.append(start_word)
guess = solver.wordle_logic(start_word, wordle)
else:
solver.word_history.append(solver.word_list[0])
guess = solver.wordle_logic(solver.word_list[0], wordle)
solver.process_guess(guess, i)
if ''.join(guess) == "ggggg":
succsess.append(i + 1)
break
if len(solver.word_list) == 1:
succsess.append(i + 1)
break
if i == 5:
fails += 1
if solver.word_list[0] in solver.word_history:
solver.word_list.pop(0)
count = 0
for i in succsess:
count += i
return count, succsess, fails
# Using each word in the word list as a Wordle, try to solve it and see what the average number of guesses is
def test():
start_word = "least"
successes = []
fails = 0
target_list = []
solver = WordleSolve("5_letter_word_list.txt", start_word)
for word in solver.word_list:
target_list.append(word)
count, successes, fails = play_bot(solver, target_list, start_word, True)
print("Number of wordles: %s" % len(target_list))
print("Number of succsess: %s" % len(successes))
print("Average guesses for successes: %s" % (count/len(successes)))
print("Fails: %s" % fails)
# Using each word in the word list as a Wordle, try to solve it and see what the average number of guesses is
def test_start_words():
averages = []
target_list = []
start_word_list =[]
start_word = "least"
solver = WordleSolve("5_letter_word_list.txt", start_word)
for word in solver.word_list:
start_word_list.append(word)
for word in solver.word_list:
target_list.append(word)
for start_word in start_word_list:
successes = []
fails = 0
count, successes, fails = play_bot(solver, target_list, start_word, False)
averages.append((count/len(successes), start_word))
print((count/len(successes), start_word), len(successes))
result = sorted(averages)
print("The best start word is %s with a average of %f Wordle guesses." % (result[0][1], result[0][0]))
# Solve a Wordle with a user
def play():
start_word = "least"
solver = WordleSolve("5_letter_word_list.txt", start_word)
solver.stats()
print("Begin with:")
scores = solver.sort_by_freq()
print(solver.box([start_word]))
solver.word_history.append(start_word)
i = 0
for i in range(0, 6):
user_input = solver.get_input()
solver.process_guess(user_input[:5], i)
solver.stats()
print("Top words:")
try:
for x in range(0, 4):
print("""%s (Freq score: %.2f)""" % (solver.word_list[x], round(solver.score_list[x], 2)))
except Exception as e:
pass
if len(solver.word_list) == 1:
if ''.join(user_input[:5]) == "ggggg":
print(solver.box(solver.word_history))
else:
print(solver.box(solver.word_history + [solver.word_list[0]]))
print("Done! Hooray!!")
exit()
if solver.word_list[0] in solver.word_history:
solver.word_list.pop(0)
print(solver.box(solver.word_history + [solver.word_list[0]]))
solver.word_history.append(solver.word_list[0])
print("Oof. Good luck picking final word!")
# Main
if __name__ == "__main__":
startTime = time()
if len(argv) == 1:
print("Try again.")
elif argv[1] == "test":
test()
elif argv[1] == "play":
play()
elif argv[1] == "test_start_words":
test_start_words()
else:
print("Try again.")
executionTime = (time() - startTime)
print('Time: ' + str(executionTime))