scrabble-solver 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require File.dirname(__FILE__) + '/../lib/scrabble-solver'
5
+
6
+ options = Hash.new(nil)
7
+
8
+ # Use the optparse library to populate the options hash.
9
+ OptionParser.new do |o|
10
+ o.banner = 'Usage: scrabble-solver tiles [options]'
11
+
12
+ # Add the README contents to the --help flag.
13
+ o.separator "\n" + File.read(File.dirname(__FILE__) + '/../README.md') + "\n"
14
+
15
+ o.separator "Optional arguments:"
16
+
17
+ o.on '-h', '--help', 'Display this message' do
18
+ puts o
19
+ exit
20
+ end
21
+
22
+ o.on '--longer-than LENGH',
23
+ 'Only return words longer than LENGTH.' do |length|
24
+ options[:longer_than] = length
25
+ end
26
+
27
+ o.on '--shorter-than LENGH',
28
+ 'Only return words shorter than LENGTH.' do |length|
29
+ options[:shorter_than] = length
30
+ end
31
+
32
+ o.on '--starts-with PREFIX',
33
+ 'Only return words that start with PREFIX.' do |prefix|
34
+ options[:starts_with] = prefix
35
+ end
36
+
37
+ o.on '--ends-with SUFFIX',
38
+ 'Only return words that end with SUFFIX.' do |suffix|
39
+ options[:ends_with] = suffix
40
+ end
41
+
42
+ o.on '--contains PART',
43
+ 'Only return words that contain PART. Must be combined with --at.' do |part|
44
+ options[:contains] = part
45
+ end
46
+
47
+ o.on '--at INDEX',
48
+ 'The index to search for --contains at.' do |index|
49
+ options[:at] = index
50
+ end
51
+
52
+ o.on '--word-file FILE',
53
+ 'The word file to use. Defaults to a preset list of words.' do |file|
54
+ options[:word_file] = file
55
+ end
56
+ end.parse!
57
+
58
+ # Pull the tiles from the first argument.
59
+ letters = ARGV[0]
60
+
61
+ # Exit the program if no first argument was supplied.
62
+ if letters.nil?
63
+ puts "You need to supply letters to work with as the first argument."
64
+ exit
65
+ end
66
+
67
+ # If all is well, solve :)
68
+ puts Scrabble::Solver.words_for letters, options
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__) + "/scrabble-solver/*.rb"].each do |file|
2
+ require file
3
+ end
@@ -0,0 +1,110 @@
1
+ module Scrabble
2
+ module Solver
3
+ # The name of the file to use as a word dictionary. This file can be any
4
+ # file that contains a list of words, one word per line.
5
+ @word_file_name = File.dirname(__FILE__) + "/../../assets/words.txt"
6
+
7
+ # Set a new word file. Exit the program if the file does not exist.
8
+ #
9
+ # If the file does exist, the cached word list is cleared out and the
10
+ # next time the word list is requested, the new list from the new file
11
+ # is returned and subsequently cached.
12
+ def self.word_file_name= file_name
13
+ if File.exists? file_name
14
+ # Reset the word list
15
+ @word_list = nil
16
+ @word_file_name = file_name
17
+ else
18
+ puts "The file name #{file_name} is not valid."
19
+ exit
20
+ end
21
+ end
22
+
23
+ # Reads in the words.txt file and returns an array containing all of the words
24
+ # in that file.
25
+ #
26
+ # Example:
27
+ #
28
+ # Scrabble::Solver.word_list.each do |word|
29
+ # puts word
30
+ # end
31
+ def self.word_list
32
+ @word_list ||= File.open(@word_file_name) do |file|
33
+ file.readlines.map do |line|
34
+ line.chomp
35
+ end.delete_if do |line|
36
+ line.length < 2
37
+ end
38
+ end
39
+
40
+ @word_list.clone
41
+ end
42
+
43
+ # Gets an array of words that would fit the current board of Scrabble
44
+ # tiles.
45
+ #
46
+ # Example:
47
+ #
48
+ # Scrabble::Solver.words_for "there"
49
+ # # => An array of words that the tiles t, h, e, r, e could make.
50
+ #
51
+ # Scrabble::Solver.words_for "there?"
52
+ # # => An array of words that the tiles t, h, e, r, e plus a blank tile
53
+ # # could make.
54
+ def self.words_for letters, options = Hash.new(nil)
55
+ letters = letters.downcase.split(//)
56
+ unknowns = letters.count "?"
57
+ letters.delete "?"
58
+
59
+ # Set a new word file if the option has been specified
60
+ if options[:word_file]
61
+ self.word_file_name = options[:word_file]
62
+ end
63
+
64
+ words = word_list.keep_if do |word|
65
+ # Split the word into its letters.
66
+ word = word.split(//)
67
+
68
+ # Strip the letters that are in our hand from the word.
69
+ letters.each do |letter|
70
+ unless word.index(letter).nil?
71
+ word.delete_at word.index(letter)
72
+ end
73
+ end
74
+
75
+ # Only return the word if the remaining letters is equal to the number
76
+ # of unknowns.
77
+ word.length == unknowns
78
+ end
79
+
80
+ # Filter only words that start with a specific sequence.
81
+ if options[:starts_with]
82
+ words.keep_if { |word| word.start_with? options[:starts_with] }
83
+ end
84
+
85
+ # Filter words that only end in a certain sequence.
86
+ if options[:ends_with]
87
+ words.keep_if { |word| word.end_with? options[:ends_with] }
88
+ end
89
+
90
+ # Fitler only words shorter than a given amount.
91
+ if options[:shorter_than]
92
+ words.keep_if { |word| word.length < options[:shorter_than].to_i }
93
+ end
94
+
95
+ # Filter words only longer than a given amount.
96
+ if options[:longer_than]
97
+ words.keep_if { |word| word.length > options[:longer_than].to_i }
98
+ end
99
+
100
+ # Filter words that contain a specific sequence at a given 1-based index.
101
+ if options[:contains] and options[:at]
102
+ words.keep_if do |word|
103
+ word[options[:at].to_i - 1, options[:contains].length] == options[:contains]
104
+ end
105
+ end
106
+
107
+ return words
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,181 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ module Scrabble
4
+ describe Solver do
5
+ # Specify the name of the test word file.
6
+ let(:test_word_file) do
7
+ File.dirname(__FILE__) + "/../assets/test_word_list.txt"
8
+ end
9
+
10
+ # Path to the solver executable
11
+ let (:executable) do
12
+ File.dirname(__FILE__) + "/../../bin/scrabble-solver"
13
+ end
14
+
15
+ context "Direct" do
16
+ it "should return a list of words that can be made" do
17
+ words = Solver.words_for "there"
18
+ words.should include "three", "there", "ether", "the", "thee", "tee"
19
+ end
20
+
21
+ it "should be able to use blank tiles with ?" do
22
+ words = Solver.words_for "g?me"
23
+ words.should include "game", "egg", "peg", "gel", "germ", "get", "go", "gum"
24
+ end
25
+
26
+ it "should be able to use a start_with argument successfully" do
27
+ words = Solver.words_for "geg", starts_with: "eg"
28
+ words.should include "egg"
29
+ end
30
+
31
+ it "should be able to use an ends_with argument successfully" do
32
+ words = Solver.words_for "pngi", ends_with: "ing"
33
+ words.should include "ping"
34
+ end
35
+
36
+ it "should be able to use a combination of starts_with and ends_with" do
37
+ words = Solver.words_for "crcak", starts_with: "cr", ends_with: "k"
38
+ words.should include "crack"
39
+ end
40
+
41
+ it "should be able to filter words by length" do
42
+ words = Solver.words_for "diegtlkwj", longer_than: "4"
43
+ words.each do |word|
44
+ word.length.should be > 4
45
+ end
46
+
47
+ # Ensure that some words were actually checked in the above loop.
48
+ words.length.should be > 0, "No words scanned."
49
+ end
50
+
51
+ it "should be able to filter by length, less than" do
52
+ words = Solver.words_for "diegtlkwj", shorter_than: "4"
53
+ words.each do |word|
54
+ word.length.should be < 4
55
+ end
56
+
57
+ # Ensure that some words were actually checked in the above loop.
58
+ words.length.should be > 0, "No words scanned."
59
+ end
60
+
61
+ it "should be able to filter by length both less than and greater than" do
62
+ words = Solver.words_for "diegtlkwj", shorter_than: "6", longer_than: "4"
63
+ words.each do |word|
64
+ word.length.should be > 4 and word.length.should be < 6
65
+ end
66
+
67
+ # Ensure that some words were actually checked in the above loop.
68
+ words.length.should be > 0, "No words scanned."
69
+ end
70
+
71
+ it "should be able to return only words that have a specific letter at " +
72
+ "a given index" do
73
+ words = Solver.words_for "diegti?wj", contains: "i", at: "2"
74
+ words.each do |word|
75
+ word[1].should == "i"
76
+ end
77
+
78
+ # Ensure that some words were actually checked in the above loop.
79
+ words.length.should be > 0, "No words scanned."
80
+ end
81
+
82
+ it "should be able to return only words that have a specific middle part" do
83
+ words = Solver.words_for "diegti?wj", contains: "it", at: "2"
84
+ words.each do |word|
85
+ word[1, 2].should == "it"
86
+ end
87
+
88
+ # Ensure that some words were actually checked in the above loop.
89
+ words.length.should be > 0, "No words scanned."
90
+ end
91
+
92
+ it "should be able to take a new word file if specified" do
93
+ words = Solver.words_for "????", word_file: test_word_file
94
+ words.should be_empty
95
+ end
96
+ end
97
+
98
+ context "Command-line" do
99
+ it "should return a list of words that can be made" do
100
+ words = `#{executable} tehre`.split(/\n/)
101
+ words.should include "three", "there", "ether", "the", "thee", "tee"
102
+ end
103
+
104
+ it "should be able to use blank tiles with ?" do
105
+ words = `#{executable} g?me`.split(/\n/)
106
+ words.should include "game", "egg", "peg", "gel", "germ", "get", "go", "gum"
107
+ end
108
+
109
+ it "should be able to use a start_with argument successfully" do
110
+ words = `#{executable} geg --starts-with eg`.split(/\n/)
111
+ words.should include "egg"
112
+ end
113
+
114
+ it "should be able to use an ends_with argument successfully" do
115
+ words = `#{executable} pngi --ends-with ing`.split(/\n/)
116
+ words.should include "ping"
117
+ end
118
+
119
+ it "should be able to use a combination of starts_with and ends_with" do
120
+ words = `#{executable} crcak --starts-with cr --ends-with k`.split(/\n/)
121
+ words.should include "crack"
122
+ end
123
+
124
+ it "should be able to filter words by length" do
125
+ words = `#{executable} diegtlkwj --longer-than 4`.split(/\n/)
126
+ words.each do |word|
127
+ word.length.should be > 4
128
+ end
129
+
130
+ # Ensure that some words were actually checked in the above loop.
131
+ words.length.should be > 0, "No words scanned."
132
+ end
133
+
134
+ it "should be able to filter by length, less than" do
135
+ words = `#{executable} diegtlkwj --shorter-than 4`.split(/\n/)
136
+ words.each do |word|
137
+ word.length.should be < 4
138
+ end
139
+
140
+ # Ensure that some words were actually checked in the above loop.
141
+ words.length.should be > 0, "No words scanned."
142
+ end
143
+
144
+ it "should be able to filter by length both less than and greater than" do
145
+ words = `#{executable} diegtlkwj --shorter-than 6 --longer-than 4`.split(/\n/)
146
+ words.each do |word|
147
+ word.length.should be > 4 and word.length.should be < 6
148
+ end
149
+
150
+ # Ensure that some words were actually checked in the above loop.
151
+ words.length.should be > 0, "No words scanned."
152
+ end
153
+
154
+ it "should be able to return only words that have a specific letter at " +
155
+ "a given index" do
156
+ words = `#{executable} diegtlkwj --contains i --at 2`.split(/\n/)
157
+ words.each do |word|
158
+ word[1].should == "i"
159
+ end
160
+
161
+ # Ensure that some words were actually checked in the above loop.
162
+ words.length.should be > 0, "No words scanned."
163
+ end
164
+
165
+ it "should be able to return only words that have a specific middle part" do
166
+ words = `#{executable} diegti?wj --contains it --at 2`.split(/\n/)
167
+ words.each do |word|
168
+ word[1, 2].should == "it"
169
+ end
170
+
171
+ # Ensure that some words were actually checked in the above loop.
172
+ words.length.should be > 0, "No words scanned."
173
+ end
174
+
175
+ it "should be able to take a new word file if specified" do
176
+ words = `#{executable} ???? --word-file #{test_word_file}`.split(/\n/)
177
+ words.should be_empty
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/../lib/scrabble-solver"
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scrabble-solver
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sam Rose
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-08 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Want to win at Scrabble while staying inside your comfy command line
15
+ interface? No problem! Scrabble solver lets you do that.
16
+ email: samwho@lbak.co.uk
17
+ executables:
18
+ - scrabble-solver
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - bin/scrabble-solver
23
+ - lib/scrabble-solver.rb
24
+ - lib/scrabble-solver/solver.rb
25
+ - spec/scrabble-solver/solver_spec.rb
26
+ - spec/spec_helper.rb
27
+ - assets/words.txt
28
+ - .gemtest
29
+ - README.md
30
+ - LICENSE
31
+ - Gemfile
32
+ - Guardfile
33
+ homepage: http://github.com/samwho/scrabble-solver
34
+ licenses:
35
+ - GPL-2
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.9.2
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.6
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: A command line Scrabble solving utility.
58
+ test_files: []