"Cheating" at Words With Friends
- Date
It’s time to admin something. I cheat at Words With Friends. I only justify it with the fact that I at least wrote the code to help me cheat.
And cheat really isn’t the word for it. Sometimes I just stare at my letter rack for hours and come up with nothing. I wrote the following script just to get my brain going. You have two options with this script. Argument 1 always has to be your letter rack. You can use the “+” sign for the blank tile. Argument 2 is regular expression if you are looking for something specific.
If you have the tile rack aabbccd
, you would run words-help aabbccd
and get a dump of every combination. If you are
looking for words with ba
at the beginning of the word, you would run words-help aabbccd ^ba
.
This script should work on anything UNIX-like that has a dictionary file at /usr/share/dict/words
. You can edit the
script as you need.
#!/usr/bin/env ruby
WORDS_FILE = '/usr/share/dict/words'
class Engine
def initialize(file)
@file = file
read_dictionary
end
def go(letters, regex = nil)
@letters = letters.split ''
@regex = Regexp.new(regex) unless regex.nil?
@words = []
# Find the words
letter_combinations do |letter_combination|
letter_combination.permutation.to_a.each do |letter_array|
word = letter_array.join('')
if @dictionary.key?(word)
if @regex.nil?
@words << word
else
@words << word if @regex.match(word)
end
end
end
end
# Clean up
@words.sort.uniq
end
private
def letter_combinations
max = (2 ** @letters.count) - 1
1.upto(max) do |i|
word = []
mask = sprintf("%0#{@letters.count}b", i).split('')
mask.each_with_index do |bit, i|
word << @letters[i] if bit == '1'
end
# Substitute wild cards
idx = word.index '+'
if idx.nil?
yield word
else
('a'..'z').each do |l|
word[idx] = l
yield word
end
end
end
end
def read_dictionary
# Build the dictionary
@dictionary = {}
# Read the words
File.open(@file, 'r') do |file|
file.each_line do |line|
line.strip!
# Skip proper nouns
next if line =~ /[A-Z]/
# Skip 1 letter words
next if line.length == 1
# Add the word to the dictionary
@dictionary[line] = 1
end
end
end
end
if ARGV.length < 1 || ARGV.length > 2
$stderr.puts "Letters are required"
exit
end
e = Engine.new WORDS_FILE
e.go(ARGV[0], ARGV[1]).each { |word| puts word }
This script is brute force, so the more letters you set as Argument 1, the longer it will take to run. You’ve been warned.