Skip to content

Commit affdbaa

Browse files
committed
Solver done + Code improvements and documentation
1 parent 86ad084 commit affdbaa

File tree

3 files changed

+2354
-372463
lines changed

3 files changed

+2354
-372463
lines changed

wordle_solver/wordle_solver.py

Lines changed: 39 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# https://github.com/dwyl/english-words for the list of words
1+
# https://www.wordunscrambler.net/word-list/wordle-word-list for the list of words
22

33
from selenium import webdriver
44
from selenium.webdriver.common.by import By
@@ -8,20 +8,28 @@
88
import time
99

1010

11+
# This class is used to store data about the wordle such as :
12+
# - the list of possible words
13+
# - the letters that are present but not in the right position
14+
# - the letters that are absent
15+
# - the letters that are correct and their position in a list
16+
1117
class Finder:
1218
def __init__(self):
1319
self.possible_words = get_list_of_words()
14-
self.absent_letters = set([])
1520
self.present_letters = set([])
21+
self.absent_letters = set([])
1622
self.word = [''] * 5
1723

1824

25+
# Function that is called by the KeyboardListener
1926
def on_release(key):
2027
# Start button
2128
if key == keyboard.Key.esc:
2229
return False # stop listener
2330

2431

32+
# Get the status of the letters in the wordle
2533
def get_row_results(game_row):
2634
tiles = game_row.find_elements(By.CLASS_NAME, "Tile-module_tile__3ayIZ")
2735
row_results = []
@@ -34,97 +42,73 @@ def get_row_results(game_row):
3442
}
3543
for tile in tiles:
3644
row_results.append(res_to_int[tile.get_attribute("data-state")])
37-
print(row_results)
45+
print(f"Row results : {row_results}")
3846

3947
return tuple(row_results)
4048

4149

50+
# Enter the word in the wordle
4251
def enter_word(word):
4352
keyboard_controller = keyboard.Controller()
4453
keyboard_controller.type(word)
4554
keyboard_controller.tap(keyboard.Key.enter)
4655
time.sleep(2)
4756

4857

49-
def create_regex(solving_string, present_letters):
50-
regex = ""
51-
for letter in range(len(solving_string)):
52-
if solving_string[letter] == "*":
53-
regex += "[a-z]"
54-
elif solving_string[letter] == "_":
55-
regex += \
56-
"[^" + str(list(present_letters)).replace("'", "").replace(",", "").replace("[", "").replace(" ", "")
57-
else:
58-
regex += "[" + solving_string[letter] + "]"
59-
return regex
60-
61-
62-
# Check word length
58+
# Check word length, used in get_list_of_words() if the source list contains words with different length
6359
def check_word_length(word):
6460
if len(word) != 5:
6561
return False
6662
else:
6763
return True
6864

6965

66+
# Check if a word contains a specific letter
7067
def check_letter_in_word(letter, word):
7168
if letter in word:
7269
return True
7370
else:
7471
return False
7572

7673

74+
# Check if the letter in the finder object is the same as the letter in the possible answer
7775
def check_match(finder_word_letter, possible_word_letter):
7876
if finder_word_letter == possible_word_letter:
7977
return True
8078
else:
8179
return False
8280

8381

84-
# From the basic list of words, return all the words with 5 characters.
82+
# From the wordle words list, return all the words
8583
def get_list_of_words():
86-
# list_of_words = open("words_alpha.txt", "r").read().strip().splitlines()
87-
list_of_words = open("words_alpha_2.txt", "r").read().strip().splitlines()
88-
list_of_words = list(filter(check_word_length, list_of_words))
84+
list_of_words = open("words_alpha.txt", "r").read().strip().splitlines()
8985

90-
return list_of_words
86+
# *** Use this if the source list contains words with different length ***
87+
# list_of_words = list(filter(check_word_length, list_of_words))
9188

92-
# Print to verify filtered_list only contains 5 characters words.
93-
# print("list of words : ", list_of_words)
89+
return list_of_words
9490

9591

9692
# Algorithm that solve the wordle
97-
# In the string we get :
98-
# _ : the letter is not on the right position (present)
99-
# * : the letter is not in the solution (absent)
100-
# X : the letter is on the right position (correct)
101-
# letters_not_in_response, is a list which keeps track of the allowed letters
102-
# letter_not_in_position, is a list which keeps track of the letters in bad position
103-
# For exemple, "A_A_*A", letters_not_in_response = ['B'], letter_not_in_position = ['K'].
10493
def solving_algorithm(word, res, finder):
105-
print("solving_algorithm start")
106-
107-
solving_string = "*****"
94+
print("Starting solving algorithm")
10895

96+
# Compare the word with the results of the wordle
10997
for letter in range(len(word)):
110-
print("letter : ", word[letter])
11198
if res[letter] == 1: # Case when the status of the letter is "correct"
112-
print("the letter is correct")
113-
solving_string = solving_string[:letter] + word[letter] + solving_string[letter + 1:]
99+
print(f"Letter {word[letter]} is correct")
114100
finder.word[letter] = word[letter]
115101
print(finder.word)
116102
elif res[letter] == 0: # Case when the status of the letter is "present" (present but at the wrong position)
117-
print("the letter is present")
103+
print(f"Letter {word[letter]} is present")
118104
finder.present_letters.add(word[letter])
119-
solving_string = solving_string[:letter] + "_" + solving_string[letter + 1:]
120105
else: # Case when the status of the letter is "absent"
121-
print("the letter is absent")
106+
print(f"Letter {word[letter]} is absent")
122107
if word[letter] not in finder.present_letters:
123108
finder.absent_letters.add(word[letter])
124-
solving_string = solving_string[:letter] + "*" + solving_string[letter + 1:]
125109

126-
print("Update list of words")
127-
print("length of list", len(finder.possible_words))
110+
print("\n")
111+
print("Updating list of possible words ...")
128112

129113
# Update list of words
130114
for absent in finder.absent_letters:
@@ -138,28 +122,27 @@ def solving_algorithm(word, res, finder):
138122
finder.possible_words = list(
139123
filter(lambda x_word: check_match(x_word[i], finder.word[i]), finder.possible_words))
140124

125+
print("List of possible words updated !\n")
126+
141127
print("letter not in the right position : ", finder.present_letters)
142128
print("Letters with absent status", finder.absent_letters)
143129
print("list of words : ", finder.possible_words)
144130
print("length of list", len(finder.possible_words))
145131

146-
regex = create_regex(solving_string, finder.present_letters) # Create the regex
147-
print(create_regex(solving_string, finder.present_letters))
148-
149-
# print("solving string :", solving_string)
150-
151132

152133
def main():
153134
# Start the browser
154135
browser = webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install()))
155136
browser.get("https://www.nytimes.com/games/wordle/index.html")
156137

138+
# Create the finder object (cf. class Finder)
157139
finder = Finder()
140+
158141
guesses_left = 6
159142

160143
# Wait for start
161144
with keyboard.Listener(on_release=on_release, suppress=True) as listener:
162-
print("Starting")
145+
print("Starting program\n")
163146
listener.join()
164147

165148
# With "suppress=True", duplicate key presses are not sent to the application
@@ -169,18 +152,25 @@ def main():
169152
# Get the game rows
170153
game_rows = browser.find_elements(By.CLASS_NAME, 'Row-module_row__dEHfN')
171154

155+
# Creators recommend “Slate” as starting word
172156
first_string = "slate"
157+
158+
# Enter the first word
173159
enter_word(first_string)
174160
res = get_row_results(game_rows[0])
175161
solving_algorithm(first_string, res, finder)
176162
guesses_left -= 1
177163

178164
time.sleep(1)
179165

166+
# Enter the next words
180167
for i in range(guesses_left, 0, -1):
181168
enter_word(finder.possible_words[0])
182169
res = get_row_results(game_rows[guesses_left + 1 - i])
183170
solving_algorithm(finder.possible_words[0], res, finder)
171+
if len(finder.possible_words) == 1:
172+
print(f"The word is : {finder.possible_words[0]}\n")
173+
break
184174
time.sleep(1)
185175

186176

0 commit comments

Comments
 (0)