-
Notifications
You must be signed in to change notification settings - Fork 2
/
wordle_regex.pi
153 lines (122 loc) · 4.85 KB
/
wordle_regex.pi
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
/*
Wordle solver using regex/2 in Picat.
This is a Wordle solver using regex/2.
It's based on wordle2.pi but correct_pos/2 and not_in_word/2 has
been replaced by calls to regex/2, and correct_char/2 has
been replaced with a loop based approach.
This model was created by Hakan Kjellerstrand, hakank@gmail.com
See also my Picat page: http://www.hakank.org/picat/
*/
import regex.
main => go.
go ?=>
wordle("...n.",["","","","",""],"slat"),
% -> [crone,brine,crony,briny,prone,corny,borne,prune,drone,phone,brink,phony,bound,pound,grind,frond,found,bring,drink,being,whine,fiend,chunk,whiny,prong,mound,horny,urine,round,drunk,irony,doing,wound,hound,opine,wring,rhino,downy,wrong,wrung,ebony,ovine,dying,young,owing,eying,vying,eking,penne,penny,bunny,funny,going,ninny,ozone,icing]
wordle(".r.n.",["","","","",""],"slatcoe"),
% -> [briny,brink,grind,bring,drink,drunk,wring,wrung]
wordle("...st",["s","","","",""],"flancre"),
% -> [moist,hoist,ghost,midst,joist,joust,boost,twist]
wordle(".r.n.",["","","","",""],"slatcoe"),
% -> [briny,brink,grind,bring,drink,drunk,wring,wrung]
wordle(".r.n.",["","","","",""],"slatcoebiy"),
% -> [drunk,wrung]
wordle(".run.",["","","","",""],"slatcoebiydk"),
% -> [wrung]
wordle(".l...",["","","a","","t"],"sn"),
% -> [alter,ultra,altar]
wordle(".r.n.",["","b","","",""],"slatcoe"),
% -> [briny,brink,bring]
wordle("..is.",["s","","","",""],"lantpoe"),
% [brisk,frisk,whisk]
% wordle(".....",["","","","",""],""),
% -> All words...
nl.
go => true.
%
% wordle(Words,CorrectPos,CorrectChar,NotInWord)
% - Words: the wordlist/candidates
% - CorrectPos: correct character in correct position,
% Example: n is in position 4 and t is in position 5: "...nt":
% - CorrectChar: correct character but in wrong position.
% Example: a is not in position 1, l is not in position 2: ["a","l","","",""]
% - NotInWord: characters not in word.
% Example: s, a, and t are not in the word: "sat"
%
wordle(CorrectPos, CorrectChar, NotInWord) =>
N = 5,
% File = "unixdict.txt", % 3161 5 letter words. Is this the smallest word list?
File = "wordle_small.txt", % 2314 words
% File = "wordle_large.txt", % 12971 words
% File = "eng_dict.txt", % 9336 5 letter words
% File = "words_lower.txt", % 21830 English 5 letter words
Words = [W : W in read_file_lines(File), length(W) == N],
NumWords = Words.len,
println(numWordsInDict=NumWords),
wordle(Words, CorrectPos, CorrectChar, NotInWord,[],Candidates),
println(candidates=Candidates),
println(len=Candidates.len),
(Candidates != [] -> println(suggestion=Candidates.first()) ; true),
nl.
%
% The main engine:
% Loop through all words and check if they are in the scope.
%
wordle([], _CorrectPos, _CorrectChar, _NotInWord,AllWords,Sorted) :-
sort_candidates(AllWords,Sorted).
wordle([Word|Words], CorrectPos, CorrectChar, NotInWord, AllWords0,AllWords) :-
( (
regex(CorrectPos,Word), % Characters in correct position (or .)
correct_char(Word,CorrectChar), % Correct character, incorrect position
if NotInWord.len > 0 then regex("^[^" ++ NotInWord ++ "]+$",Word) end % Characters not in word
)
->
AllWords1 = AllWords0 ++ [Word],
wordle(Words, CorrectPos, CorrectChar, NotInWord, AllWords1,AllWords)
;
wordle(Words, CorrectPos, CorrectChar, NotInWord, AllWords0,AllWords)
).
%
% Correct char.
% Ensure that the character is in the word, but not
% in the given position.
%
correct_char(Word,CorrectChars) :-
foreach({I,Cs} in zip(1..Word.len,CorrectChars))
foreach(C in Cs)
membchk(C,Word), % Character is in the word
Word[I] != C % but is not in this position
end
end.
%
% Sort the candidates.
%
sort_candidates(Candidates,Sorted) =>
% The probability order for each position in the word.
% Reversed and with missing chars.
% See wordle.pi (go2/0) for the method to get this.
Alphas = ["xzyjkquinovhewlrmdgfaptbcs",
"jzqfkgxvbsdymcwptnhulieroa",
"qjhzkxfwyvcbpmgdstlnrueoia",
"jqxyzbwhfvpkmdguotcrilasne",
"jqvuzxbiwfcsgmpoakdnhlrtye"],
score_words(Candidates,Alphas,[],Scores),
Sorted = [W : [_S=W] in Scores.sort_down].
%
% Score all words
%
score_words([],_Alpha,Scores,Scores).
score_words([Word|Words],Alphas,Scores0,[[Score=Word]|Scores]) :-
% We prefer words with distinct characters
Score1 = cond(Word.len==Word.remove_dups.len,100,0),
score_word(Word,Alphas,Score1,Score),
score_words(Words,Alphas,Scores0,Scores).
% Score each character in a word
score_word([],_Alpha,Score,Score).
score_word([C|Cs],[Alpha|Alphas],Score0,Score) :-
nth(N,Alpha,C), % find the position of this character
S = N / 2,
Score1 = Score0 + S,
score_word(Cs,Alphas,Score1,Score).
% Print an empty board
empty() =>
println("wordle(\".....\",[\"\",\"\",\"\",\"\",\"\"],\"\")").