ruby-wordle 0.9.2 → 1.0.0
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/CHANGELOG.md +19 -0
- data/README.md +66 -36
- data/bin/ruby-wordle +3 -21
- data/lib/ruby-wordle/cli.rb +426 -0
- data/lib/ruby-wordle/dictionary.rb +74 -0
- data/lib/ruby-wordle/feedback.rb +73 -0
- data/lib/ruby-wordle/game.rb +84 -0
- data/lib/ruby-wordle/solver.rb +92 -0
- data/lib/ruby-wordle/style.rb +56 -0
- data/lib/ruby-wordle/version.rb +1 -1
- data/lib/ruby-wordle.rb +7 -0
- data/word-lists/5-letters/word-list-basic-uniq-letters.txt +1 -8
- data/word-lists/5-letters/word-list-basic.txt +1 -9
- data/word-lists/5-letters/word-list-uniq-letters.txt +1 -0
- data/word-lists/5-letters/word-list.txt +1 -0
- metadata +25 -29
- data/.gitignore +0 -58
- data/Gemfile +0 -3
- data/Rakefile +0 -1
- data/img/ruby-wordle-play.png +0 -0
- data/img/ruby-wordle-solver.png +0 -0
- data/lib/ruby-wordle/script-filter.rb +0 -48
- data/lib/ruby-wordle/script-play.rb +0 -124
- data/lib/ruby-wordle/script-solver.rb +0 -179
- data/lib/ruby-wordle/script-utils.rb +0 -23
- data/ruby-wordle.gemspec +0 -21
- data/word-lists/word-list-basic.txt +0 -12937
- data/word-lists/word-list-original.txt +0 -370103
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
|
|
3
|
+
module RubyWordle
|
|
4
|
+
# Word lists for one word length.
|
|
5
|
+
#
|
|
6
|
+
# +answers+ are common words a game picks from; +guesses+ are every word
|
|
7
|
+
# accepted as a guess. Every answer is also a valid guess.
|
|
8
|
+
class Dictionary
|
|
9
|
+
DATA_DIR = File.expand_path("../../word-lists", __dir__)
|
|
10
|
+
WORD_LENGTH = 5
|
|
11
|
+
|
|
12
|
+
attr_reader :answers, :guesses, :length
|
|
13
|
+
|
|
14
|
+
def self.load(dir = File.join(DATA_DIR, "#{WORD_LENGTH}-letters"), length: WORD_LENGTH)
|
|
15
|
+
read = ->(name) { File.readlines(File.join(dir, name), chomp: true) }
|
|
16
|
+
new(answers: read.("word-list-basic.txt"), guesses: read.("word-list.txt"), length: length)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.default
|
|
20
|
+
@default ||= load
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(answers:, guesses:, length: WORD_LENGTH)
|
|
24
|
+
@length = length
|
|
25
|
+
invalid = (answers + guesses).grep_v(/\A[a-z]{#{length}}\z/)
|
|
26
|
+
raise ArgumentError, "not #{length} letters a-z: #{invalid.first(3).join(', ')}" unless invalid.empty?
|
|
27
|
+
raise ArgumentError, "no answers" if answers.empty?
|
|
28
|
+
|
|
29
|
+
@answers = answers.uniq.sort.freeze
|
|
30
|
+
@guesses = (guesses | answers).sort.freeze
|
|
31
|
+
@answer_set = @answers.to_set
|
|
32
|
+
@guess_set = @guesses.to_set
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def valid_guess?(word)
|
|
36
|
+
@guess_set.include?(word)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def common?(word)
|
|
40
|
+
@answer_set.include?(word)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Keeps alphabetic words of +length+ letters, lowercased. Capitalized
|
|
44
|
+
# entries in a source list (proper nouns) are dropped when
|
|
45
|
+
# +lowercase_only+ is set.
|
|
46
|
+
def self.normalize(lines, length:, lowercase_only: false)
|
|
47
|
+
lines.map(&:strip)
|
|
48
|
+
.reject { |w| lowercase_only && w.match?(/\A[A-Z][a-z]/) }
|
|
49
|
+
.map(&:downcase)
|
|
50
|
+
.select { |w| w.match?(/\A[a-z]{#{length}}\z/) }
|
|
51
|
+
.uniq
|
|
52
|
+
.sort
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Writes the n-letter lists derived from the full and basic source lists.
|
|
56
|
+
def self.generate(length:, source_dir: DATA_DIR, out_dir: File.join(source_dir, "#{length}-letters"))
|
|
57
|
+
read = ->(name) { File.readlines(File.join(source_dir, name), chomp: true) }
|
|
58
|
+
basic = normalize(read.("word-list-basic.txt"), length: length, lowercase_only: true)
|
|
59
|
+
all = (normalize(read.("word-list-original.txt"), length: length) | basic).sort
|
|
60
|
+
uniq = ->(words) { words.select { |w| w.chars.uniq.size == length } }
|
|
61
|
+
|
|
62
|
+
Dir.mkdir(out_dir) unless Dir.exist?(out_dir)
|
|
63
|
+
{
|
|
64
|
+
"word-list.txt" => all,
|
|
65
|
+
"word-list-uniq-letters.txt" => uniq.(all),
|
|
66
|
+
"word-list-basic.txt" => basic,
|
|
67
|
+
"word-list-basic-uniq-letters.txt" => uniq.(basic)
|
|
68
|
+
}.each do |name, words|
|
|
69
|
+
File.write(File.join(out_dir, name), words.map { |w| "#{w}\n" }.join)
|
|
70
|
+
end
|
|
71
|
+
out_dir
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module RubyWordle
|
|
2
|
+
# Wordle feedback for one guess against one answer.
|
|
3
|
+
#
|
|
4
|
+
# A pattern is an Integer in base 3, one digit per position (position 0 is
|
|
5
|
+
# the least significant digit): 0 = absent, 1 = present elsewhere,
|
|
6
|
+
# 2 = correct. Integers keep the solver's partitioning cheap.
|
|
7
|
+
module Feedback
|
|
8
|
+
ABSENT = 0
|
|
9
|
+
PRESENT = 1
|
|
10
|
+
CORRECT = 2
|
|
11
|
+
|
|
12
|
+
SYMBOLS = { "x" => ABSENT, "b" => ABSENT, "0" => ABSENT, "-" => ABSENT, "." => ABSENT,
|
|
13
|
+
"y" => PRESENT, "1" => PRESENT,
|
|
14
|
+
"g" => CORRECT, "2" => CORRECT }.freeze
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def score(guess, answer)
|
|
19
|
+
size = guess.bytesize
|
|
20
|
+
marks = Array.new(size, ABSENT)
|
|
21
|
+
remaining = Hash.new(0)
|
|
22
|
+
|
|
23
|
+
size.times do |i|
|
|
24
|
+
if guess.getbyte(i) == answer.getbyte(i)
|
|
25
|
+
marks[i] = CORRECT
|
|
26
|
+
else
|
|
27
|
+
remaining[answer.getbyte(i)] += 1
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
size.times do |i|
|
|
32
|
+
next if marks[i] == CORRECT
|
|
33
|
+
|
|
34
|
+
byte = guess.getbyte(i)
|
|
35
|
+
if remaining[byte] > 0
|
|
36
|
+
marks[i] = PRESENT
|
|
37
|
+
remaining[byte] -= 1
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
encode(marks)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def encode(marks)
|
|
45
|
+
marks.reverse_each.reduce(0) { |acc, mark| acc * 3 + mark }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def decode(pattern, size)
|
|
49
|
+
Array.new(size) do
|
|
50
|
+
mark = pattern % 3
|
|
51
|
+
pattern /= 3
|
|
52
|
+
mark
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def solved(size)
|
|
57
|
+
encode(Array.new(size, CORRECT))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Parses user notation such as "xygxx", "01200" or "b.y-g".
|
|
61
|
+
# Returns nil when the text is not a valid pattern of the given size.
|
|
62
|
+
def parse(text, size)
|
|
63
|
+
chars = text.downcase.chars
|
|
64
|
+
return nil unless chars.size == size && chars.all? { |c| SYMBOLS.key?(c) }
|
|
65
|
+
|
|
66
|
+
encode(chars.map { |c| SYMBOLS[c] })
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def notation(pattern, size)
|
|
70
|
+
decode(pattern, size).map { |mark| "xyg"[mark] }.join
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "ruby-wordle/feedback"
|
|
2
|
+
|
|
3
|
+
module RubyWordle
|
|
4
|
+
# One round of Wordle with a hidden answer.
|
|
5
|
+
class Game
|
|
6
|
+
class InvalidGuess < StandardError; end
|
|
7
|
+
|
|
8
|
+
MAX_ATTEMPTS = 6
|
|
9
|
+
|
|
10
|
+
attr_reader :answer, :history, :max_attempts, :dictionary, :revealed
|
|
11
|
+
|
|
12
|
+
def initialize(dictionary, answer: nil, random: Random.new, max_attempts: MAX_ATTEMPTS)
|
|
13
|
+
@dictionary = dictionary
|
|
14
|
+
@answer = answer || dictionary.answers.sample(random: random)
|
|
15
|
+
raise ArgumentError, "#{@answer} is not in the word list" unless dictionary.valid_guess?(@answer)
|
|
16
|
+
|
|
17
|
+
@max_attempts = max_attempts
|
|
18
|
+
@history = []
|
|
19
|
+
@assisted = false
|
|
20
|
+
@revealed = nil
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Marks that the player used a hint.
|
|
24
|
+
def assist!
|
|
25
|
+
@assisted = true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def assisted?
|
|
29
|
+
@assisted
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Reveals the letter at the first position never guessed correctly, once
|
|
33
|
+
# per game, as [position, letter]. Returns nil when fewer than two
|
|
34
|
+
# positions have never been guessed correctly.
|
|
35
|
+
def reveal
|
|
36
|
+
return @revealed if @revealed
|
|
37
|
+
|
|
38
|
+
found = @history.flat_map do |_, pattern|
|
|
39
|
+
marks = Feedback.decode(pattern, length)
|
|
40
|
+
marks.each_index.select { |i| marks[i] == Feedback::CORRECT }
|
|
41
|
+
end
|
|
42
|
+
unknown = (0...length).to_a - found
|
|
43
|
+
return nil if unknown.size < 2
|
|
44
|
+
|
|
45
|
+
assist!
|
|
46
|
+
@revealed = [unknown.first, @answer[unknown.first]]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def length
|
|
50
|
+
@answer.length
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def guess(word)
|
|
54
|
+
raise InvalidGuess, "the game is over" if over?
|
|
55
|
+
raise InvalidGuess, "type #{length} letters (a-z)" unless word.match?(/\A[a-z]{#{length}}\z/)
|
|
56
|
+
raise InvalidGuess, "\"#{word}\" is not in the word list" unless @dictionary.valid_guess?(word)
|
|
57
|
+
|
|
58
|
+
pattern = Feedback.score(word, @answer)
|
|
59
|
+
@history << [word, pattern]
|
|
60
|
+
pattern
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def won?
|
|
64
|
+
@history.any? { |_, pattern| pattern == Feedback.solved(length) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def over?
|
|
68
|
+
won? || @history.size >= @max_attempts
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def attempts_left
|
|
72
|
+
@max_attempts - @history.size
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The best feedback seen for each guessed letter, for the keyboard.
|
|
76
|
+
def letter_states
|
|
77
|
+
@history.each_with_object({}) do |(word, pattern), states|
|
|
78
|
+
word.chars.zip(Feedback.decode(pattern, length)) do |char, mark|
|
|
79
|
+
states[char] = mark if states.fetch(char, -1) < mark
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require "ruby-wordle/feedback"
|
|
2
|
+
|
|
3
|
+
module RubyWordle
|
|
4
|
+
# Narrows down the words consistent with a history of guesses and
|
|
5
|
+
# feedback, and ranks the next guess.
|
|
6
|
+
#
|
|
7
|
+
# A word stays a candidate only if scoring it as the answer reproduces
|
|
8
|
+
# every observed pattern, so repeated letters are handled exactly as the
|
|
9
|
+
# game scores them.
|
|
10
|
+
class Solver
|
|
11
|
+
Suggestion = Struct.new(:word, :expected, :possible)
|
|
12
|
+
|
|
13
|
+
SAMPLE_SIZE = 400
|
|
14
|
+
PROBE_SIZE = 300
|
|
15
|
+
|
|
16
|
+
attr_reader :dictionary, :history, :candidates
|
|
17
|
+
|
|
18
|
+
def initialize(dictionary, pool: dictionary.guesses)
|
|
19
|
+
@dictionary = dictionary
|
|
20
|
+
@pool = pool
|
|
21
|
+
reset
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reset
|
|
25
|
+
@history = []
|
|
26
|
+
@candidates = @pool
|
|
27
|
+
self
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add(guess, pattern)
|
|
31
|
+
guess = guess.dup.freeze
|
|
32
|
+
@history << [guess, pattern]
|
|
33
|
+
@candidates = @candidates.select { |word| Feedback.score(guess, word) == pattern }
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def undo
|
|
38
|
+
previous = @history[0...-1]
|
|
39
|
+
reset
|
|
40
|
+
previous.each { |guess, pattern| add(guess, pattern) }
|
|
41
|
+
self
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Candidates that are common words; the answer is most likely among them.
|
|
45
|
+
def likely
|
|
46
|
+
common = @candidates.select { |w| @dictionary.common?(w) }
|
|
47
|
+
common.empty? ? @candidates : common
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Ranks guesses by the expected number of likely candidates left after
|
|
51
|
+
# playing them (lower is better), preferring words that can still be
|
|
52
|
+
# the answer on ties. Large sets are estimated from an even sample.
|
|
53
|
+
def suggest(limit = 10)
|
|
54
|
+
targets = likely
|
|
55
|
+
return [] if targets.empty?
|
|
56
|
+
|
|
57
|
+
sample = spread(targets, SAMPLE_SIZE)
|
|
58
|
+
scale = targets.size.to_f / (sample.size**2)
|
|
59
|
+
possible = @candidates.to_h { |w| [w, true] }
|
|
60
|
+
|
|
61
|
+
ranked = probes(targets).map do |probe|
|
|
62
|
+
buckets = Hash.new(0)
|
|
63
|
+
sample.each { |answer| buckets[Feedback.score(probe, answer)] += 1 }
|
|
64
|
+
expected = buckets.each_value.sum { |n| n * n } * scale
|
|
65
|
+
Suggestion.new(probe, expected, possible.key?(probe))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
ranked.min_by(limit) { |s| [s.expected, s.possible ? 0 : 1, s.word] }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def spread(words, size)
|
|
74
|
+
return words if words.size <= size
|
|
75
|
+
|
|
76
|
+
Array.new(size) { |i| words[i * words.size / size] }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The targets themselves plus guesses whose letters split the targets
|
|
80
|
+
# most evenly, which can narrow them down better than any target can.
|
|
81
|
+
def probes(targets)
|
|
82
|
+
frequency = Hash.new(0)
|
|
83
|
+
targets.each { |w| w.chars.uniq.each { |c| frequency[c] += 1 } }
|
|
84
|
+
split = frequency.transform_values { |n| n * (targets.size - n) }
|
|
85
|
+
coverage = ->(w) { -w.chars.uniq.sum { |c| split.fetch(c, 0) } }
|
|
86
|
+
|
|
87
|
+
own = targets.size <= PROBE_SIZE ? targets : targets.min_by(PROBE_SIZE, &coverage)
|
|
88
|
+
others = @dictionary.guesses.min_by(PROBE_SIZE / 2, &coverage)
|
|
89
|
+
own | others
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require "ruby-wordle/feedback"
|
|
2
|
+
|
|
3
|
+
module RubyWordle
|
|
4
|
+
# Terminal styling. With color disabled, marks are shown with brackets:
|
|
5
|
+
# [A] correct, (A) present elsewhere, and a plain lowercase letter absent.
|
|
6
|
+
class Style
|
|
7
|
+
CODES = { bold: 1, dim: 2, underline: 4, red: 31, green: 32, yellow: 33,
|
|
8
|
+
blue: 34, magenta: 35, cyan: 36, gray: 90 }.freeze
|
|
9
|
+
# Fixed 256-color tiles (green, yellow, gray with white letters) look the
|
|
10
|
+
# same in every terminal theme.
|
|
11
|
+
TILE_CODES = { Feedback::CORRECT => "1;38;5;231;48;5;71", Feedback::PRESENT => "1;38;5;231;48;5;178",
|
|
12
|
+
Feedback::ABSENT => "1;38;5;231;48;5;240" }.freeze
|
|
13
|
+
EMOJI = { Feedback::CORRECT => "🟩", Feedback::PRESENT => "🟨", Feedback::ABSENT => "⬛" }.freeze
|
|
14
|
+
|
|
15
|
+
def self.for(output, env = ENV)
|
|
16
|
+
new(color: output.respond_to?(:tty?) && output.tty? && env["NO_COLOR"].to_s.empty?)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(color:)
|
|
20
|
+
@color = color
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def color?
|
|
24
|
+
@color
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def paint(text, *styles)
|
|
28
|
+
return text.to_s unless @color && !styles.empty?
|
|
29
|
+
|
|
30
|
+
"\e[#{styles.map { |s| CODES.fetch(s) }.join(';')}m#{text}\e[0m"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# A letter tile; +mark+ is a Feedback mark, or nil for an untested letter.
|
|
34
|
+
def tile(char, mark)
|
|
35
|
+
if @color
|
|
36
|
+
mark ? "\e[#{TILE_CODES.fetch(mark)}m #{char.upcase} \e[0m" : " #{char.upcase} "
|
|
37
|
+
else
|
|
38
|
+
case mark
|
|
39
|
+
when Feedback::CORRECT then "[#{char.upcase}]"
|
|
40
|
+
when Feedback::PRESENT then "(#{char.upcase})"
|
|
41
|
+
when Feedback::ABSENT then " #{char.downcase} "
|
|
42
|
+
else " #{char.upcase} "
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def row(word, pattern)
|
|
48
|
+
marks = Feedback.decode(pattern, word.length)
|
|
49
|
+
word.chars.zip(marks).map { |char, mark| tile(char, mark) }.join(" ")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def emoji(pattern, length)
|
|
53
|
+
Feedback.decode(pattern, length).map { |mark| EMOJI[mark] }.join
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/ruby-wordle/version.rb
CHANGED
data/lib/ruby-wordle.rb
ADDED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
april
|
|
2
|
-
bible
|
|
3
|
-
false
|
|
4
|
-
islam
|
|
5
|
-
march
|
|
6
|
-
x-ray
|
|
7
1
|
abhor
|
|
8
2
|
abide
|
|
9
3
|
abort
|
|
@@ -333,6 +327,7 @@ facet
|
|
|
333
327
|
faint
|
|
334
328
|
fairy
|
|
335
329
|
faith
|
|
330
|
+
false
|
|
336
331
|
fancy
|
|
337
332
|
farce
|
|
338
333
|
fault
|
|
@@ -523,7 +518,6 @@ leafy
|
|
|
523
518
|
learn
|
|
524
519
|
least
|
|
525
520
|
lemon
|
|
526
|
-
let's
|
|
527
521
|
light
|
|
528
522
|
liken
|
|
529
523
|
liner
|
|
@@ -1107,7 +1101,6 @@ wring
|
|
|
1107
1101
|
wrist
|
|
1108
1102
|
write
|
|
1109
1103
|
wrong
|
|
1110
|
-
x-ray
|
|
1111
1104
|
yacht
|
|
1112
1105
|
yearn
|
|
1113
1106
|
yeast
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
april
|
|
2
|
-
bible
|
|
3
|
-
false
|
|
4
|
-
islam
|
|
5
|
-
march
|
|
6
|
-
satan
|
|
7
|
-
x-ray
|
|
8
1
|
abate
|
|
9
2
|
abbey
|
|
10
3
|
abhor
|
|
@@ -501,6 +494,7 @@ facet
|
|
|
501
494
|
faint
|
|
502
495
|
fairy
|
|
503
496
|
faith
|
|
497
|
+
false
|
|
504
498
|
fancy
|
|
505
499
|
farce
|
|
506
500
|
fatal
|
|
@@ -776,7 +770,6 @@ ledge
|
|
|
776
770
|
leech
|
|
777
771
|
legal
|
|
778
772
|
lemon
|
|
779
|
-
let's
|
|
780
773
|
level
|
|
781
774
|
lever
|
|
782
775
|
libel
|
|
@@ -1592,7 +1585,6 @@ wring
|
|
|
1592
1585
|
wrist
|
|
1593
1586
|
write
|
|
1594
1587
|
wrong
|
|
1595
|
-
x-ray
|
|
1596
1588
|
yacht
|
|
1597
1589
|
yearn
|
|
1598
1590
|
yeast
|
metadata
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-wordle
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yoichiro Hasebe
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
13
|
+
name: reline
|
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
|
16
15
|
requirements:
|
|
17
|
-
- - "
|
|
16
|
+
- - "~>"
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
18
|
+
version: '0.3'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
|
-
- - "
|
|
23
|
+
- - "~>"
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
27
|
-
description: A
|
|
28
|
-
|
|
25
|
+
version: '0.3'
|
|
26
|
+
description: A command-line Wordle game and a solver that ranks the next guess, with
|
|
27
|
+
English word lists.
|
|
29
28
|
email:
|
|
30
29
|
- yohasebe@gmail.com
|
|
31
30
|
executables:
|
|
@@ -33,31 +32,29 @@ executables:
|
|
|
33
32
|
extensions: []
|
|
34
33
|
extra_rdoc_files: []
|
|
35
34
|
files:
|
|
36
|
-
-
|
|
37
|
-
- Gemfile
|
|
35
|
+
- CHANGELOG.md
|
|
38
36
|
- LICENSE
|
|
39
37
|
- README.md
|
|
40
|
-
- Rakefile
|
|
41
38
|
- bin/ruby-wordle
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
- lib/ruby-wordle/
|
|
45
|
-
- lib/ruby-wordle/
|
|
46
|
-
- lib/ruby-wordle/
|
|
47
|
-
- lib/ruby-wordle/
|
|
39
|
+
- lib/ruby-wordle.rb
|
|
40
|
+
- lib/ruby-wordle/cli.rb
|
|
41
|
+
- lib/ruby-wordle/dictionary.rb
|
|
42
|
+
- lib/ruby-wordle/feedback.rb
|
|
43
|
+
- lib/ruby-wordle/game.rb
|
|
44
|
+
- lib/ruby-wordle/solver.rb
|
|
45
|
+
- lib/ruby-wordle/style.rb
|
|
48
46
|
- lib/ruby-wordle/version.rb
|
|
49
|
-
- ruby-wordle.gemspec
|
|
50
47
|
- word-lists/5-letters/word-list-basic-uniq-letters.txt
|
|
51
48
|
- word-lists/5-letters/word-list-basic.txt
|
|
52
49
|
- word-lists/5-letters/word-list-uniq-letters.txt
|
|
53
50
|
- word-lists/5-letters/word-list.txt
|
|
54
|
-
|
|
55
|
-
- word-lists/word-list-original.txt
|
|
56
|
-
homepage: http://github.com/yohasebe/ruby-wordle
|
|
51
|
+
homepage: https://github.com/yohasebe/ruby-wordle
|
|
57
52
|
licenses:
|
|
58
53
|
- MIT
|
|
59
|
-
metadata:
|
|
60
|
-
|
|
54
|
+
metadata:
|
|
55
|
+
source_code_uri: https://github.com/yohasebe/ruby-wordle
|
|
56
|
+
changelog_uri: https://github.com/yohasebe/ruby-wordle/blob/main/CHANGELOG.md
|
|
57
|
+
rubygems_mfa_required: 'true'
|
|
61
58
|
rdoc_options: []
|
|
62
59
|
require_paths:
|
|
63
60
|
- lib
|
|
@@ -65,15 +62,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
65
62
|
requirements:
|
|
66
63
|
- - ">="
|
|
67
64
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
65
|
+
version: '3.2'
|
|
69
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
67
|
requirements:
|
|
71
68
|
- - ">="
|
|
72
69
|
- !ruby/object:Gem::Version
|
|
73
70
|
version: '0'
|
|
74
71
|
requirements: []
|
|
75
|
-
rubygems_version: 3.
|
|
76
|
-
signing_key:
|
|
72
|
+
rubygems_version: 3.6.9
|
|
77
73
|
specification_version: 4
|
|
78
|
-
summary:
|
|
74
|
+
summary: Play Wordle and solve Wordle on the command line
|
|
79
75
|
test_files: []
|
data/.gitignore
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
*.gem
|
|
2
|
-
*.rbc
|
|
3
|
-
/.config
|
|
4
|
-
/coverage/
|
|
5
|
-
/InstalledFiles
|
|
6
|
-
/pkg/
|
|
7
|
-
/spec/reports/
|
|
8
|
-
/spec/examples.txt
|
|
9
|
-
/test/tmp/
|
|
10
|
-
/test/version_tmp/
|
|
11
|
-
/tmp/
|
|
12
|
-
|
|
13
|
-
# Used by dotenv library to load environment variables.
|
|
14
|
-
# .env
|
|
15
|
-
|
|
16
|
-
# Ignore Byebug command history file.
|
|
17
|
-
.byebug_history
|
|
18
|
-
|
|
19
|
-
## Specific to RubyMotion:
|
|
20
|
-
.dat*
|
|
21
|
-
.repl_history
|
|
22
|
-
build/
|
|
23
|
-
*.bridgesupport
|
|
24
|
-
build-iPhoneOS/
|
|
25
|
-
build-iPhoneSimulator/
|
|
26
|
-
|
|
27
|
-
## Specific to RubyMotion (use of CocoaPods):
|
|
28
|
-
#
|
|
29
|
-
# We recommend against adding the Pods directory to your .gitignore. However
|
|
30
|
-
# you should judge for yourself, the pros and cons are mentioned at:
|
|
31
|
-
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
|
32
|
-
#
|
|
33
|
-
# vendor/Pods/
|
|
34
|
-
|
|
35
|
-
## Documentation cache and generated files:
|
|
36
|
-
/.yardoc/
|
|
37
|
-
/_yardoc/
|
|
38
|
-
/doc/
|
|
39
|
-
/rdoc/
|
|
40
|
-
|
|
41
|
-
## Environment normalization:
|
|
42
|
-
/.bundle/
|
|
43
|
-
/vendor/bundle
|
|
44
|
-
/lib/bundler/man/
|
|
45
|
-
|
|
46
|
-
# for a library or gem, you might want to ignore these files since the code is
|
|
47
|
-
# intended to run in multiple environments; otherwise, check them in:
|
|
48
|
-
# Gemfile.lock
|
|
49
|
-
# .ruby-version
|
|
50
|
-
# .ruby-gemset
|
|
51
|
-
|
|
52
|
-
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
53
|
-
.rvmrc
|
|
54
|
-
|
|
55
|
-
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
|
56
|
-
# .rubocop-https?--*
|
|
57
|
-
|
|
58
|
-
.DS_Store
|
data/Gemfile
DELETED
data/Rakefile
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
require "bundler/gem_tasks"
|
data/img/ruby-wordle-play.png
DELETED
|
Binary file
|
data/img/ruby-wordle-solver.png
DELETED
|
Binary file
|