Class: SCRABBLR::Scrabbler

Inherits:
Object
  • Object
show all
Defined in:
lib/scrabbler.rb

Overview

Class for solving scrabble puzzles

Constant Summary collapse

MIN_WORD_SIZE =
3
SCRABBLER_DICTIONARIES =
{
 :nl => File.dirname(__FILE__) +"/data/nederlands.dict", 
 :en => File.dirname(__FILE__) +"/data/en-US.dict"
}
SCORES =
YAML::load(File.new(File.dirname(__FILE__) + "/data/scores.yml").read)
MAX_THREADS =
10

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(_dictionary) ⇒ Scrabbler

initializes the Scrabbler with:

  • the array if the given object is of type array

  • the dictonary file specified by the given symbos (:nl, :en)



31
32
33
34
35
36
37
38
39
40
# File 'lib/scrabbler.rb', line 31

def initialize(_dictionary)
  case _dictionary
    when Symbol 
      @dictionary = Vocabulary.new(File.read(SCRABBLER_DICTIONARIES[_dictionary]))
      @dictionary.info = _dictionary
    when Array
      @dictionary = Vocabulary.new(_dictionary)
      @dictionary.info = "based on supplied array"
  end
end

Class Method Details

.def_singletons(*args) ⇒ Object



100
101
102
103
104
105
106
107
108
# File 'lib/scrabbler.rb', line 100

def self.def_singletons(*args)
  args.each do |arg|
    module_eval <<-EOS
      def self.get_#{arg}
        @@instance_#{arg} ||= Scrabbler.new(:#{arg})
      end
    EOS
  end
end

Instance Method Details

#calculate_score(letters, test_word, num_wild = 0, rating = :nl) ⇒ Object

calculate the score of the test_word. Doesn’t return anything if it is impossible to create the word from the given letters



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/scrabbler.rb', line 72

def calculate_score(letters, test_word, num_wild = 0, rating = :nl)
  return if letters.length < test_word.length

  letters = letters.dup # make a copy, we'll use the array desctructively
  score_sum, score_explanation = 0, []

  test_word.scan(/[a-z]/).each do |c|
    if letters.include? c
      letters.remove_first{|v| v == c} # we've used this letter, remove it
      curr_rate = SCORES[rating][c.to_sym];
      score_sum = score_sum + curr_rate
      score_explanation << "#{c} (#{curr_rate})"
    elsif num_wild > 0 # see if we've got any wildcards left
      num_wild = num_wild - 1
      score_explanation << "#{c} (wildcard)"
    else
      return
    end
  end

  # return the result
  Score.new(test_word, score_sum , score_explanation)
end

#inspectObject



96
97
98
# File 'lib/scrabbler.rb', line 96

def inspect
  "scrabbler, dictionary: #{@dictionary.info}"
end

#scrabble(letters, min_score = 10, rating = :nl, wildcard = '$') ⇒ Object

find possible solutions of the given array of letters



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/scrabbler.rb', line 43

def scrabble(letters, min_score = 10, rating = :nl, wildcard = '$')
  raise "Wildcard can not be a word character" if wildcard =~ /[\w]/
  
  letters = letters.scan(/[a-z#{wildcard}]/)
  num_wildcards = letters.select{|c| c == wildcard}.length # count the number of wildcards

  # create a subset based on the word letters
  dictonary_subset = @dictionary.calculate_subset(letters, wildcard, MIN_WORD_SIZE)

  scores, threads = [], []

  dictonary_subset.each_slice(@dictionary.words.length / (MAX_THREADS - 1) + 1) do |slice|
     threads << Thread.new(slice) do |s|
      slice_scores = []
      s.each do |word|
        score = calculate_score(letters, word, num_wildcards, rating)
        slice_scores << score if score && score.score > min_score
      end
      slice_scores
    end    
  end

  threads.each {|t| scores = scores + t.value} # gather values from all threads

  scores.sort
end