ðŸŽ‰ Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io ðŸŽ‰

# DanOlson's solution

## to Scrabble Score in the Ruby Track

Published at Apr 01 2020 · 0 comments
Instructions
Test suite
Solution

Given a word, compute the scrabble score for that word.

## Letter Values

You'll need these:

``````Letter                           Value
A, E, I, O, U, L, N, R, S, T       1
D, G                               2
B, C, M, P                         3
F, H, V, W, Y                      4
K                                  5
J, X                               8
Q, Z                               10
``````

## Examples

"cabbage" should be scored as worth 14 points:

• 3 points for C
• 1 point for A, twice
• 3 points for B, twice
• 2 points for G
• 1 point for E

And to total:

• `3 + 2*1 + 2*3 + 2 + 1`
• = `3 + 2 + 6 + 3`
• = `5 + 9`
• = 14

## Extensions

• You can play a double or a triple letter.
• You can play a double or a triple word.

For installation and learning resources, refer to the Ruby resources page.

For running the tests provided, you will need the Minitest gem. Open a terminal window and run the following command to install minitest:

``````gem install minitest
``````

If you would like color output, you can `require 'minitest/pride'` in the test file, or note the alternative instruction, below, for running the test file.

Run the tests from the exercise directory using the following command:

``````ruby scrabble_score_test.rb
``````

To include color from the command line:

``````ruby -r minitest/pride scrabble_score_test.rb
``````

## Source

Inspired by the Extreme Startup game https://github.com/rchatley/extreme_startup

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### scrabble_score_test.rb

``````require 'minitest/autorun'
require_relative 'scrabble_score'

class ScrabbleTest < Minitest::Test
def test_empty_word_scores_zero
assert_equal 0, Scrabble.new('').score
end

def test_whitespace_scores_zero
skip
assert_equal 0, Scrabble.new(" \t\n").score
end

def test_nil_scores_zero
skip
assert_equal 0, Scrabble.new(nil).score
end

def test_scores_very_short_word
skip
assert_equal 1, Scrabble.new('a').score
end

def test_scores_other_very_short_word
skip
assert_equal 4, Scrabble.new('f').score
end

def test_simple_word_scores_the_number_of_letters
skip
assert_equal 6, Scrabble.new('street').score
end

def test_complicated_word_scores_more
skip
assert_equal 22, Scrabble.new('quirky').score
end

def test_scores_are_case_insensitive
skip
assert_equal 41, Scrabble.new('OXYPHENBUTAZONE').score
end

def test_convenient_scoring
skip
assert_equal 13, Scrabble.score('alacrity')
end
end``````
``````class Scrabble
class Letter

def initialize(character, value)
@character = character
@value = value
@multiplier = 1
end

def double
@multiplier = 2
end

def triple
@multiplier = 3
end

def multiplied?
@multiplier > 1
end

def score
value * @multiplier
end
end

class Score
class Configuration
def initialize
@scores_by_letter = {}
end

def apply(value:, to:)
Array(to).each do |letter|
@scores_by_letter[letter.downcase] = value
end
end

def value_of(letter)
@scores_by_letter.fetch(letter.downcase, 0)
end
end

class << self

def for(letters)
new(letters)
end

def configure
config = Configuration.new
yield config
@configuration = config
end
end

def initialize(letters, multiplier: 1)
@letters = init_letters(letters)
@multiplier = multiplier
end

def to_s
"#<Scrabble::Score:#{to_int} @letters=#{@letters}, @multiplier=#{@multiplier}>"
end

def inspect
to_s
end

def ==(other)
Integer(other) == Integer(self)
end

def to_int
@letters.sum(&:score) * @multiplier
end

def double_letter(*doubled_letters)
letters_matching(doubled_letters).each &:double
self
end

def triple_letter(*tripled_letters)
letters_matching(tripled_letters).each &:triple
self
end

def double_word
@multiplier = 2
self
end

def triple_word
@multiplier = 3
self
end

private

def init_letters(characters)
characters.map do |char|
value = self.class.configuration.value_of(char)
Letter.new(char, value)
end
end

def letters_matching(multiplier_candidates)
Enumerator.new do |y|
multiplier_candidates.flat_map { |l| l.split('') }.each do |l|
letter = find_multiplier_candidate(l) and y << letter
end
end
end

def find_multiplier_candidate(character)
@letters.find do |letter|
letter.character == character && !letter.multiplied?
end
end
end

Score.configure do |config|
config.apply value: 1, to: %w(A E I O U L N R S T)
config.apply value: 2, to: %w(D G)
config.apply value: 3, to: %w(B C M P)
config.apply value: 4, to: %w(F H V W Y)
config.apply value: 5, to: 'K'
config.apply value: 8, to: %w(J X)
config.apply value: 10, to: %w(Q Z)
end

class << self
def score(word)
new(word).score
end
end

def initialize(word)
@word = word
end

def score
Score.for(letters)
end

private

def letters
String(@word).scan(/[a-zA-Z]/)
end
end

if defined?(Minitest)
def test_double_letter_score
assert_equal 13, Scrabble.new('barf').score.double_letter('f')
end

def test_double_letter_score_banana_with_b_and_one_a
assert_equal 12, Scrabble.new('banana').score.double_letter('b', 'a')
end

def test_double_letter_score_banana_with_b_and_two_as
assert_equal 13, Scrabble.new('banana').score.double_letter('b', 'a', 'a')
end

def test_multiple_double_letter_scores
assert_equal 16, Scrabble.new('barf').score.double_letter('bf')
end

def test_multiple_double_letter_scores_varargs
assert_equal 16, Scrabble.new('barf').score.double_letter('f', 'b')
end

def test_repeated_calls_to_double_letter_score
assert_equal 16, Scrabble.new('barf').score.double_letter('f').double_letter('b')
end

def test_double_letter_score_ignores_letter_not_in_word
assert_equal 9, Scrabble.new('barf').score.double_letter('z')
end

def test_double_letter_score_ignores_invalid_input
assert_equal 9, Scrabble.new('barf').score.double_letter('pico')
end

def test_double_letter_score_ignores_invalid_portion_of_input
assert_equal 13, Scrabble.new('barf').score.double_letter('foo')
end

def test_double_letter_score_ignores_invalid_portion_of_input_varargs
assert_equal 13, Scrabble.new('barf').score.double_letter('f', 'o', 'o')
end

def test_triple_letter_score
assert_equal 15, Scrabble.new('barf').score.triple_letter('b')
end

def test_multiple_triple_letter_scores
assert_equal 17, Scrabble.new('barf').score.triple_letter('b', 'a')
end

def test_repeated_calls_to_triple_letter_score
assert_equal 17, Scrabble.new('barf').score.triple_letter('b').triple_letter('a')
end

def test_triple_letter_score_ignores_letter_not_in_word
assert_equal 9, Scrabble.new('barf').score.triple_letter('z')
end

def test_triple_letter_score_ignores_invalid_input
assert_equal 9, Scrabble.new('barf').score.triple_letter('pico')
end

def test_triple_letter_score_ignores_invalid_portion_of_input
assert_equal 17, Scrabble.new('barf').score.triple_letter('foo')
end

def test_triple_letter_score_ignores_invalid_portion_of_input_varargs
assert_equal 17, Scrabble.new('barf').score.triple_letter('f', 'o', 'o')
end

def test_double_and_triple_letter_scores
assert_equal 20, Scrabble.new('barf').score.double_letter('b').triple_letter('f')
end

def test_double_word_score_with_simple_word
assert_equal 12, Scrabble.new('street').score.double_word
end

def test_double_word_score_with_double_letter_score
assert_equal 64, Scrabble.new('quiz').score.double_word.double_letter('z')
end

def test_double_word_score_with_triple_letter_score
assert_equal 48, Scrabble.new('quiz').score.double_word.triple_letter('u')
end

def test_double_word_score_with_double_and_triple_letter_scores
assert_equal 68, Scrabble.new('quiz').score.double_word.double_letter('z').triple_letter('u')
end

def test_double_and_triple_letter_scores_with_double_word_score
assert_equal 68, Scrabble.new('quiz').score.double_letter('z').triple_letter('u').double_word
end

def test_triple_word_score_with_simple_word
assert_equal 18, Scrabble.new('street').score.triple_word
end

def test_triple_word_score_with_double_letter_score
assert_equal 105, Scrabble.new('pizza').score.triple_word.double_letter('z')
end

def test_triple_word_score_with_triple_letter_score
assert_equal 135, Scrabble.new('pizza').score.triple_word.triple_letter('z')
end

def test_triple_word_score_with_double_and_triple_letter_scores
assert_equal 144, Scrabble.new('pizza').score.triple_word.double_letter('p').triple_letter('z')
end

def test_double_and_triple_letter_scores_with_triple_word_score
assert_equal 144, Scrabble.new('pizza').score.double_letter('p').triple_letter('z').triple_word
end
end
end``````

### What can you learn from this solution?

A huge amount can be learned from reading other peopleâ€™s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

• What compromises have been made?