Как найти лучшую комбинацию слов Wordle First с Python
При игре Wordle поиск правильных букв обычно определяется первыми словами. Чем эффективнее первое слово, тем больше подсказок мы можем получить, чтобы правильно написать букву. Обычно у каждого свои предпочтения.
Для тех из вас, кто не знает, Wordle - это ежедневная игра в слова, созданная Джошем Уордлом, в которой каждый день вам предстоит разгадывать новую словесную головоломку. Для получения дополнительной информации вы можете получить https://www.nytimes.com/games/wordle/index.html.
В этом посте я буду искать решение с использованием Python для получения наилучшего первого слова. Здесь буду использовать основные статистические методы, чтобы всем было легко их понять.
Я разделю эту статью на 4 части, а именно:
- Поиск по первому слову
- В поисках второго слова
- Найдите сочетание первого и второго слов
- Ищите наиболее оптимальную комбинацию.
Поиск по первому слову
Я использовал dataset из книги Дональда Э. Кнута "Искусство компьютерного программирования" (TAOCP). Dataset содержит слова на английском языке, состоящие из 5 букв (в соответствии с ограничением букв в мире), в общей сложности 5757 слов.
ЦИТАТА: Приведенный выше набор данных взят у проф. Дональда Э. Кнута, почетного профессора искусства компьютерного программирования в Стэнфордском университете.
Первое, что я сделал, это импортировал зависимости и загрузил файл набора данных.
import pandas as pd
import numpy as np
import math
words = []
with open('sgb-words.txt') as f:
words = [line.rstrip() for line in f]
Набор данных содержит следующие данные.
which
there
their
about
would
...
pupal
Первая обработка выполняется путем удаления одних и тех же букв в каждом слове. Это необходимо для того, чтобы мы могли получить слово из 5 разных букв. Метод заключается в следующем:
distinct_words = []
for word in words:
distinct_words.append(list(set(word)))
Результат будет таким.
[['w', 'h', 'i', 'c'],
['t', 'h', 'e', 'r'],
['t', 'h', 'e', 'i', 'r'],
['a', 'b', 'o', 'u', 't'],
['w', 'o', 'u', 'l', 'd'],
...
['p', 'u', 'a', 'l']]
После этого мы можем получить вес каждой буквы. Метод довольно прост, а именно путем сложения каждой буквы, и результаты представлены в виде словаря. Вес будет определять, как часто появляется буква, чем чаще появляется буква, тем больше вес буквы.
letter_counter = {}
for word in distinct_words:
for letter in word:
if letter in letter_counter:
letter_counter[letter] += 1
else:
letter_counter[letter] = 0
Результат мы увидим ниже:
{'h': 790,
'w': 500,
'i': 1538,
'c': 919,
'e': 2657,
't': 1461,
'r': 1798,
'u': 1067,
'a': 2180,
'o': 1682,
'b': 668,
'l': 1433,
'd': 1099,
's': 2673,
'f': 501,
'g': 650,
'k': 573,
'n': 1218,
'y': 867,
'p': 894,
'v': 308,
'm': 793,
'q': 52,
'j': 87,
'x': 137,
'z': 120}
Если мы отсортируем результат будет таким:
>>> {key: val for key, val in sorted(letter_counter.items(), key = lambda x: x[1], reverse = True)}
{'s': 2673,
'e': 2657,
'a': 2180,
'r': 1798,
'o': 1682,
'i': 1538,
't': 1461,
'l': 1433,
'n': 1218,
'd': 1099,
'u': 1067,
'c': 919,
'p': 894,
'y': 867,
'm': 793,
'h': 790,
'b': 668,
'g': 650,
'k': 573,
'f': 501,
'w': 500,
'v': 308,
'x': 137,
'z': 120,
'j': 87,
'q': 52}
Из этих результатов видно, что 5 букв, которые появляются чаще всего это буквы s
, e
, a
, r
и o
. Кроме того, это результат выраженный в процентах.
>>> values = letter_counter.values()
>>> total = sum(values)
>>> percent = [value * 100. / total for value in values]
>>> for i, letter in enumerate(letter_counter.keys()):
... print("{}: {}".format(letter, percent[i]))
h: 2.962685167822989
w: 1.8751171948246765
i: 5.767860491280705
c: 3.4464654040877556
e: 9.964372773298331
t: 5.479092443277705
r: 6.742921432589537
u: 4.00150009375586
a: 8.17551096943559
o: 6.307894243390212
b: 2.505156572285768
l: 5.374085880367523
d: 4.121507594224639
s: 10.024376523532721
f: 1.878867429214326
g: 2.4376523532720795
k: 2.148884305269079
n: 4.567785486592912
y: 3.2514532158259892
p: 3.3527095443465216
v: 1.1550721920120008
m: 2.973935870991937
q: 0.19501218826176636
j: 0.3262703918994937
x: 0.5137821113819614
Далее нам нужно найти только слово, в котором буква имеет наибольший вес. Метод заключается в следующем:
word_values = []
for word in distinct_words:
temp_value = 0
for letter in word:
temp_value += letter_counter[letter]
word_values.append(temp_value)
words[np.argmax(word_values)]
И результат arose
. Если посмотреть на приведенные выше данные, можно увидеть, что в слове arose
есть буквы, которые имеют большой вес. Таким образом, на основе статистических результатов можно сделать вывод, что слово arose - лучшее слово для использования в первом слове Wordle.
Но достаточно ли первого слова?
Иногда нам нужно еще одно слово, чтобы получить достаточно подсказок. Поэтому мы поищем другое слово.
В поисках второго слова
После того, как мы получили первое слово, следующим шагом будет получение списка слов, не содержащих букв в первом слове. Например, первое слово, которое мы получаем - arose
. Таким образом, список слов в наборе данных не может содержать буквы a
, r
, o
, s
и e
. Если есть слово, содержащее эти буквы, то оно будет удалено из списка. Метод заключается в следующем.
result_word = []
first_word_list = list(set(best_word))for word in words:
in_word = False
i = 0
while i < len(first_word_list) and not in_word:
if first_word_list[i] in word:
in_word = True
i += 1
if not in_word:
result_word.append(word)
И результат таков.
['which',
'think',
'might',
'until',
...
'biffy']
Количество слов сократилось до 310 слов с прежних 5757 слов. Осталось всего около 5% слов.
Следующим шагом будет повторение процесса, аналогичному поиску по первому слову. Полный код выглядит следующим образом:
import pandas as pd
import numpy as np
import math
def best_words(words):
distinct_words = []
for word in words:
distinct_words.append(list(set(word)))
letter_counter = {}
for word in distinct_words:
for letter in word:
if letter in letter_counter:
letter_counter[letter] += 1
else:
letter_counter[letter] = 0
word_values = []
for word in distinct_words:
temp_value = 0
for letter in word:
temp_value += letter_counter[letter]
word_values.append(temp_value)
return word_values
def get_best_word(words, word_values):
return words[np.argmax(word_values)]
def remove_word_contain_letters(words, first_word):
result_word = []
first_word_list = list(set(first_word))
for word in words:
in_word = False
i = 0
while i < len(first_word_list) and not in_word:
if first_word_list[i] in word:
in_word = True
i += 1
if not in_word:
result_word.append(word)
return result_word
words = []
with open('sgb-words.txt') as f:
words = [line.rstrip() for line in f]
word_values = best_words(words)
first_word = get_best_word(words, word_values)
second_words = remove_word_contain_letters(words, first_word)
second_values = best_words(second_words)
second_word = get_best_word(second_words, second_values)
print(first_word) # first word
print(second_word) # second word
И результаты первого и второго слова - arose
и unity
.
Из приведенного выше метода можно сделать вывод, что слова arose
и unity
лучшие слова для начала игры в Wordle. Однако, если мы посмотрим на статистику по количеству букв в предыдущем посте, то можно увидеть, что буквы u
и y
не входят в топ-10 наиболее часто используемых букв. Это указывает на то, что слова arose
и unity
могут быть не самыми оптимальными словами.
Найдите сочетание первого и второго слов
В этом разделе мы обсудим, как можно получить два слова, буквы которых - это все буквы, которые встречаются чаще всего.
Нам нужно только повторить процесс, который мы уже проделывали раньше. Если в предыдущем процессе мы использовали только первое слово, с лучшим значением, то теперь мы также используем второе лучшее слово в качестве первого слова, чтобы получить больше вариаций в результатах.
Шаги следующие
Первый - вычислить значение для всех слов, а затем отсортировать слова по значению.
values = best_words(words)
values_index = np.argsort(values)[::-1]
После этого мы будем искать первое и второе слова, как и раньше. Разница в том, что мы продолжим цикл, чтобы найти первый и второй цикл комбинации слов, чтобы получить слова, которые имеют наилучшие значения.
best_val = 0
best_word_list = []
top_words = sorted(values, reverse=True)
for i, idx in enumerate(values_index):
best_word = words[idx]
second_words = remove_word_contain_letters(words, best_word)
second_values = best_words(second_words)
second_best_word = get_best_word(second_words, second_values)
temp_value = 0
for letter in second_best_word:
temp_value += letter_counter[letter]
if temp_value + top_words[i] >= best_val:
best_val = temp_value + top_words[i]
print(best_word, second_best_word, top_words[i] + temp_value)
И результат таков.
arose unity 17141
tears doily 17388
stare doily 17388
tares doily 17388
rates doily 17388
aster doily 17388
tales irony 17507
taels irony 17507
stale irony 17507
least irony 17507
tesla irony 17507
steal irony 17507
slate irony 17507
teals irony 17507
stela irony 17507
store inlay 17507
lores antic 17559
...
laird stone 17739
adorn tiles 17739
radon tiles 17739
tonal rides 17739
talon rides 17739
lined roast 17739
intro leads 17739
nitro leads 17739
nodal tries 17739
Из этих результатов первый столбец - первое слово, второй столбец - второе, а третий столбец - это сумма значений первого и второго слова.
Если вы посмотрите на результаты выше, слова arose
и unity
не являются сочетанием слов с наибольшей ценностью. Кроме того, есть много словосочетаний которые получают значение 17739. Если вы обратите внимание на все буквы в словосочетании, которое получают это значение, именно 10 букв встречаются чаще всего в наборе данных. Таким образом, можно сделать вывод, что комбинация слов, получившее значение 17739, является наивысшей комбинацией слов, которую мы можем получить.
Но какое сочетание слов является наиболее оптимальным?
Чтобы получить ответ на этот вопрос нам нужно знать вес букв в зависимости от их размещения.
Ищите наиболее оптимальную комбинацию
Теперь мы будем искать более оптимальное словосочетание, которое будет первым и вторым словом в игре Wordle. Далее нам нужно рассчитать веса букв для каждой позиции. Метод заключается в следующем.
letter_list =['r', 'o', 'a', 's', 't', 'l', 'i', 'n', 'e', 's']
letter_value = {}
for letter in letter_list:
letter_counter = {}
for i in range(len(letter_list)//2):
loc_counter = 0
for j in range(len(words)):
if words[j][i] == letter:
loc_counter += 1
letter_counter[str(i)] = loc_counter
letter_value[letter] = letter_counter
Переменная letter_list
состоит из букв, которые встречаются чаще всего. После этого мы посчитаем, сколько вхождений этих букв в начале слова и так далее из всех слов в наборе данных.
Содержимое letter_value
следующее
{'r': {'0': 268, '1': 456, '2': 475, '3': 310, '4': 401},
'o': {'0': 108, '1': 911, '2': 484, '3': 262, '4': 150},
'a': {'0': 296, '1': 930, '2': 605, '3': 339, '4': 178},
's': {'0': 724, '1': 40, '2': 248, '3': 257, '4': 1764},
't': {'0': 376, '1': 122, '2': 280, '3': 447, '4': 360},
'l': {'0': 271, '1': 360, '2': 388, '3': 365, '4': 202},
'i': {'0': 74, '1': 673, '2': 516, '3': 284, '4': 45},
'n': {'0': 118, '1': 168, '2': 410, '3': 386, '4': 203},
'e': {'0': 129, '1': 660, '2': 397, '3': 1228, '4': 595}}
Эти результаты объясняют, что, например, буква r
появляется как первая буква 268 раз, как вторая буква 456 раз и так далее... Таким образом, мы можем получить значение каждой позиции.
Далее мы рассчитаем вес словосочетания, которое мы получили ранее, используя letter_value
. Метод заключается в следующем:
result_list = []
for i in range(len(best_word_list)):
word_value = 0
for word in best_word_list[i]:
for j, letter in enumerate(word):
if letter in letter_value:
word_value += letter_value[letter][str(j)]
result_list.append(word_value)
И вот результат.
for i in range(len(result_list)):
print(best_word_list[i], result_list[i])
=== result ===
['arose', 'unity'] 3219
['tears', 'doily'] 5507
['stare', 'doily'] 4148
['tares', 'doily'] 6565
...
['lined', 'roast'] 4983
['intro', 'leads'] 4282
['nitro', 'leads'] 4831
['nodal', 'tries'] 5910
Чтобы получить более оптимальную комбинацию значений, мы можем ввести следующий синтаксис:
result_index = np.argsort(result_list)[::-1]
best_word_list[result_index[0]]
И лучшее словосочетание это toned
и rails
.
Наконец, это полный код первого поиска слов в Wordle с использованием серии Python.
import pandas as pd
import numpy as np
import math
def best_words(words):
distinct_words = []
for word in words:
distinct_words.append(list(set(word)))
letter_counter = {}
for word in distinct_words:
for letter in word:
if letter in letter_counter:
letter_counter[letter] += 1
else:
letter_counter[letter] = 0
word_values = []
for word in distinct_words:
temp_value = 0
for letter in word:
temp_value += letter_counter[letter]
word_values.append(temp_value)
return word_values
def get_best_word(words, word_values):
return words[np.argmax(word_values)]
def remove_word_contain_letters(words, first_word):
result_word = []
first_word_list = list(set(first_word))
for word in words:
in_word = False
i = 0
while i < len(first_word_list) and not in_word:
if first_word_list[i] in word:
in_word = True
i += 1
if not in_word:
result_word.append(word)
return result_word
words = []
with open('sgb-words.txt') as f:
words = [line.rstrip() for line in f]
distinct_words = []
for word in words:
distinct_words.append(list(set(word)))
letter_counter = {}
for word in distinct_words:
for letter in word:
if letter in letter_counter:
letter_counter[letter] += 1
else:
letter_counter[letter] = 0
word_values = best_words(words)
first_word = get_best_word(words, word_values)
second_words = remove_word_contain_letters(words, first_word)
second_values = best_words(second_words)
second_word = get_best_word(second_words, second_values)
values = best_words(words)
values_index = np.argsort(values)[::-1]
best_val = 0
best_word_list = []
top_words = sorted(values, reverse=True)
for i, idx in enumerate(values_index):
best_word = words[idx]
second_words = remove_word_contain_letters(words, best_word)
second_values = best_words(second_words)
second_best_word = get_best_word(second_words, second_values)
temp_value = 0
for letter in second_best_word:
temp_value += letter_counter[letter]
if temp_value + top_words[i] >= best_val:
best_val = temp_value + top_words[i]
best_word_list.append([best_word, second_best_word])
letter_list =['r', 'o', 'a', 's', 't', 'l', 'i', 'n', 'e', 's']
letter_value = {}
for letter in letter_list:
letter_counter = {}
for i in range(len(letter_list)//2):
loc_counter = 0
for j in range(len(words)):
if words[j][i] == letter:
loc_counter += 1
letter_counter[str(i)] = loc_counter
letter_value[letter] = letter_counter
result_list = []
for i in range(len(best_word_list)):
word_value = 0
for word in best_word_list[i]:
for j, letter in enumerate(word):
if letter in letter_value:
word_value += letter_value[letter][str(j)]
result_list.append(word_value)
result_index = np.argsort(result_list)[::-1]
print(best_word_list[result_index[0]])
Можно сделать вывод, что слова toned
и rails
являются лучшими сочетаниями слов для начала игры Wordle. В дополнение к тому факту, что буквы в словесной комбинации являются буквами, которые чаще всего встречаются в наборе данных, буквы также размещаются в позиции, которая имеет наибольшее значение.
Ответ может быть не полностью оптимальным, поскольку он опирается только на статистические данные без учета других соображений.