waylon-wordle 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/data/vocabulary.json +2329 -7207
- data/lib/waylon/skills/wordle.rb +19 -22
- data/lib/waylon/wordle/solver.rb +65 -31
- data/lib/waylon/wordle/version.rb +1 -1
- data/lib/waylon/wordle.rb +7 -1
- metadata +2 -2
data/lib/waylon/skills/wordle.rb
CHANGED
@@ -24,7 +24,7 @@ module Waylon
|
|
24
24
|
|
25
25
|
def todays_wordle
|
26
26
|
solution = solve_todays_wordle
|
27
|
-
case solution.size
|
27
|
+
case solution.attempts.size
|
28
28
|
when 1..2
|
29
29
|
reply("I got *really* lucky today!")
|
30
30
|
when 3
|
@@ -32,44 +32,40 @@ module Waylon
|
|
32
32
|
when 4..5
|
33
33
|
reply("I managed to figure it out.")
|
34
34
|
when 6
|
35
|
-
|
36
|
-
|
37
|
-
|
35
|
+
if solution.last_attempted_word == Waylon::Wordle.for_today
|
36
|
+
reply("I barely solved it!")
|
37
|
+
else
|
38
|
+
reply("Unfortunately, I didn't solve it.")
|
39
|
+
end
|
38
40
|
end
|
39
41
|
|
40
|
-
reply(formatted_wordle_score(solution))
|
42
|
+
reply(formatted_wordle_score(solution.attempts.map { _1[:result] }))
|
41
43
|
end
|
42
44
|
|
43
45
|
def spoil_wordle
|
44
46
|
threaded_reply("Here's what my attempt looked like...")
|
45
47
|
threaded_reply(
|
46
48
|
codify(
|
47
|
-
JSON.pretty_generate(solve_todays_wordle.map { |w| w.
|
49
|
+
JSON.pretty_generate(solve_todays_wordle.attempts.map { |w| w[:word].upcase })
|
48
50
|
)
|
49
51
|
)
|
50
52
|
end
|
51
53
|
|
52
54
|
private
|
53
55
|
|
54
|
-
def formatted_wordle_score(
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
56
|
+
def formatted_wordle_score(scores)
|
57
|
+
translation = {
|
58
|
+
miss: "⬛",
|
59
|
+
hit: "🟩",
|
60
|
+
near_hit: "🟨"
|
61
|
+
}
|
62
|
+
|
63
|
+
solved_in = scores.last.map(&:to_sym) == %i[hit hit hit hit hit] ? scores.size : "X"
|
60
64
|
|
61
65
|
lines = ["Wordle #{Waylon::Wordle.todays_number} #{solved_in}/6"]
|
62
66
|
lines << ""
|
63
|
-
|
64
|
-
lines <<
|
65
|
-
if letter == todays_answer[index]
|
66
|
-
hit
|
67
|
-
elsif todays_answer.include?(letter)
|
68
|
-
near_hit
|
69
|
-
else
|
70
|
-
miss
|
71
|
-
end
|
72
|
-
end.join
|
67
|
+
scores.each do |score|
|
68
|
+
lines << score.map { translation[_1.to_sym] }.join
|
73
69
|
end
|
74
70
|
lines.join("\n")
|
75
71
|
end
|
@@ -78,6 +74,7 @@ module Waylon
|
|
78
74
|
cache("solution_#{DateTime.now.new_offset("-05:00").to_date}", expires: 24 * 60 * 60) do
|
79
75
|
solver = Waylon::Wordle::Solver.new(Waylon::Wordle.random_startword)
|
80
76
|
solver.solve_todays_wordle
|
77
|
+
solver
|
81
78
|
end
|
82
79
|
end
|
83
80
|
end
|
data/lib/waylon/wordle/solver.rb
CHANGED
@@ -4,39 +4,83 @@ module Waylon
|
|
4
4
|
module Wordle
|
5
5
|
# Utility class for solving Wordle puzzles
|
6
6
|
class Solver
|
7
|
-
attr_reader :startword
|
7
|
+
attr_reader :startword, :answer
|
8
|
+
attr_accessor :attempts, :hits, :near_hits, :misses
|
8
9
|
|
9
10
|
def initialize(startword)
|
10
11
|
@startword = startword
|
12
|
+
@attempts = []
|
13
|
+
@hits = []
|
14
|
+
@near_hits = []
|
15
|
+
@misses = []
|
16
|
+
@answer = Waylon::Wordle.for_today.chars
|
11
17
|
end
|
12
18
|
|
13
19
|
def solve_todays_wordle
|
14
|
-
answer
|
15
|
-
|
16
|
-
|
20
|
+
until attempts.any? && (last_attempted_word == answer.join || attempts.size == 6)
|
21
|
+
attempts << make_attempt(answer)
|
22
|
+
end
|
17
23
|
|
18
|
-
|
24
|
+
last_attempted_word == answer.join
|
19
25
|
end
|
20
26
|
|
21
|
-
def make_attempt(answer
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
near_hits[index] ||= []
|
32
|
-
near_hits[index] << letter
|
33
|
-
else
|
34
|
-
misses << letter unless misses.include?(letter)
|
35
|
-
end
|
27
|
+
def make_attempt(answer)
|
28
|
+
word = next_word
|
29
|
+
attempt = { word: word.join, result: [] }
|
30
|
+
word.each_with_index do |letter, index|
|
31
|
+
if letter == answer[index]
|
32
|
+
add_hit(letter, index, attempt)
|
33
|
+
elsif answer.include?(letter)
|
34
|
+
add_near_hit(letter, index, word, attempt)
|
35
|
+
else
|
36
|
+
add_miss(letter, attempt)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
39
|
-
|
40
|
+
attempt
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_potential_solution
|
44
|
+
potential_solutions = Waylon::Wordle.vocabulary.map(&:chars).reject { _1.intersect?(misses) }
|
45
|
+
|
46
|
+
candidates = potential_solutions.select do |word|
|
47
|
+
hits_match?(hits, word) && near_hits_match?(near_hits, word)
|
48
|
+
end
|
49
|
+
|
50
|
+
candidates.shuffle.max_by { _1.uniq.size }
|
51
|
+
end
|
52
|
+
|
53
|
+
def last_attempted_word = attempts.last[:word]
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def add_hit(letter, index, attempt)
|
58
|
+
hits[index] = letter
|
59
|
+
attempt[:result] << :hit
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_near_hit(letter, index, word, attempt)
|
63
|
+
near_hits[index] ||= []
|
64
|
+
near_hits[index] << letter
|
65
|
+
attempt[:result] << (counts_as_near_hit?(word, index) ? :near_hit : :miss)
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_miss(letter, attempt)
|
69
|
+
misses << letter unless misses.include?(letter)
|
70
|
+
attempt[:result] << :miss
|
71
|
+
end
|
72
|
+
|
73
|
+
def next_word = attempts.empty? ? startword.chars : find_potential_solution
|
74
|
+
|
75
|
+
def counts_as_near_hit?(word, index)
|
76
|
+
return false unless answer.include?(word[index])
|
77
|
+
|
78
|
+
answer_hit_count = answer.count(word[index])
|
79
|
+
word_hits = word.each_with_index.with_object([]) do |(letter, idx), indices|
|
80
|
+
indices << idx if letter == word[index] && answer[idx] == word[idx]
|
81
|
+
end
|
82
|
+
|
83
|
+
answer_hit_count > word_hits.size
|
40
84
|
end
|
41
85
|
|
42
86
|
def hits_match?(hits, exploded_word)
|
@@ -59,16 +103,6 @@ module Waylon
|
|
59
103
|
|
60
104
|
true
|
61
105
|
end
|
62
|
-
|
63
|
-
def find_potential_solution(hits, near_hits, misses)
|
64
|
-
potential_solutions = Waylon::Wordle.vocabulary.map(&:chars).reject do |word|
|
65
|
-
word.intersect?(misses)
|
66
|
-
end
|
67
|
-
|
68
|
-
potential_solutions.select do |word|
|
69
|
-
hits_match?(hits, word) && near_hits_match?(near_hits, word)
|
70
|
-
end.sample
|
71
|
-
end
|
72
106
|
end
|
73
107
|
end
|
74
108
|
end
|
data/lib/waylon/wordle.rb
CHANGED
@@ -29,7 +29,13 @@ module Waylon
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.random_startword
|
32
|
-
vocabulary.select
|
32
|
+
vocabulary.select do |word|
|
33
|
+
if rand(10) > 5
|
34
|
+
word.chars.uniq.intersection(%w[a e i o u]).uniq.size >= 4
|
35
|
+
else
|
36
|
+
word.chars.uniq.intersection(%w[a e o s t r n]).uniq.size >= 4
|
37
|
+
end
|
38
|
+
end.sample
|
33
39
|
end
|
34
40
|
|
35
41
|
def self.todays_date
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waylon-wordle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Gnagy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: waylon-core
|