ruby-wordle 0.9.2
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 +7 -0
- data/.gitignore +58 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +101 -0
- data/Rakefile +1 -0
- data/bin/ruby-wordle +24 -0
- data/img/ruby-wordle-play.png +0 -0
- data/img/ruby-wordle-solver.png +0 -0
- data/lib/ruby-wordle/script-filter.rb +48 -0
- data/lib/ruby-wordle/script-play.rb +124 -0
- data/lib/ruby-wordle/script-solver.rb +179 -0
- data/lib/ruby-wordle/script-utils.rb +23 -0
- data/lib/ruby-wordle/version.rb +3 -0
- data/ruby-wordle.gemspec +21 -0
- data/word-lists/5-letters/word-list-basic-uniq-letters.txt +1118 -0
- data/word-lists/5-letters/word-list-basic.txt +1603 -0
- data/word-lists/5-letters/word-list-uniq-letters.txt +10173 -0
- data/word-lists/5-letters/word-list.txt +15918 -0
- data/word-lists/word-list-basic.txt +12937 -0
- data/word-lists/word-list-original.txt +370103 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 911b41c72a106def560cc14de90dce996c354e615054d67ea3d3c150a29dad21
|
4
|
+
data.tar.gz: 80a2fabaa942be49066e69af631ae473b2f5b774439fb2aa626e7e6748f3498f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b363d91c62414d150ae0675ea0345253659d20e7f2202c0c2a2fa5ba516125ace8f3a3023c21878dbf29b0c0d8a990c9ed755d4ac4fc0b2ed3ab47a6998660a3
|
7
|
+
data.tar.gz: 77816914e691cae8ee15bd5a11d8ff1e2c06b873d88de5bbb5b7c3e2e9676a8cd87d7674c7cbdb0f996546e82c28baea718b904a70a6b6cfb506e4b1d407480e
|
data/.gitignore
ADDED
@@ -0,0 +1,58 @@
|
|
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
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 Yoichiro Hasebe
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Ruby-Wordle
|
2
|
+
|
3
|
+
A set of ruby scripts to generate word-lists, solve Wordle, and play Wordle on the command line
|
4
|
+
|
5
|
+
⬜️⬜️⬜️⬜️⬜️ \
|
6
|
+
⬜️⬜️⬜️🟨🟨 \
|
7
|
+
⬜️🟩⬜️🟩⬜️ \
|
8
|
+
🟩🟩⬜️🟩🟩 \
|
9
|
+
🟩🟩🟩🟩🟩
|
10
|
+
|
11
|
+
----
|
12
|
+
|
13
|
+
### Installation
|
14
|
+
|
15
|
+
```
|
16
|
+
> gem install ruby-wordle
|
17
|
+
```
|
18
|
+
|
19
|
+
----
|
20
|
+
|
21
|
+
### Play Wordle
|
22
|
+
|
23
|
+
**Usage Example**
|
24
|
+
|
25
|
+
```plain-text
|
26
|
+
> ruby-worldle play
|
27
|
+
```
|
28
|
+
|
29
|
+
<img src='https://github.com/yohasebe/ruby-wordle/blob/main/img/ruby-wordle-play.png?raw=true' width='500' />
|
30
|
+
|
31
|
+
You can ask for help by typing "?↵" while playing. In the suggestion list, basic words consisting of 5 different letters are printed in red 🟥 while basic words having the same letters used more than once are printed in blue 🟦
|
32
|
+
|
33
|
+
----
|
34
|
+
|
35
|
+
### Wordle Solver
|
36
|
+
|
37
|
+
**Usage Example**
|
38
|
+
|
39
|
+
```plain-text
|
40
|
+
> ruby-worldle solver
|
41
|
+
```
|
42
|
+
|
43
|
+
<img src='https://github.com/yohasebe/ruby-wordle/blob/main/img/ruby-wordle-solver.png?raw=true' width='500' />
|
44
|
+
|
45
|
+
In the result list, basic words consisting of 5 different letters are printed in red 🟥 while basic words having the same letters used more than once are printed in blue 🟦
|
46
|
+
|
47
|
+
----
|
48
|
+
|
49
|
+
### Word Lists
|
50
|
+
|
51
|
+
#### Word List Generator Script
|
52
|
+
|
53
|
+
🟢 `lib/ruby-wordle/script-filter.rb`
|
54
|
+
|
55
|
+
A ruby script to generate *n*-letter word lists (`word-list.txt` and `word-list-uniq.txt`)
|
56
|
+
|
57
|
+
The default *n* value is 5. Change `NUM_LETTERS` if necessary, and run the script as follows. A directory will be created automatically with the title having the number specified in the script (e.g. `word-lists/5-letters`, `word-lists/6-letters`) and resulting word lists will be stored in that directory.
|
58
|
+
|
59
|
+
----
|
60
|
+
|
61
|
+
#### Lists of English Words of 5 Letters (useful to solve Wordle)
|
62
|
+
|
63
|
+
🟡 `word-lists/5-letters/word-list.txt`
|
64
|
+
|
65
|
+
A list of five-letter words, generated from the original word list.
|
66
|
+
|
67
|
+
🟡 `word-lists/5-letters/word-list-uniq-letters.txt`
|
68
|
+
|
69
|
+
A list of five-letter words consisting of five different letters, generated from the original word list.
|
70
|
+
|
71
|
+
🟡 `word-lists/5-letters/word-list-basic.txt`
|
72
|
+
|
73
|
+
A list of five-letter words, generated from the basic word list.
|
74
|
+
|
75
|
+
🟡 `word-lists/5-letters/word-list-basic-uniq-letters.txt`
|
76
|
+
|
77
|
+
A list of five-letter words consisting of five different letters, generated from the basic word list.
|
78
|
+
|
79
|
+
----
|
80
|
+
|
81
|
+
#### Original/Basic Lists of English Words
|
82
|
+
|
83
|
+
⚪️ `word-lists/word-list-original.txt`
|
84
|
+
|
85
|
+
A large word list based on `words_alpha.txt` in [dwyl/english-words](https://github.com/dwyl/english-words), containing more than 370,000 items.
|
86
|
+
|
87
|
+
⚪️ `word-lists/word-list-basic.txt`
|
88
|
+
|
89
|
+
A smaller word list containing about 13,000 basic English words.
|
90
|
+
|
91
|
+
----
|
92
|
+
|
93
|
+
### Author
|
94
|
+
|
95
|
+
Yoichiro HASEBE
|
96
|
+
|
97
|
+
----
|
98
|
+
|
99
|
+
### License
|
100
|
+
|
101
|
+
MIT
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/ruby-wordle
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH << File.join( File.dirname(__FILE__), '..', 'lib')
|
4
|
+
$LOAD_PATH << File.join( File.dirname(__FILE__), '..', 'lib', 'ruby-wordle')
|
5
|
+
|
6
|
+
require 'version'
|
7
|
+
require 'script-play'
|
8
|
+
require 'script-solver'
|
9
|
+
|
10
|
+
if ARGV[0] == "-v" || ARGV[0] == "--version"
|
11
|
+
puts "Ruby Wordle #{RubyWordle::VERSION}"
|
12
|
+
exit
|
13
|
+
elsif ARGV[0] == "play" || ARGV[0] == "play" || ARGV[0] == "p"
|
14
|
+
RubyWordlePlay::play_wordle
|
15
|
+
elsif ARGV[0] == "solver" || ARGV[0] == "solve" || ARGV[0] == "s"
|
16
|
+
RubyWordleSolver::run_interactively
|
17
|
+
else
|
18
|
+
puts "Ruby Wordle #{RubyWordle::VERSION}"; puts
|
19
|
+
puts "Usage: ruby-wordle (play|solve)"
|
20
|
+
puts; puts "See https://github.com/yohasebe/ruby-wordle"
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
|
Binary file
|
Binary file
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
NUM_LETTERS = 5
|
4
|
+
|
5
|
+
HOME_DIR = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
|
6
|
+
|
7
|
+
original_word_list = File.readlines("#{HOME_DIR}/word-lists/word-list-original.txt")
|
8
|
+
basic_word_list = File.readlines("#{HOME_DIR}/word-lists/word-list-basic.txt")
|
9
|
+
n_letter_dir = "#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters"
|
10
|
+
|
11
|
+
unless Dir.exist? n_letter_dir
|
12
|
+
Dir.mkdir File.expand_path(n_letter_dir)
|
13
|
+
end
|
14
|
+
|
15
|
+
n_letter_word_file = File.new(File.join(n_letter_dir, "word-list.txt"), "w")
|
16
|
+
uniq_n_letter_word_file = File.new(File.join(n_letter_dir, "word-list-uniq-letters.txt"), "w")
|
17
|
+
basic_n_letter_word_file = File.new(File.join(n_letter_dir, "word-list-basic.txt"), "w")
|
18
|
+
uniq_basic_n_letter_word_file = File.new(File.join(n_letter_dir, "word-list-basic-uniq-letters.txt"), "w")
|
19
|
+
|
20
|
+
original_word_list.sort!.uniq!
|
21
|
+
original_word_list.each do |line|
|
22
|
+
chars = line.strip.split(//)
|
23
|
+
if chars.size == NUM_LETTERS
|
24
|
+
line.downcase!
|
25
|
+
n_letter_word_file.write line
|
26
|
+
if chars.uniq.size == chars.size
|
27
|
+
uniq_n_letter_word_file.write line
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
basic_word_list.sort!.uniq!
|
33
|
+
basic_word_list.each do |line|
|
34
|
+
chars = line.strip.split(//)
|
35
|
+
if chars.size == NUM_LETTERS
|
36
|
+
line.downcase!
|
37
|
+
basic_n_letter_word_file.write line
|
38
|
+
if chars.uniq.size == chars.size
|
39
|
+
uniq_basic_n_letter_word_file.write line
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
n_letter_word_file.close
|
45
|
+
uniq_n_letter_word_file.close
|
46
|
+
basic_n_letter_word_file.close
|
47
|
+
uniq_basic_n_letter_word_file.close
|
48
|
+
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require "readline"
|
4
|
+
require "script-solver"
|
5
|
+
|
6
|
+
module RubyWordlePlay
|
7
|
+
NUM_LETTERS = 5
|
8
|
+
NUM_ATTEMPTS = 6
|
9
|
+
|
10
|
+
LPAD = " " * 1
|
11
|
+
PROMPT = "〉".bold.red
|
12
|
+
|
13
|
+
@word_list_basic = File.readlines("#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters/word-list-basic.txt").map(&:strip)
|
14
|
+
@word_list_original = File.readlines("#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters/word-list.txt").map(&:strip)
|
15
|
+
@word = @word_list_basic.sample
|
16
|
+
@not_used = []
|
17
|
+
@not_used_here = {1 => [], 2 => [], 3 => [], 4 => [], 5 => []}
|
18
|
+
@letters_known = ".....".split(//)
|
19
|
+
|
20
|
+
def self.check_dict(attempt)
|
21
|
+
@word_list_original.include?(attempt)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.evaluate(attempt)
|
25
|
+
puts
|
26
|
+
@letters_known = attempt.split(//)
|
27
|
+
correct = []
|
28
|
+
wrong = []
|
29
|
+
@word.split(//).each_with_index do |l, i|
|
30
|
+
if attempt[i] == l
|
31
|
+
correct << i
|
32
|
+
else
|
33
|
+
wrong << i
|
34
|
+
@letters_known[i] = '.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
attempt.split(//).each_with_index do |l, i|
|
38
|
+
if correct.include?(i)
|
39
|
+
print " #{@word[i]} ".upcase.bg_green
|
40
|
+
else
|
41
|
+
elsewhere = wrong.map{|i|@word[i]}.index(l)
|
42
|
+
if elsewhere
|
43
|
+
print " #{l} ".upcase.bg_brown
|
44
|
+
@not_used_here[i + 1] << l unless @not_used_here[i + 1].include? l
|
45
|
+
else
|
46
|
+
print " ".bg_gray
|
47
|
+
@not_used << attempt[i] if /[a-z]/ =~ attempt[i]
|
48
|
+
@not_used.uniq!
|
49
|
+
@not_used.sort!
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
puts
|
54
|
+
correct.size == NUM_LETTERS
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.get_help
|
58
|
+
letters_used = []
|
59
|
+
@not_used_here.each do |k, v|
|
60
|
+
v.each do |r|
|
61
|
+
letters_used << [k - 1, r]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
RubyWordleSolver.solve_wordle(@letters_known.join, letters_used, @not_used, :basic, @word)
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.ask_for_attempt(str)
|
68
|
+
puts
|
69
|
+
print " #{str} ".bold.bg_cyan + " "
|
70
|
+
attempt = Readline.readline(LPAD + PROMPT, true).strip.downcase
|
71
|
+
if attempt == "quit" || attempt == "q"
|
72
|
+
exit
|
73
|
+
elsif attempt == "help" || attempt == "h" || attempt == "?"
|
74
|
+
get_help
|
75
|
+
attempt = ask_for_attempt(str)
|
76
|
+
end
|
77
|
+
|
78
|
+
unless /\A[a-z]{5}\z/ =~ attempt
|
79
|
+
puts
|
80
|
+
puts LPAD + "Sorry,".red + " the input format is not right"
|
81
|
+
puts
|
82
|
+
attempt = ask_for_attempt(str)
|
83
|
+
end
|
84
|
+
|
85
|
+
unless check_dict(attempt)
|
86
|
+
puts
|
87
|
+
puts LPAD + "Sorry,".red + " it's not listed in the dictionary"
|
88
|
+
puts
|
89
|
+
attempt = ask_for_attempt(str)
|
90
|
+
end
|
91
|
+
attempt
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.draw_decorated_border
|
95
|
+
puts
|
96
|
+
puts (' '.bg_green * 5 + ' '.bg_gray * 3 + ' '.bg_brown * 5 + ' '.bg_gray * 3) * 4
|
97
|
+
puts
|
98
|
+
end
|
99
|
+
|
100
|
+
def play_wordle
|
101
|
+
draw_decorated_border;
|
102
|
+
puts "Input a word of 5 letters and press \"↵\"".cyan.bold
|
103
|
+
puts "Type \"?↵\" for help, \"quit↵\" to exit".gray
|
104
|
+
|
105
|
+
NUM_ATTEMPTS.times do |i|
|
106
|
+
attempt = ask_for_attempt("#{i + 1}/#{NUM_ATTEMPTS}")
|
107
|
+
result = evaluate(attempt)
|
108
|
+
puts
|
109
|
+
if result
|
110
|
+
puts "C O R R E C T !".upcase.bg_magenta
|
111
|
+
exit
|
112
|
+
elsif i + 1 == NUM_ATTEMPTS
|
113
|
+
puts " ANSWER: #{@word} ".upcase.bg_red
|
114
|
+
exit
|
115
|
+
else
|
116
|
+
puts @not_used.uniq.map{|l| " #{l.upcase} "}.join.bg_black if !@not_used.empty?
|
117
|
+
puts
|
118
|
+
end
|
119
|
+
end
|
120
|
+
draw_decorated_border
|
121
|
+
end
|
122
|
+
|
123
|
+
module_function :play_wordle
|
124
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
HOME_DIR = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
|
4
|
+
|
5
|
+
require "readline"
|
6
|
+
require "script-utils"
|
7
|
+
|
8
|
+
module RubyWordleSolver
|
9
|
+
NUM_LETTERS = 5
|
10
|
+
MAX_SUGGESTIONS = 128
|
11
|
+
|
12
|
+
LPAD = " " * 7
|
13
|
+
PROMPT = "〉".bold.red
|
14
|
+
|
15
|
+
@word_list = File.readlines("#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters/word-list.txt").map(&:strip)
|
16
|
+
@word_list_basic = File.readlines("#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters/word-list-basic.txt").map(&:strip)
|
17
|
+
|
18
|
+
@basic_word_list = {}
|
19
|
+
File.readlines("#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters/word-list-basic.txt").map(&:strip).each do |b|
|
20
|
+
@basic_word_list[b] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
@basic_word_uniq_list = {}
|
24
|
+
File.readlines("#{HOME_DIR}/word-lists/#{NUM_LETTERS}-letters/word-list-basic-uniq-letters.txt").map(&:strip).each do |b|
|
25
|
+
@basic_word_uniq_list[b] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
@letters_known = nil
|
29
|
+
@letters_used = nil
|
30
|
+
@letters_not_used = nil
|
31
|
+
|
32
|
+
def self.get_letters_known
|
33
|
+
puts
|
34
|
+
puts "STEP 1".bold.bg_green + " Input " + "known letters".underline + " using alphabets and dots"
|
35
|
+
puts LPAD + "Then press \"↵\" (Type \"quit↵\" to exit)"
|
36
|
+
if @letters_known
|
37
|
+
puts LPAD + "Last time: #{@letters_known.bg_green}"
|
38
|
+
else
|
39
|
+
puts LPAD + "Example: #{'s...h'.bg_green}"
|
40
|
+
end
|
41
|
+
|
42
|
+
letters_known = Readline.readline(LPAD + PROMPT, true).strip.downcase
|
43
|
+
|
44
|
+
exit if letters_known == "quit" || letters_known == "q"
|
45
|
+
unless /\A[a-z\.]{5}\z/ =~ letters_known
|
46
|
+
puts LPAD + "Sorry,".red + " the input format is not right"
|
47
|
+
letters_known = get_letters_known
|
48
|
+
end
|
49
|
+
letters_known
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.get_letters_used
|
53
|
+
puts
|
54
|
+
puts "STEP 2".bold.bg_brown + " Input pairs of " + "a position and a letter".underline
|
55
|
+
puts LPAD + "Then press \"↵\" (Type \"quit↵\" to exit)"
|
56
|
+
if @letters_used
|
57
|
+
puts LPAD + "Last time: #{@letters_used.map{|p, l| (p + 1).to_s + l}.join(' ').bg_brown}"
|
58
|
+
else
|
59
|
+
puts LPAD + "Example: #{'1g 2e 5a'.bg_brown}"
|
60
|
+
end
|
61
|
+
|
62
|
+
letters_used = Readline.readline(LPAD + PROMPT, true).strip.downcase
|
63
|
+
|
64
|
+
exit if letters_used == "quit" || letters_used == "q"
|
65
|
+
if letters_used.strip == ""
|
66
|
+
array_letters_used = []
|
67
|
+
elsif /\A(?:[1-5][a-z][\s,]*)+\z/ =~ letters_used
|
68
|
+
array_letters_used = []
|
69
|
+
letters_used.gsub(/\s+/, "").split(//).each_slice(2) do |k, v|
|
70
|
+
array_letters_used << [k.to_i - 1, v]
|
71
|
+
end
|
72
|
+
else
|
73
|
+
puts LPAD + "Sorry,".red + " the input format is not right"
|
74
|
+
array_letters_used = get_letters_used
|
75
|
+
end
|
76
|
+
array_letters_used
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.get_letters_not_used
|
80
|
+
puts
|
81
|
+
puts "STEP 3".bold.bg_gray + " Input " + "letters not used".underline + " in the word"
|
82
|
+
puts LPAD + "Then press \"↵\" (Type \"quit↵\" to exit)"
|
83
|
+
if @letters_not_used
|
84
|
+
puts LPAD + "Last time: #{@letters_not_used.join.bg_gray}"
|
85
|
+
else
|
86
|
+
puts LPAD + "Example: #{'ieagh'.bg_gray}"
|
87
|
+
end
|
88
|
+
|
89
|
+
letters_not_used = Readline.readline(LPAD + PROMPT, true).strip.downcase
|
90
|
+
|
91
|
+
exit if letters_not_used == "quit" || letters_not_used == "q"
|
92
|
+
if letters_not_used.strip == ""
|
93
|
+
array_letters_not_used = []
|
94
|
+
elsif /\A[a-z\s]+\z/ =~ letters_not_used
|
95
|
+
array_letters_not_used = letters_not_used.split(//)
|
96
|
+
else
|
97
|
+
puts LPAD + "Sorry,".red + " the input format is not right"
|
98
|
+
array_letters_not_used = get_letters_not_used
|
99
|
+
end
|
100
|
+
array_letters_not_used
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.draw_decorated_border
|
104
|
+
puts; puts (' '.bg_green * 5 + ' '.bg_gray * 3 + ' '.bg_brown * 5 + ' '.bg_gray * 3) * 4; puts
|
105
|
+
end
|
106
|
+
|
107
|
+
def solve_wordle(letters_known, letters_used, letters_not_used, mode = :original, answer = nil)
|
108
|
+
word_list = mode == :original ? @word_list : @word_list_basic
|
109
|
+
indices_unknown_letters = (0 ... letters_known.length).find_all { |i| letters_known[i, 1] == '.' }
|
110
|
+
|
111
|
+
word_list_a = word_list.select do |word|
|
112
|
+
Regexp.compile(letters_known) =~ word
|
113
|
+
end
|
114
|
+
|
115
|
+
word_list_b = word_list_a.select do |word|
|
116
|
+
word_letters = word.split(//)
|
117
|
+
letters_in_candidate = word_letters.values_at(*indices_unknown_letters) - letters_used.map{|l| l[1]}
|
118
|
+
cond1 = letters_used.all? do |position, letter|
|
119
|
+
word_letters[position] != letter
|
120
|
+
end
|
121
|
+
cond2 = (letters_used.map{|l| l[1]} - word_letters).empty?
|
122
|
+
cond3 = (letters_in_candidate - letters_not_used).size == letters_in_candidate.size
|
123
|
+
cond1 && cond2 && cond3
|
124
|
+
end
|
125
|
+
|
126
|
+
if answer
|
127
|
+
word_list_b.delete(answer)
|
128
|
+
reduced_list = word_list_b.sample(MAX_SUGGESTIONS - 1)
|
129
|
+
reduced_list << answer
|
130
|
+
else
|
131
|
+
reduced_list = word_list_b.sample(MAX_SUGGESTIONS)
|
132
|
+
end
|
133
|
+
|
134
|
+
results = reduced_list.sort.map do |word|
|
135
|
+
if @basic_word_uniq_list[word]
|
136
|
+
mode == :original ? word.red.bold : word.red
|
137
|
+
elsif @basic_word_list[word]
|
138
|
+
mode == :original ? word.blue.bold : word.blue
|
139
|
+
else
|
140
|
+
word
|
141
|
+
end
|
142
|
+
end
|
143
|
+
puts
|
144
|
+
results.each_with_index do |word, i|
|
145
|
+
if (i + 1) % 8 == 0 || results.size == i + 1
|
146
|
+
print word + "\n"
|
147
|
+
else
|
148
|
+
print word + " "
|
149
|
+
end
|
150
|
+
end
|
151
|
+
word_list_b
|
152
|
+
end
|
153
|
+
|
154
|
+
def run_interactively
|
155
|
+
draw_decorated_border
|
156
|
+
@letters_known = get_letters_known
|
157
|
+
@letters_used = get_letters_used
|
158
|
+
@letters_not_used = get_letters_not_used
|
159
|
+
results = solve_wordle(@letters_known, @letters_used, @letters_not_used)
|
160
|
+
draw_decorated_border
|
161
|
+
|
162
|
+
exit if results.size < 2
|
163
|
+
|
164
|
+
puts " ? ".bold.bg_magenta + " Press \"↵\" to continue."
|
165
|
+
puts LPAD + "Type \"quit↵\" to exit."
|
166
|
+
|
167
|
+
response = Readline.readline(LPAD + PROMPT, true).strip.downcase
|
168
|
+
|
169
|
+
if response == "quit" || response == "q"
|
170
|
+
exit
|
171
|
+
else
|
172
|
+
run_interactively
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
module_function :run_interactively
|
177
|
+
module_function :solve_wordle
|
178
|
+
end
|
179
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class String
|
2
|
+
def black; "\e[30m#{self}\e[0m" end
|
3
|
+
def red; "\e[31m#{self}\e[0m" end
|
4
|
+
def green; "\e[32m#{self}\e[0m" end
|
5
|
+
def brown; "\e[33m#{self}\e[0m" end
|
6
|
+
def blue; "\e[34m#{self}\e[0m" end
|
7
|
+
def magenta; "\e[35m#{self}\e[0m" end
|
8
|
+
def cyan; "\e[36m#{self}\e[0m" end
|
9
|
+
def gray; "\e[37m#{self}\e[0m" end
|
10
|
+
def bg_black; "\e[40m#{self}\e[0m" end
|
11
|
+
def bg_red; "\e[41m#{self}\e[0m" end
|
12
|
+
def bg_green; "\e[42m#{self}\e[0m" end
|
13
|
+
def bg_brown; "\e[43m#{self}\e[0m" end
|
14
|
+
def bg_blue; "\e[44m#{self}\e[0m" end
|
15
|
+
def bg_magenta; "\e[45m#{self}\e[0m" end
|
16
|
+
def bg_cyan; "\e[46m#{self}\e[0m" end
|
17
|
+
def bg_gray; "\e[47m#{self}\e[0m" end
|
18
|
+
def bold; "\e[1m#{self}\e[22m" end
|
19
|
+
def italic; "\e[3m#{self}\e[23m" end
|
20
|
+
def underline; "\e[4m#{self}\e[24m" end
|
21
|
+
def blink; "\e[5m#{self}\e[25m" end
|
22
|
+
def reverse_color; "\e[7m#{self}\e[27m" end
|
23
|
+
end
|
data/ruby-wordle.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "ruby-wordle/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "ruby-wordle"
|
8
|
+
s.version = RubyWordle::VERSION
|
9
|
+
s.authors = ["Yoichiro Hasebe"]
|
10
|
+
s.email = ["yohasebe@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/yohasebe/ruby-wordle"
|
12
|
+
s.summary = %q{A set of ruby scripts to generate word-lists, solve Wordle, and play Wordle}
|
13
|
+
s.description = %q{A set of ruby scripts to generate word-lists, solve Wordle, and play Wordle}
|
14
|
+
s.licenses = ["MIT"]
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
s.add_runtime_dependency 'readline'
|
21
|
+
end
|