Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions lib/adagrams.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
require "csv"

def draw_letters
available_letters = {"a" => 9, "b" => 2, "c" => 2, "d" => 4, "e" => 12, "f" => 2, "g" => 3, "h" => 2,
"i" => 9, "j" => 1, "k" => 1, "l" => 4, "m" => 2, "n" => 6, "o" => 8, "p" => 2,
"q" => 1, "r" => 6, "s" => 4, "t" => 6, "u" => 4, "v" => 2, "w" => 2, "x" => 1,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love this data structure - it makes it very clear how many there are of each letter. Could you store it in a constant?

"y" => 2, "z" => 1}
drawn_letters = []
10.times do
letter = available_letters.keys.sample
while available_letters[letter] == 0
letter = available_letters.keys.sample

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By this logic, you're as likely to draw an e as you are a z!

Instead, you might create an array containing all the letters, with code similar to this:

letter_counts = {
  # the hash you had before
}
letters = []
letter_counts.each do |letter, count|
  count.times do
    letters << letter
  end
end

Then you could pull a random tile with letters.sample and get a nice even distribution.

end
drawn_letters.push(letter)
available_letters[letter] = available_letters[letter] - 1
end
return drawn_letters
end

# Method that deletes element in array at its specific index. Prevents deletion of mathcing elements
class Array
def delete_elements_in_(array)
array.each do |x|
if index = index(x)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful about this sort of monkey patching - it's typically considered bad form. Instead, you could write a helper method that's not part of the Array class:

def delete_from(hay, needles)
  needles.each do |needle|
    if index = hay.index(needle)
      hay.delete_at(index)
    end
  end
end

delete_at(index)
end
end
end
end

# Checks if user inputs letters from their letter bank
def uses_available_letters?(input, letters_in_hand)
used_letters = []
input.each_char do |letter|
if letters_in_hand.include?(letter) == true
letters_in_hand.delete_elements_in_([letter])
used_letters << letter

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you only use delete_elements_in_ with an array of length 1, it might make sense to make the argument be a single object instead of an array.

Please make sure you fully understand the code you borrow from other sources before including it in your project.

else
letters_in_hand = (letters_in_hand << used_letters).flatten! # Resets letter bank if previous user input included invalid letters
return false
end
end
letters_in_hand = (letters_in_hand << used_letters).flatten!
return true
end

#Wave 3
def score_word(word)
word = word.downcase
score = 0
word.each_char do |letter|
case letter
when "a", "e", "i", "o", "u", "l", "n", "r", "s", "t"
score += 1
when "d", "g"
score += 2

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this case statement works, it means that the information about which letter has which score is locked into this piece of code, and can't easily be used elsewhere. For example, if you wanted to display the value of each letter in a hand, you would need to repeat this work.

An alternative approach would be to store the letter scores in a hash, something like this:

LETTER_SCORES = {
  "A" => 1
  "B" => 3,
  "C" => 3,
  "D" => 2,
  # ...
}

Then to get the score for a letter, you can say LETTER_SCORES[letter].

when "b", "c", "m", "p"
score += 3
when "f", "h", "v", "w", "y"
score += 4
when "k"
score += 5
when "j", "x"
score += 8
when "q", "z"
score += 10
end
end
if word.length >= 7
score += 8
end
return score
end

#Wave 4
def highest_score_from(words)
winning = {}
high_score = 0
best_word = nil
words.each do |word|
if score_word(word) > high_score
high_score = score_word(word)
best_word = word
elsif score_word(word) == high_score
if best_word.length == 10
next
elsif word.length == 10
best_word = word
elsif word.length < best_word.length
best_word = word
end
end
end
winning[:word] = best_word
winning[:score] = high_score
return winning
end

# Wave 5
def is_in_english_dict?(input)
english_words = CSV.read("assets/dictionary-english.csv")
return english_words.flatten!.include?(input)
end
102 changes: 56 additions & 46 deletions specs/adagrams_spec.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
require 'minitest/autorun'
require 'minitest/reporters'
require 'minitest/skip_dsl'
require "minitest/autorun"
require "minitest/reporters"
require "minitest/skip_dsl"

require_relative '../lib/adagrams'
require_relative "../lib/adagrams"

# Get that nice colorized output
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new

describe 'Adagrams' do
describe 'draw_letters method' do
it 'draws ten letters from the letter pool' do
describe "Adagrams" do
describe "draw_letters method" do
it "draws ten letters from the letter pool" do
drawn_letters = draw_letters
expect(drawn_letters.size).must_equal 10
end

it 'returns an array, and each item is a single-letter string' do
it "returns an array, and each item is a single-letter string" do
drawn_letters = draw_letters
expect(drawn_letters.size).must_equal 10

Expand All @@ -26,137 +26,135 @@
end
end

describe 'uses_available_letters? method' do

it 'returns true if the submitted letters are valid against the drawn letters' do
drawn_letters = ['D', 'O', 'G', 'X', 'X', 'X', 'X', 'X', 'X', 'X']
test_word = 'DOG'
describe "uses_available_letters? method" do
it "returns true if the submitted letters are valid against the drawn letters" do
drawn_letters = ["D", "O", "G", "X", "X", "X", "X", "X", "X", "X"]
test_word = "DOG"

is_valid = uses_available_letters? test_word, drawn_letters

expect(is_valid).must_equal true
end

it 'returns false word contains letters not in the drawn letters' do
drawn_letters = ['D', 'O', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X']
test_word = 'DOG'
it "returns false word contains letters not in the drawn letters" do
drawn_letters = ["D", "O", "X", "X", "X", "X", "X", "X", "X", "X"]
test_word = "DOG"

is_valid = uses_available_letters? test_word, drawn_letters

expect(is_valid).must_equal false
end

it 'returns false word contains repeated letters more than in the drawn letters' do
drawn_letters = ['A', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X']
test_word = 'AAA'
it "returns false word contains repeated letters more than in the drawn letters" do
drawn_letters = ["A", "X", "X", "X", "X", "X", "X", "X", "X", "X"]
test_word = "AAA"

is_valid = uses_available_letters? test_word, drawn_letters

expect(is_valid).must_equal false
end

end

describe 'score_word method' do
it 'returns an accurate numerical score according to the score chart' do
describe "score_word method" do
it "returns an accurate numerical score according to the score chart" do
expect(score_word("A")).must_equal 1
expect(score_word("DOG")).must_equal 5
expect(score_word("WHIMSY")).must_equal 17
end

it 'returns a score regardless of input case' do
it "returns a score regardless of input case" do
expect(score_word("a")).must_equal 1
expect(score_word("dog")).must_equal 5
expect(score_word("wHiMsY")).must_equal 17
end

it 'returns a score of 0 if given an empty input' do
it "returns a score of 0 if given an empty input" do
expect(score_word("")).must_equal 0
end

it 'adds an extra 8 points if the word is 7 or more characters long' do
it "adds an extra 8 points if the word is 7 or more characters long" do
expect(score_word("XXXXXXX")).must_equal 64
expect(score_word("XXXXXXXX")).must_equal 72
expect(score_word("XXXXXXXXX")).must_equal 80
end
end

describe 'highest_score_from method' do
it 'returns a hash that contains the word and score of best word in an array' do
words = ['X', 'XX', 'XXX', 'XXXX']
describe "highest_score_from method" do
it "returns a hash that contains the word and score of best word in an array" do
words = ["X", "XX", "XXX", "XXXX"]
best_word = highest_score_from words

expect(best_word[:word]).must_equal 'XXXX'
expect(best_word[:word]).must_equal "XXXX"
expect(best_word[:score]).must_equal 32
end

it 'accurately finds best scoring word even if not sorted' do
words = ['XXX', 'XXXX', 'XX', 'X']
it "accurately finds best scoring word even if not sorted" do
words = ["XXX", "XXXX", "XX", "X"]
best_word = highest_score_from words

expect(best_word[:word]).must_equal 'XXXX'
expect(best_word[:word]).must_equal "XXXX"
expect(best_word[:score]).must_equal 32
end

it 'in case of tied score, prefers the word with fewer letters' do
it "in case of tied score, prefers the word with fewer letters" do
# the character 'M' is worth 3 points, 'W' is 4 points
words = ['MMMM', 'WWW']
words = ["MMMM", "WWW"]

# verify both have a score of 12
expect(score_word(words.first)).must_equal 12
expect(score_word(words.last)).must_equal 12

best_word = highest_score_from words

expect(best_word[:word]).must_equal 'WWW'
expect(best_word[:word]).must_equal "WWW"
expect(best_word[:score]).must_equal 12
end

it 'in case of tied score, prefers the word with fewer letters regardless of order' do
it "in case of tied score, prefers the word with fewer letters regardless of order" do
# the character 'M' is worth 3 points, 'W' is 4 points
words = ['WWW', 'MMMM']
words = ["WWW", "MMMM"]

# verify both have a score of 12
expect(score_word(words.first)).must_equal 12
expect(score_word(words.last)).must_equal 12

best_word = highest_score_from words

expect(best_word[:word]).must_equal 'WWW'
expect(best_word[:word]).must_equal "WWW"
expect(best_word[:score]).must_equal 12
end

it 'in case of tied score, prefers most the word with 10 letters' do
it "in case of tied score, prefers most the word with 10 letters" do
# the character 'A' is worth 1 point, 'B' is 3 points
words = ['AAAAAAAAAA', 'BBBBBB']
words = ["AAAAAAAAAA", "BBBBBB"]

# verify both have a score of 10
expect(score_word(words.first)).must_equal 18
expect(score_word(words.last)).must_equal 18

best_word = highest_score_from words

expect(best_word[:word]).must_equal 'AAAAAAAAAA'
expect(best_word[:word]).must_equal "AAAAAAAAAA"
expect(best_word[:score]).must_equal 18
end

it 'in case of tied score, prefers most the word with 10 letters regardless of order' do
it "in case of tied score, prefers most the word with 10 letters regardless of order" do
# the character 'A' is worth 1 point, 'B' is 3 points
words = ['BBBBBB', 'AAAAAAAAAA']
words = ["BBBBBB", "AAAAAAAAAA"]

# verify both have a score of 10
expect(score_word(words.first)).must_equal 18
expect(score_word(words.last)).must_equal 18

best_word = highest_score_from words

expect(best_word[:word]).must_equal 'AAAAAAAAAA'
expect(best_word[:word]).must_equal "AAAAAAAAAA"
expect(best_word[:score]).must_equal 18
end

it 'in case of tied score and same length words, prefers the first word' do
it "in case of tied score and same length words, prefers the first word" do
# the character 'A' is worth 1 point, 'E' is 1 point
words = ['AAAAAAAAAA', 'EEEEEEEEEE']
words = ["AAAAAAAAAA", "EEEEEEEEEE"]

# verify both have a score of 10
expect(score_word(words.first)).must_equal 18
Expand All @@ -168,4 +166,16 @@
expect(best_word[:score]).must_equal 18
end
end

describe "is in dictionary" do
it "returns true for word in English dictionary" do
word = "cat"
expect(is_in_english_dict?(word)).must_equal true
end

it "returns false if word not in English dictionary" do
word = "qqq"
expect(is_in_english_dict?(word)).must_equal false
end
end
end