POLSKIE WORDLE Z MATLABEM – CZĘŚĆ 3 – METODA TRZECH PIERWSZYCH WYRAZÓW

Trzecia część wpisu autorstwa  Michała Hałonia dotyczy metody wpisywania tych samych trzech słów przy każdej rozgrywce. MATLAB w tym przypadku zostaje wykorzystany jedynie na etapie znalezienia odpowiedniego zestawu wyrazów.

Poprzednie wpisy dotyczące polskiej wersji gry Wordle opisywały oraz testowały strategię wymagającą dostępu do programu MATLAB w trakcie gry. W tym wpisie zaproponuję metodę wpisywania tych samych trzech słów przy każdej rozgrywce. MATLAB w tym przypadku zostanie wykorzystany jedynie na etapie znalezienia odpowiedniego zestawu wyrazów.

Opisywany pomysł opiera się na następujących założeniach:

  • podczas każdej rozgrywki wpisujemy na początku zawsze te same, wybrane wcześniej trzy słowa,
  • wszystkie litery wchodzące w skład wyrazów powinny być unikalne (występować tylko raz),
  • ponadto, wykorzystane litery w wyrazach powinny być tymi najczęściej występującymi w słowniku wyrazów,
  • po wpisaniu trzech pierwszych słów graczowi pozostają trzy próby na odgadnięcie poszukiwanego hasła,
  • znalezienie docelowego wyrazu w tej metodzie powinno być prostsze z uwagi na informację o obecności w haśle 15 najczęściej występujących liter.

Podsumowując, w tej metodzie poświęcamy połowę dostępnych podejść na rozpoznanie występowania w poszukiwanym słowie 15 najpopularniejszych liter w wyrazach, następnie na podstawie uzyskanych w ten sposób informacji próbujemy odgadnąć hasło w ramach kolejnych trzech podejść.

ODNALEZIENIE ZESTAWU TRZECH WYRAZÓW

W pierwszej części wykorzystamy MATLABa do znalezienia odpowiedniego zestawu trzech wyrazów, który będzie spełniał podane wyżej warunki unikalności liter. W tym celu w pierwszej kolejności wyświetlimy posortowany rozkład częstości występowania liter w słowniku wyrazów.

% load the list of unique five letter words
% file 'slowa.txt' to be downloaded from https://sjp.pl/sl/growy/ website
word5 = load_words('slowa.txt');
% split our words into their individual letters
letters = split(word5,"");
% this also creates leading and trailing blank strings, drop them
letters = letters(:,2:end-1);
% view the counts of letter use
figure;
h = histogram(categorical(letters(:)), 'DisplayOrder', 'descend', 'EdgeColor', 'none');
title("Częstość występowania danych liter w analizowanych słowach")
ylabel("Liczba wystąpień litery")
% create table with letters and their scores
lt = table(h.Categories',h.Values','VariableNames',["letters","score"]);

Poszukujemy trzech wyrazów zawierających 15 unikatowych i najczęściej występujących liter, zatem na prezentowanym histogramie interesuje nas 15 liter od lewej strony wykresu (na lewo od czerwonej pionowej linii). Z nich w następnych krokach spróbujemy złożyć trzy wyrazy.

Zanim przystąpimy do poszukiwań odpowiedniego zestawu słów, sprawdźmy jaki procent spośród liter we wszystkich wyrazach stanowi 15 najpopularniejszych, wybranych przeze mnie.

top15percentage = sum(lt(1:15,:).score)/sum(lt.score)*100;
fprintf("15 najczęściej występujących liter stanowi ~%0.1f%% wszystkich liter w wyrazach.\n",top15percentage)

Okazuje się, że wybrane 15 liter (spośród wszystkich 34) stanowi 72.4% liter obecnych w wyrazach analizowanego słownika. W tym miejscu można zadać pytanie: czy na sprawdzenie obecności najpopularniejszych liter w haśle nie będzie lepiej przeznaczyć dwa lub cztery słowa zamiast trzech?

Można łatwo sprawdzić (wykorzystując powyższe linie kodu), że 10 liter stanowi 54.9%, a 20 najpopularniejszych około 85.9% wszystkich liter w wyrazach. Pierwszy wynik (statystycznie około połowy liter podświetlanych na żółto bądź zielono w trakcie rozgrywki) zdaje się być zbyt niski by mógł być pomocny, natomiast wykorzystanie strategii czterech pierwszych wyrazów (zamiast trzech) jest warte rozważenia. W tych rozważaniach skupimy się jednak na metodzie trzech słów.

Kontynuując poszukiwania zestawu wyrazów z unikatowymi literami przygotuję tabelę zawierającą słowa z niepowtarzającymi się literami.

% create words table from array
words = array2table(word5,'VariableNames',"words");
% find how many unique letters are in each word
words.num_letters = arrayfun(@(x) numel(unique(char(x))),words.words);
% keep only the words with no repeated letters
words = words(words.num_letters==5,:);
head(words)

Uzyskaliśmy tabelę zawierającą ponad 21 tysięcy słów. Następnie pozostawimy w niej jedynie te wyrazy, które w całości składają się z najczęściej występujących liter.

% create an array containing top 15 letters
top_15_array = table2array(lt(1:15,1));
% find the number of unique top 15 letters in each word
words.top_letters = arrayfun(@(x) sum(contains(split(x,""),top_15_array)),words.words);
% keep only the words with 5 letters from top list
words = words(words.top_letters==5,:);
head(words)

Przetworzoną w ten sposób tabelę, zawierającą prawie 3 700 słów, możemy teraz wykorzystać do odnalezienia trójek wyrazów, które będzie można wpisywać jako rozpoczęcie rozgrywki w polskiej wersji gry Wordle. W tym celu skorzystam z poniższego kodu, który dla każdej pary, a następnie trójki kandydatów sprawdza liczbę unikalnych liter, która powinna być równa długości analizowanych wyrazów. Zestawów spełniających powyższe wymagania są tysiące, natomiast na potrzeby wpisu wyszukamy pierwsze kilkadziesiąt.

% create empty table for searching results
search_results = cell2table(cell(0,3),'VariableNames', {'word1','word2','word3'});
% get number of words analyzed
words_len = height(words);

% iterate over all words available
for i_1 = 1:words_len
% finish searching if found more than 10 sets of three opening words
if height(search_results) > 10
break
end
for i_2 = (i_1+1):words_len
% concatenate two analyzed words
two_words = strcat(words.words(i_1),words.words(i_2));
% check if all 10 letters are unique
if strlength(join(unique(split(two_words,"")),"")) == 10
for i_3 = (i_2+1):words_len
% concatenate two words with third one
three_words = strcat(two_words,words.words(i_3));
% check if all 15 letters are unique
if strlength(join(unique(split(three_words,"")),"")) == 15
% add new set of opening three words to results
search_results = [search_results; {words.words(i_1),words.words(i_2),words.words(i_3)}];
end
end
end
end
end
head(search_results)
save('literalnie_3_words.mat','search_results');

Jak można zauważyć, udało nam się odnaleźć takie zestawy trójek wyrazów, które zawierają 15 najczęściej występujących liter w słowniku słów pięcioliterowych. Wykorzystajmy je teraz w prawdziwej rozgrywce.

TESTOWANIE OPISYWANEJ METODY

Do testów moglibyśmy wykorzystać pierwszą znalezioną trójkę, czyli wyrazy „AKIOM”, „LUSTR” oraz „PEWNY”, jednak przy planowanym regularnym wykorzystaniu mogłyby pojawić się problemy z zapamiętaniem rzadko wykorzystywanych pierwszych dwóch słów. Przeszukałem zatem tabelę search_results w celu odnalezienia bardziej popularnych zestawów i najbardziej spodobała mi się trójka „ARMIE”, „KLONY” oraz „WPUST”. Dwa pierwsze słowa mogą kojarzyć się z filmami, których akcja rozgrywa się „dawno temu w odległej galaktyce”, natomiast wyraz „WPUST” to element połączenia wpustowego (termin znany m.in. Studentom kierunków mechanicznych).

Przejdźmy zatem do przykładowych wyników rozgrywek zakończonych z wykorzystaniem proponowanego zestawu wyrazów.

W pierwszym przypadku (po lewej) już po wpisaniu dwóch wyrazów uzyskaliśmy informację o dokładnym położeniu trzech oraz obecności czwartej litery, natomiast w drugim dowiedzieliśmy się o obecności dwóch liter w docelowym wyrazie. Nie tylko litery podświetlone kolorami stanowią dla nas cenną informację w trakcie rozgrywki – te oznaczone na szaro wskazują które litery nie wchodzą w skład poszukiwanego wyrazu, co znacznie ułatwia jego odnalezienie. Opisywaną metodę trzech pierwszych wyrazów przetestowałem także długoterminowo, przez ponad trzy miesiące –  wyniki umieściłem poniżej.

W ponad 40% przypadków udało mi się odgadnąć hasło już w pierwszej próbie po wpisaniu wybranego zestawu trzech wyrazów (za czwartym razem), a w trzech przypadkach udało mi się to nawet za drugim razem (od razu po wpisaniu wyrazu „ARMIE”). Ponadto, wszystkie rozgrywki zakończyły się sukcesem. Można zatem uznać opisywaną strategię za wiarygodną i jednocześnie prostą w codziennym użyciu (do jej wykorzystania wystarczy zapamiętać trzy słowa).

TESTY NA ORYGINALNEJ GRZE WORDLE

Opisywaną strategię można wykorzystać także w oryginalnej grze Wordle – zachęcam do samodzielnego przygotowania odpowiedniego kodu na podstawie oryginalnego wpisu na blogu Loren on the Art of MATLAB oraz powyższych rozważań. W angielskiej wersji 15 najczęściej występujących liter stanowi 81.2% wszystkich liter w wyrazach (o 8.8 punktów procentowych więcej w porównaniu do polskiej wersji), a 20 liter aż 95.8%. Przykładowe zestawy znalezionych trzech wyrazów umieszczono poniżej.

Wykorzystując pierwszą odnalezioną trójkę, tzn. wyrazy „ACHED”, „LIONS” oraz „TRUMP” uzyskałem następujące wyniki w Wordle:


W przypadku angielskiej wersji gry również udało się odgadnąć hasło - odpowiednio w czwartej oraz trzeciej próbie.

PODSUMOWANIE

Podsumowując, w ramach opisywanego pomysłu na wykorzystanie stałych trzech wyrazów w każdej rozgrywce udało się za pomocą MATLABa odnaleźć tysiące takich zestawów, spośród których wybrałem trójkę słów stosunkowo łatwych do zapamiętania („ARMIE”, „KLONY” oraz „WPUST”). Metoda ta okazała się być pomocna w praktycznym wykorzystaniu, co potwierdza próba 95 rozgrywek zakończonych sukcesem. Ponadto, strategia może być wykorzystana także w przypadku angielskiej, oryginalnej wersji gry (np. wyrazy „ACHED”, „LIONS” oraz „TRUMP”).

Zachęcam do wypróbowania opisywanej metody w praktyce oraz podzielenia się swoimi doświadczeniami dotyczącymi uzyskanych wyników.

Poniżej znajduje się kod całego programu:

% Partially based on the code from the blog post:
% https://blogs.mathworks.com/loren/2022/01/18/building-a-wordle-solver/

%% LOAD DATA
% load the list of unique five letter words
% file 'slowa.txt' to be downloaded from https://sjp.pl/sl/growy/ website
word5 = load_words('slowa.txt');
% display number of words in arrays
fprintf("Lista zwiera %i słów pięcioliterowych.\n",height(word5))

%% LETTERS FREQUENCY IN FIVE LETTER WORDS
% split our words into their individual letters
letters = split(word5,"");
% this also creates leading and trailing blank strings, drop them
letters = letters(:,2:end-1);
% view the counts of letter use
figure;
h = histogram(categorical(letters(:)), 'DisplayOrder', 'descend', 'EdgeColor', 'none');
title("Częstość występowania danych liter w analizowanych słowach")
ylabel("Liczba wystąpień litery")
% create table with letters and their scores
lt = table(h.Categories',h.Values','VariableNames',["letters","score"]);

%% CHECK PRECENTAGE OF TOP 15 LETTERS IN ALL WORDS
top15percentage = sum(lt(1:15,:).score)/sum(lt.score)*100;
fprintf("15 najczęściej występujących liter stanowi ~%0.1f%% wszystkich liter w wyrazach.\n",top15percentage)

%% CREATE TABLE WITH WORDS AND UNIQUE LETTERS NUMBERS
% create words table from array
words = array2table(word5,'VariableNames',"words");
% find how many unique letters are in each word
words.num_letters = arrayfun(@(x) numel(unique(char(x))),words.words);
% keep only the words with no repeated letters
words = words(words.num_letters==5,:);
head(words)

%% ADD NUMBER OF TOP15 LETTERS IN WORDS COLUMN
% create an array containing top 15 letters
top_15_array = table2array(lt(1:15,1));
% find the number of unique top 15 letters in each word
words.top_letters = arrayfun(@(x) sum(contains(split(x,""),top_15_array)),words.words);
% keep only the words with 5 letters from top list
words = words(words.top_letters==5,:);
head(words)

%% SEARCHING FOR OPENING THREE WORDS SETS

% create empty table for searching results
search_results = cell2table(cell(0,3),'VariableNames', {'word1','word2','word3'});
% get number of words analyzed
words_len = height(words);

% iterate over all words available
for i_1 = 1:words_len
% finish searching if found more than 10 sets of three opening words
if height(search_results) > 10
break
end
for i_2 = (i_1+1):words_len
% concatenate two analyzed words
two_words = strcat(words.words(i_1),words.words(i_2));
% check if all 10 letters are unique
if strlength(join(unique(split(two_words,"")),"")) == 10
for i_3 = (i_2+1):words_len
% concatenate two words with third one
three_words = strcat(two_words,words.words(i_3));
% check if all 15 letters are unique
if strlength(join(unique(split(three_words,"")),"")) == 15
% add new set of opening three words to results
search_results = [search_results; {words.words(i_1),words.words(i_2),words.words(i_3)}];
end
end
end
end
end
head(search_results)
save('literalnie_3_words.mat','search_results');
%% FUNCTIONS
function word5 = load_words(txt_filename)
% read the list of words into a string array
r = readlines(txt_filename);
% Wordle uses all upper case letters
r = upper(r);
% get the list of unique five letter words
word5 = unique(r(strlength(r)==5));
end %load_words

(Visited 589 times, 1 visits today)

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *