-
Notifications
You must be signed in to change notification settings - Fork 0
/
wordle4stats.py
259 lines (202 loc) · 8.56 KB
/
wordle4stats.py
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
"""
Computer plays wordle (targeted strategic guessing).
:author: Terry Sergeant
:version: 4
In this version the computer picks its first word: "lares" based on
pre-processing. This will typically leave less than 300 possible words
remaining. Words having the form: _a_es are especially troublesome and
tend to result in lots of guesses when guesses are selected randomly.
So, the pre-computed word "compt" was found to be an optimal second
guess for these difficult words.
For other situations if the number of remaining words is more than 100 then we
make a random selection. Otherwise we perform an optimal calculation.
"""
import random
import re
from colorama import Fore, Back, Style
def is_possible(word, result):
"""
Determine whether a word is possible based on guesses made so far
and the partial word provided by the user.
:param word: The word we are considering as a possible match.
:param result: A dictionary that stores the guess and the grade.
:returns: True if the word is a possible match; False otherwise.
"""
guesslist = list(result['guess'])
gradelist = list(result['grade'])
wordlist = list(word)
# if perfect match letters don't line up then its not a match
for i in range(len(word)):
if gradelist[i] == '2':
if guesslist[i] != word[i]:
return False
else:
# remove letters that are a perfect match
wordlist[i] = '!'
guesslist[i] = '@'
for i in range(len(word)):
if gradelist[i] == '1':
# right letter, wrong position -> if its there mark it out, if not
# return False. If it is in the same spot return False.
if guesslist[i] == wordlist[i]:
return False
if not (guesslist[i] in wordlist):
return False
for j in range(len(word)):
if guesslist[i] == wordlist[j]:
guesslist[i] = '@'
wordlist[j] = '!'
break
elif gradelist[i] != '*':
# we guessed wrong letter completely so it shouldn't be left in the word
if guesslist[i] in wordlist:
return False
return word != guess # true unless the word matches our guessed word
def reduce_word_list(wordlist, result):
"""
Narrow down list of possible choices.
:param wordlist: List of words that are still considered possibilities.
:param result: A dictionary that stores the guess and the grade.
:returns: Modified and shortened word_list.
"""
newwordlist = [w for w in wordlist if is_possible(w, result)]
#print("My mega-intelligence has reduced the options to " + str(len(newwordlist)) + " words.")
return newwordlist
def load_dictionary(filename):
"""
Read the dictionary from the file and return the words as a list and
as a hash of boolean values.
:param filename: The file containing the official dictionary.
:returns: wordlist, wordhash
NOTE: The wordhash isn't really necessary in this version of the code, but will
be useful when dealing with a user who might guess a word not in the dictionary.
"""
wordlist = []
wordhash = {}
with open(filename) as f:
for word in f:
wordlist.append(word.rstrip())
wordhash[word.rstrip()] = True
#print("Dictionary contains "+str(len(wordlist))+" words.")
return wordlist, wordhash
def get_random_guess(wordlist):
"""
Randomly select a word from the list of words.
:param wordlist: The list of words currently being considered.
:returns: A randomly selected word from the list.
"""
return wordlist[random.randint(0,len(wordlist)-1)]
def grade_word(answer, guess, wordhash, possible_letters):
"""
Grades the guess by comparing it to the correct answer.
:param answer: The answer word the computer is trying to guess.
:param guess: The guessed word to be graded.
:param wordhash: A hash of the original dictionary words in order to verify
that the guess is valid.
:returns: False if guess is not in the dictionary; otherwise returns a dictionary
containing the original guess and the grade string.
NOTE: The grade string contains 5 characters as follows:
* there is a 2 if the correct letter is used in the correct place
* there is a 1 if the correct letter is used in the wrong place
* the original guessed letter is used if the letter does not occur in the answer
"""
if not (guess in wordhash):
print("The word '" + guess + "' is not in the dictionary file")
return false
answerlist= list(answer)
guesslist= list(guess)
for i in range(len(answer)):
if answerlist[i] == guesslist[i]:
guesslist[i] = '2'
answerlist[i] = 0
for i in range(len(answer)):
for j in range(len(answer)):
if answerlist[i] == guesslist[j]:
guesslist[j] = '1'
answerlist[i] = 0
for i in range(len(guess)):
if guesslist[j] > '2':
possible_letters[ord(guesslist[j])-97] = False
return {
"guess": guess,
"grade": "".join(guesslist),
"possible" : possible_letters
}
def show_grade(result):
"""
Displays a graded guess using traditional color coding.
:param result: A dictionary that stores the guess and the grade.
Colors are:
* text is white
* green background if the correct letter is used in the correct place
* yellow background if the correct letter is used in the wrong place
* black background if the letter does not occur in the answer
"""
print(Fore.WHITE)
for i in range(len(result['guess'])):
if result['grade'][i] == '2':
print(Back.GREEN + result['guess'][i], end='')
elif result['grade'][i] == '1':
print(Back.YELLOW+ result['guess'][i], end='')
else:
print(Back.BLACK + result['guess'][i], end='')
print(Style.RESET_ALL)
def get_best_guess(allwords, possiblewords, wordhash, orig_possible_letters, is_hard_word):
"""
Select a word from allwords that will on average reduce the number of
possiblewords the most. We don't allow repeated letters to improve a score.
:param allwords: The complete list of dictionary words.
:param possiblewords: The words that have not yet been eliminated based
one previous guesses.
:param wordhash: A hash of possible words.
:orig_possible_letters: A list of letters that are possible based on previous guesses.
:is_hard_word: This value is true if the guess count is 1 and the grade was: .2.22
:returns: The "best" next guess.
"""
if len(possiblewords) < 3:
return possiblewords[0]
if is_hard_word:
return "compt"
if len(possiblewords) > 100:
return get_random_guess(possiblewords)
bestword = "false"
bestscore = 20000000
for guess in allwords:
total = 0
for answer in possiblewords:
possible_letters = orig_possible_letters.copy()
result = grade_word(answer, guess, wordhash, possible_letters)
lst = reduce_word_list(wordlist, result)
total += len(lst)
if total < bestscore:
bestword = guess
bestscore = total
#print(bestword + " is best word to guess with score of " +
# str(bestscore / len(possiblewords)))
return bestword
#-------------------------------------------------------------------------------------
random.seed()
origlist, wordhash = load_dictionary("dictionary.txt")
f = open("wordle4commentary.txt", "w")
# We use the precalculated "optimal" guess lares as first guess
for answer in origlist:
possible_letters = [True] * 26
f.write(answer + ": ")
wordlist, wordhash = load_dictionary("dictionary.txt")
possible_letters = [True] * 26
guess = "lares"
result = grade_word(answer, guess, wordhash, possible_letters)
f.write(guess + ",")
possible_letters = result["possible"]
count = 1
while result['grade'] != '22222':
wordlist = reduce_word_list(wordlist, result)
is_hard_word = count == 1 and ((result["grade"][1]=='2' and result["grade"][4]=='2') or (result["grade"][3] == '2' and result["grade"][4] == '2'))
guess = get_best_guess(origlist, wordlist, wordhash, possible_letters, is_hard_word)
f.write(guess + ",")
result = grade_word(answer, guess, wordhash, possible_letters)
possible_letters = result["possible"]
count += 1
f.write("\n")
print(answer + " " + str(count))
f.close()