wordle_decoder 0.1.2 → 0.1.5
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/.rubocop.yml +1 -0
- data/CHANGELOG.md +12 -2
- data/Gemfile.lock +1 -1
- data/README.md +22 -5
- data/Rakefile +2 -2
- data/bin/wordle_decoder +4 -1
- data/lib/wordle_decoder/guess.rb +6 -17
- data/lib/wordle_decoder/version.rb +1 -1
- data/lib/wordle_decoder/word.rb +1 -1
- data/lib/wordle_decoder/word_position.rb +18 -2
- data/lib/wordle_decoder/word_search.rb +1 -1
- data/lib/wordle_decoder/wordle_share.rb +25 -6
- data/lib/wordle_decoder.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61618c17d27aec3ae0fc68ea34e5f316d050d72e556e5ab7c972871649a3e4d2
|
4
|
+
data.tar.gz: 6545c2ad4fc4113a210b2b8e5809827327ef0830bfafaaf9f9aa141c0706df4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f03be54869ded6a7a8364e9ecc30c23f755ce093b30f8b92f6075774ea160962fe40fd190ecc8061e71752ed41d26b5f91fe5729809ba4920650500b1d0ab9e6
|
7
|
+
data.tar.gz: 440b20a14eba2632a58ab7f79a78075f2798420f695540e269fbdcb9590f00ae7a2d21e6ac5c5c3e26ad0ea3b365c60639eb637827100b64e72ffe9b3abfd808
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
## [0.1.
|
3
|
+
## [0.1.5] - 2022-03-20
|
4
4
|
|
5
|
-
-
|
5
|
+
- Support high contrast mode - https://github.com/mattruzicka/wordle_decoder/issues/4
|
6
|
+
- Fix edge case where words were being falsey considered impossible when containing more than one of the same letter which is in the answer - https://github.com/mattruzicka/wordle_decoder/issues/3
|
7
|
+
- Found some old notes/TODOs I took while on a walk and forgot about [4201916](https://github.com/mattruzicka/wordle_decoder/commit/4201916b47f84ba7ee851db938b8dba36eb64236)
|
8
|
+
|
9
|
+
## [0.1.4] - 2022-03-06
|
10
|
+
|
11
|
+
- Deal with weird input chars - https://github.com/mattruzicka/wordle_decoder/issues/2
|
12
|
+
|
13
|
+
## [0.1.3] - 2022-03-05
|
14
|
+
|
15
|
+
- Support shortcode emojis - https://github.com/mattruzicka/wordle_decoder/pull/1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,19 +1,36 @@
|
|
1
1
|
# Wordle Decoder
|
2
2
|
|
3
|
-
It guesses your guesses based on all those little
|
3
|
+
It guesses your guesses based on all those little square emojis that we now love to share.
|
4
4
|
|
5
|
-
Given your Wordle share
|
5
|
+
Given your Wordle share, the decoder will spit back the five-letter words it thinks you played with some degree of confidence.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
+
Have Ruby 2.7 or later installed, then run the following at the command line:
|
10
|
+
|
9
11
|
$ gem install wordle_decoder
|
10
12
|
|
11
13
|
## Usage
|
12
14
|
|
13
|
-
After
|
15
|
+
After installing the gem, run `wordle_decoder` at the command line like so:
|
14
16
|
|
15
17
|
$ wordle_decoder
|
16
18
|
|
17
|
-
|
19
|
+
You'll be prompted to share your wordle. Go ahead and paste in your wordle share text. The share text should look something like this:
|
20
|
+
|
21
|
+
```
|
22
|
+
Wordle 258 3/6
|
23
|
+
|
24
|
+
⬛⬛🟨⬛🟨
|
25
|
+
⬛🟩🟩🟩⬛
|
26
|
+
🟩🟩🟩🟩🟩
|
27
|
+
```
|
28
|
+
You'll then be prompted to confirm the word of the day, or if it couldn't figure it out, you'll be asked for it. After that, it'll _try_ to guess your guesses.
|
29
|
+
|
30
|
+
## Why?
|
31
|
+
|
32
|
+
I wasn't sure if something like this was possible and thought it'd be a fun project to build. It does a pretty good job, at least some of the time... I built an excuse to play Wordle.
|
33
|
+
|
34
|
+
Enjoy,
|
18
35
|
|
19
|
-
|
36
|
+
[@mattruzicka](https://twitter.com/mattruzicka)
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ task default: %i[test rubocop]
|
|
20
20
|
task :create_word_search_files do
|
21
21
|
sorted_words = load_words_sorted_by_frequency
|
22
22
|
most_common_words = sorted_words.pop(3_000)
|
23
|
-
most_common_words, less_common_words = most_common_words.partition { |r| r.
|
23
|
+
most_common_words, less_common_words = most_common_words.partition { |r| r.chars.uniq.count == 5 }
|
24
24
|
File.write(lib_path("most_common_words.txt"), most_common_words.join("\n"))
|
25
25
|
File.write(lib_path("less_common_words.txt"), less_common_words.join("\n"))
|
26
26
|
File.write(lib_path("least_common_words.txt"), sorted_words.join("\n"))
|
@@ -29,7 +29,7 @@ end
|
|
29
29
|
task :output_most_common_letters do
|
30
30
|
words = load_wordle_words("allowed_answers.txt")
|
31
31
|
words.concat load_wordle_words("allowed_guesses.txt")
|
32
|
-
letter_count_tally = words.flat_map { _1.
|
32
|
+
letter_count_tally = words.flat_map { _1.chars }.tally
|
33
33
|
letter_count_tally = letter_count_tally.sort_by { -_2 }
|
34
34
|
puts letter_count_tally.first(10).map { _1.first }.join(" ")
|
35
35
|
end
|
data/bin/wordle_decoder
CHANGED
@@ -134,9 +134,12 @@ salutations = ["Maybe some of those are your words!",
|
|
134
134
|
"What lovely words you have!",
|
135
135
|
"I hope my words were as good as yours!",
|
136
136
|
"Please enjoy the rest of your day!",
|
137
|
+
"I hope that what I'm lacking in precision is made up in personality.",
|
138
|
+
"I see your love of the game.",
|
139
|
+
"Wanna exchange five letter words?",
|
137
140
|
"I may not be positive about your words, but I am about you!",
|
138
141
|
"Thank you for spending time with me!",
|
139
142
|
"I enjoyed our time together!"].freeze
|
140
143
|
|
141
144
|
puts CLI::UI.fmt("\n\n #{salutations.sample}\n " \
|
142
|
-
|
145
|
+
"{{blue:https://github.com/mattruzicka/wordle_decoder}}}}\n\n")
|
data/lib/wordle_decoder/guess.rb
CHANGED
@@ -9,19 +9,19 @@ class WordleDecoder
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def score
|
12
|
-
@score ||=
|
12
|
+
@score ||= best_words_with_scores.sum(&:last)
|
13
13
|
end
|
14
14
|
|
15
15
|
def word_scores
|
16
|
-
@word_scores ||=
|
16
|
+
@word_scores ||= best_words_with_scores.map(&:last)
|
17
17
|
end
|
18
18
|
|
19
19
|
def words
|
20
|
-
@words ||=
|
20
|
+
@words ||= best_words_with_scores.map { |w, _s| w.to_s }
|
21
21
|
end
|
22
22
|
|
23
|
-
def
|
24
|
-
@
|
23
|
+
def best_words_with_scores
|
24
|
+
@best_words_with_scores ||= select_best_words_with_scores
|
25
25
|
end
|
26
26
|
|
27
27
|
def inspect
|
@@ -30,18 +30,7 @@ class WordleDecoder
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
|
34
|
-
# Greatly penalize words that have multiple of the same black letters
|
35
|
-
# Greatly penalize words that have the same black letters as any seen word
|
36
|
-
# Greatly penalize words that have the same yellow letter/index pair as any seen word
|
37
|
-
# Reward words that have yellow letters that match yellow letters in seen words, but in different positions
|
38
|
-
# Reward words that have yellow letters that match green letters in seen words
|
39
|
-
# Penalize words that have yellow letters that don't appear in seen words
|
40
|
-
# Penalize words that have green letters that don't appear in seen words
|
41
|
-
# Rewards words based on commonality
|
42
|
-
# Reward/penalize words based on line indexes and common letters
|
43
|
-
#
|
44
|
-
def select_words_with_scores
|
33
|
+
def select_best_words_with_scores
|
45
34
|
selected_words = [@start_word]
|
46
35
|
selected_word_scores = [@start_word.score]
|
47
36
|
seen_black_chars = @start_word.black_chars
|
data/lib/wordle_decoder/word.rb
CHANGED
@@ -5,7 +5,9 @@ class WordleDecoder
|
|
5
5
|
EMOJI_HINT_CHARS = { "⬛" => "b",
|
6
6
|
"⬜" => "b",
|
7
7
|
"🟨" => "y",
|
8
|
-
"
|
8
|
+
"🟦" => "y",
|
9
|
+
"🟩" => "g",
|
10
|
+
"🟧" => "g" }.freeze
|
9
11
|
|
10
12
|
def initialize(hint_line, line_index, answer_chars)
|
11
13
|
@hint_chars = normalize_hint_chars(hint_line)
|
@@ -143,7 +145,21 @@ class WordleDecoder
|
|
143
145
|
end
|
144
146
|
|
145
147
|
def impossible_words(commonality)
|
146
|
-
WordSearch.chars_at_index(@answer_chars, @index, commonality)
|
148
|
+
words = WordSearch.chars_at_index(@answer_chars, @index, commonality)
|
149
|
+
reject_possible_words_where_letter_appears_multiple_times_and_is_in_answer!(words)
|
150
|
+
words
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def reject_possible_words_where_letter_appears_multiple_times_and_is_in_answer!(words)
|
156
|
+
words.reject! do |word|
|
157
|
+
chars = word.chars
|
158
|
+
index_char = chars[@index]
|
159
|
+
chars.each_with_index.any? do |char, idx|
|
160
|
+
char == index_char && idx != @index && @answer_chars.include?(char)
|
161
|
+
end
|
162
|
+
end
|
147
163
|
end
|
148
164
|
end
|
149
165
|
end
|
@@ -2,10 +2,28 @@
|
|
2
2
|
|
3
3
|
class WordleDecoder
|
4
4
|
class WordleShare
|
5
|
-
ANSWER_LINES = ["🟩🟩🟩🟩🟩",
|
5
|
+
ANSWER_LINES = ["🟩🟩🟩🟩🟩",
|
6
|
+
"🟧🟧🟧🟧🟧"].freeze
|
6
7
|
|
7
|
-
def self.final_line?(
|
8
|
-
|
8
|
+
def self.final_line?(input_line)
|
9
|
+
line = normalize_wordle_line(input_line.strip)
|
10
|
+
ANSWER_LINES.include?(line)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.normalize_wordle_line(line)
|
14
|
+
line = translate_emoji_shortcodes(line)
|
15
|
+
line.each_grapheme_cluster.map { |cluster| cluster.codepoints.first }.pack("U*")
|
16
|
+
end
|
17
|
+
|
18
|
+
SHORTCODES_TO_EMOJIS = { ":black_large_square:" => "⬛",
|
19
|
+
":white_large_square:" => "⬜",
|
20
|
+
":large_green_square:" => "🟩",
|
21
|
+
":large_yellow_square:" => "🟨",
|
22
|
+
":large_orange_square:" => "🟧",
|
23
|
+
":large_blue_square:" => "🟦" }.freeze
|
24
|
+
|
25
|
+
def self.translate_emoji_shortcodes(line)
|
26
|
+
SHORTCODES_TO_EMOJIS.reduce(line) { |acc, (key, val)| acc.gsub(key, val) }
|
9
27
|
end
|
10
28
|
|
11
29
|
NEEDS_HELP_INPUTS = %w[help what wordle wat ? man okay yes no test].freeze
|
@@ -60,7 +78,7 @@ class WordleDecoder
|
|
60
78
|
end
|
61
79
|
|
62
80
|
def answer_chars
|
63
|
-
@answer_chars ||= answer&.strip&.
|
81
|
+
@answer_chars ||= answer&.strip&.chars
|
64
82
|
end
|
65
83
|
|
66
84
|
def hint_lines
|
@@ -99,8 +117,9 @@ class WordleDecoder
|
|
99
117
|
VALID_HINT_CHARS = WordPosition::EMOJI_HINT_CHARS.to_a.flatten.uniq
|
100
118
|
|
101
119
|
def parse_wordle_lines!
|
102
|
-
input_lines.
|
103
|
-
line
|
120
|
+
input_lines.filter_map do |line|
|
121
|
+
line = self.class.normalize_wordle_line(line)
|
122
|
+
line if line.each_char.all? { |c| VALID_HINT_CHARS.include?(c) }
|
104
123
|
end
|
105
124
|
end
|
106
125
|
|
data/lib/wordle_decoder.rb
CHANGED
@@ -36,7 +36,7 @@ class WordleDecoder
|
|
36
36
|
|
37
37
|
def to_terminal
|
38
38
|
str = +"\n"
|
39
|
-
best_guess.
|
39
|
+
best_guess.best_words_with_scores.reverse_each do |word, guess_score|
|
40
40
|
str << " #{word.to_terminal} #{word.confidence_score(guess_score)}\n"
|
41
41
|
end
|
42
42
|
str << " {{green:#{@wordle_share.answer}}}\n"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wordle_decoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Ruzicka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cli-ui
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.5'
|
27
|
-
description: Given your Wordle share text, with some degree of confidence,
|
27
|
+
description: Given your Wordle share text, with some degree of confidence,the decoder
|
28
28
|
will spit back the five-letter words it thinks you played.
|
29
29
|
email:
|
30
30
|
executables:
|
@@ -59,6 +59,7 @@ licenses:
|
|
59
59
|
metadata:
|
60
60
|
homepage_uri: https://github.com/mattruzicka/wordle_decoder
|
61
61
|
source_code_uri: https://github.com/mattruzicka/wordle_decoder
|
62
|
+
rubygems_mfa_required: 'true'
|
62
63
|
post_install_message:
|
63
64
|
rdoc_options: []
|
64
65
|
require_paths:
|