Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scrabble - Jackie & Leti (Ampers) #8

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
27059d0
Added scoring method and fixed typo on the 'game' files - First score…
LetiTran Feb 20, 2018
ad5a0be
adds 50 pts to 7 letter word, returns nil for bad characters
Jackiesan Feb 20, 2018
24022ff
..
LetiTran Feb 20, 2018
71bdb93
returns nil for words > 7 letters
LetiTran Feb 20, 2018
1b70781
returns nil for empty words
Jackiesan Feb 20, 2018
f89b00a
added code for 'returns nil if no words were passed' - OK
LetiTran Feb 20, 2018
e112d21
returns the only word in a length-1 array
Jackiesan Feb 20, 2018
78c157b
returns the highest word if there are two words - OK
LetiTran Feb 20, 2018
68481b7
if tied, prefer 7 letter word
Jackiesan Feb 20, 2018
27b4827
if tied and no word has 7 letters, prefers the word with fewer letters
LetiTran Feb 21, 2018
5d1ddef
returns the first word of a tie with same letter count
Jackiesan Feb 21, 2018
f7dab82
..
LetiTran Feb 21, 2018
5d4ef25
Merge branch 'master' of https://github.com/LeticiaTran/Scrabble
LetiTran Feb 21, 2018
f3b149f
Initializes Player class and passes initialize test
Jackiesan Feb 21, 2018
0f2a8d1
Merge branch 'master' of https://github.com/LeticiaTran/Scrabble
LetiTran Feb 21, 2018
fa5f61a
'Added a method 'plays' and passed the test 'Returns an array of word…
LetiTran Feb 21, 2018
0a34fec
Adds input word to @plays array.
Jackiesan Feb 21, 2018
8263c3f
added total_score method and passed test Returns the sum of scores of…
LetiTran Feb 21, 2018
6a082a8
won? returns true if player has over 100 pts
Jackiesan Feb 21, 2018
27db390
mmm needed this commit/dont understand why
LetiTran Feb 21, 2018
ecc16a8
added method highest_scoring_word and passed test.
LetiTran Feb 22, 2018
bf0bc47
fixed indentention
LetiTran Feb 22, 2018
4abaeba
returns score of highest scoring word
Jackiesan Feb 22, 2018
b143409
Created TileBag class and files, initialized the TileBag class and pa…
LetiTran Feb 22, 2018
390cabe
draws tiles from the tile bag
Jackiesan Feb 22, 2018
e248d2c
removed wrong puts that was used for testing
LetiTran Feb 22, 2018
25389fc
Merge branch 'master' of https://github.com/LeticiaTran/Scrabble
LetiTran Feb 22, 2018
e745716
Fixed a typo on tile_bag.rb and passed test Removes tiles from the de…
LetiTran Feb 23, 2018
e320ff2
Draw_tiles(tile_bag) not passing test
Jackiesan Feb 23, 2018
c4c3900
Draws tiles with max of 7
Jackiesan Feb 23, 2018
cc8d12e
just running tests....
LetiTran Feb 23, 2018
0c54c22
just running tests....
LetiTran Feb 23, 2018
4eb126f
fills tile array until it reaches 7
Jackiesan Feb 23, 2018
7280a67
Merge branch 'master' of https://github.com/LeticiaTran/Scrabble
Jackiesan Feb 23, 2018
0fe3c6c
fills tile array until it reaches 7
Jackiesan Feb 23, 2018
64fec21
git error...
LetiTran Feb 23, 2018
495dbc0
git error...
LetiTran Feb 23, 2018
4e25418
Added descriptive comments on class files.
LetiTran Feb 23, 2018
cd4c9f4
changed how TileBag is initialized (not taking any arguments now), an…
LetiTran Feb 23, 2018
3965a05
Changed the highest_scoring_word(player.rb) to call the highest_score…
LetiTran Feb 23, 2018
c21f74d
Deleted unecessary testing comments.
LetiTran Feb 23, 2018
be94de4
Refactor some iterations/conditional blocks into one line.
LetiTran Feb 23, 2018
3ab27bf
Removed unnecessary scoring table hash variable from player.rb, added…
Jackiesan Feb 23, 2018
eac411c
Changed method score in score.rb to calculate points using 'case' ins…
LetiTran Feb 23, 2018
2678435
Merge branch 'master' of https://github.com/LeticiaTran/Scrabble
LetiTran Feb 23, 2018
deeb039
Merge after changing score method to use case instead of if to evalua…
LetiTran Feb 23, 2018
897a2c8
Changed how the highest_score_from is called on player.rb and deleted…
LetiTran Feb 25, 2018
0dfb93e
Refactor the project to atend to the necessary changes on the first 4…
LetiTran Feb 26, 2018
2405cff
Added edge cases to scoring_spec and tile_bag
Jackiesan Feb 27, 2018
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
67 changes: 67 additions & 0 deletions lib/player.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#***********************************************************
#Class Player in Scrabble Module:

require_relative 'scoring'

module Scrabble
class Player

attr_reader :name, :plays

def initialize(name)
@name = name
@plays = []
@tiles= []
end

# _________________DRAW TILES METHOD____________________
# Randomly chooses as many possible words (max 7) that the player can still draw from the tiles bag by calling the draw_tiles method from the TileBag class.

def draw_tiles(tile_bag)
@tiles = tile_bag.draw_tiles(7 - @tiles.count)
return @tiles
end


# _________________PLAY METHOD____________________
# Accepts a word as an argument and add it to the plays array.

def play(word)

Choose a reason for hiding this comment

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

Read the description again. This method is required to return false if they have already won prior to playing the word.

@plays << word
end

# _________________TOTAL SCORE METHOD____________________
# Calculates and returns the toal score of the words in the plays array by calling the score method of the class Scoring.

def total_score
sum_of_scores = 0
@plays.each do |word|
score = Scoring.score(word)

sum_of_scores += score
end
return sum_of_scores
end

# _________________WON? METHOD____________________
# Returns true if the toal score is more than 100, otherwise returns false.

def won?
total_score > 100 ? true : false

Choose a reason for hiding this comment

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

Good ternary

end

# ___________HIGHEST SCORING WORD METHOD________________
# Returns the highest scoring played word.

def highest_scoring_word
return Scoring.highest_score_from(@plays)
end

# ______________HIGHEST WORD SCORE METHOD__________________
# Returns the score of the highest scored word.

def highest_word_score
return Scoring.score(highest_scoring_word)
end
end
end
92 changes: 89 additions & 3 deletions lib/scoring.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,95 @@
###########################################################
#Class Scoring in Scrabble Module:

module Scrabble
class Scoring

# _________________SCORE METHOD____________________
# Calculates points scored with the given word:

def self.score(word)

# If word contains a character that is not a letter, returns nil:
if word.match?(/\W/)
return nil
end

# If word is has more than 7 characters, returns nil:
if word.length > 7
return nil
end

# Creates a new array with each letter of the word as a diferent element:
letters_array = word.upcase.split("")

# If array with letters is empty, returns nil:
if letters_array.empty?
return nil
end


# Set the points for that word to initialize at zero and calculate the points of each word, adding them to the points variable:
points = 0

letters_array.each do |letter|

Choose a reason for hiding this comment

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

This works, but is there a more compact way to do this, maybe with a hash?

case letter
when "Q", "Z"
points += 10
when "D", "G"
points += 2
when "B", "C", "M", "P"
points += 3
when "F", "H", "V", "W", "Y"
points += 4
when "K"
points += 5
when "J", "X"
points += 8
else #"A", "E", "I", "O", "U", "L", "N", "R", "S", "T"
points += 1
end
end

# If word has 7 letters add 50 more points:
letters_array.length == 7 ? points += 50 : points += 0

return points
end

def self.highest_score_from(array_of_words)

# ________________HIGHEST SCORE METHOD___________________
# Returns the word with highest score, from a given array of words:

def self.highest_score_from(array_of_words = [])
# Evaluate given words to find the one with highest score, according to the game rules. If there is a tie, it will populate a new array with the words in the tie and compare them to find the winner.

scoring_table = {}

# Set the scores of each word:
array_of_words.each {|word| scoring_table["#{word}"] = score(word)}

Choose a reason for hiding this comment

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

You don't need the interpolation here:

array_of_words.each {|word| scoring_table[word] = score(word)}

will work


# Find the words with the maximu score between them:
max = scoring_table.values.max
max_scored_words_hash = Hash[scoring_table.select { |k, v| v == max}]

# Select only the keys from the has with the highest scored words:
winning_words = max_scored_words_hash.keys

# Choose and return the winning word according to the rules and solving a tie if any:
if array_of_words == []
return nil
elsif winning_words.length == 1
winner = winning_words[0]
return winner
elsif winning_words.max_by(&:length).length == 7
winner = winning_words.max_by(&:length)
return winner
else
winner = winning_words.min_by {|word| word.length}
return winner
end
end
end
end


end #Scoring
end #Scrabble
46 changes: 46 additions & 0 deletions lib/tile_bag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#***********************************************************
# Class TileBag in Scrabble Module:

module Scrabble
class TileBag

attr_reader :bag

def initialize
@bag = {

Choose a reason for hiding this comment

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

There's a number of problems with this data structure. Most pressing is that it gives each letter an equal chance of being drawn. This is despite the fact that there are a lot more "E" tiles than "Z".

"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, "Y" => 2, "Z" => 1
}
end

# _________________DRAW TILES METHOD____________________
# Accepts a number of tiles that needs to be drawn for the player and creates a loop that will:
# - take a random letter from the bag
# - add this letter to the array of tiles
# - subtract the quantity of that letter available in the tile bag
# - delete the key of this letter from the tile bag if there are none available anymore.

def draw_tiles(num)
tiles_drawn = [ ]

num.times do
new_letter = @bag.keys.sample
tiles_drawn << new_letter
@bag[new_letter] -= 1
@bag.delete_if { |letter, quantity| quantity == 0 }

Choose a reason for hiding this comment

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

Good that you're keeping the number of tiles from going negative.

end

return tiles_drawn
end

# _______________TILES REMAINING METHOD_________________
# Returns the total quantity of tiles still available in the tile bag.

def tiles_remaining
return @bag.values.sum
end

end
end
125 changes: 125 additions & 0 deletions specs/player_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
require 'minitest/autorun'
require 'minitest/reporters'
require 'minitest/skip_dsl'

require_relative '../lib/player'
require_relative '../lib/tile_bag'

Minitest::Reporters.use!
Minitest::Reporters::SpecReporter.new

describe 'Player' do
describe "#initialize" do
it "Takes a Player name" do
player_1 = Scrabble::Player.new("Patrick")
player_1.must_be_instance_of Scrabble::Player

player_1.must_respond_to :name
player_1.name.must_equal "Patrick"
player_1.name.must_be_kind_of String

end
describe "#plays" do
it "Returns an array of words played:" do
player_1 = Scrabble::Player.new("Patrick")

player_1.plays.must_be_kind_of Array

end
end

describe "#play(word)" do

Choose a reason for hiding this comment

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

What about:

  1. Playing more than 1 word.
  2. Playing an invalid word
  3. Playing words after the player has already won!

it "Adds input word to @plays array." do
player_1 = Scrabble::Player.new("Patrick")
player_1.play("orange")

player_1.plays.must_include "orange"

end
end
end

describe '#total_score' do
it " Returns the sum of scores of played words" do
player_1 = Scrabble::Player.new("Patrick")
player_1.play("apples")
player_1.play("fuzzy")

player_1.total_score.must_equal 39
end
end

describe '#won?' do
it "Returns true if player has over 100 points" do
player_1 = Scrabble::Player.new("Patrick")
player_1.play("xxxxxxx")
player_1.won?.must_equal true

end

it "Returns false if player has over 100 points" do

Choose a reason for hiding this comment

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

won? should return true if the player has >= 100 points. It's play that should return false

player_1 = Scrabble::Player.new("Patrick")
player_1.play("apples")
player_1.play("fuzzy")
player_1.won?.must_equal false

end
end

describe '#highest_scoring_word' do
it 'Returns the highest scoring played word' do
player_1 = Scrabble::Player.new("Patrick")
player_1.play("cat")
player_1.play("pig")

player_1.highest_scoring_word.must_equal "pig"
player_1.highest_scoring_word.must_be_kind_of String

end


it 'Returns the first word if there is a tie between hightest scoring words"' do
player_1 = Scrabble::Player.new("Patrick")
player_1.play("ba")
player_1.play("dd")

player_1.highest_scoring_word.must_equal "ba"

end

end

describe '#highest_word_score' do
it 'Returns the score of the highest scoring word' do
player_1 = Scrabble::Player.new("Patrick")
player_1.play("cat")
player_1.play("pig")

player_1.highest_word_score.must_equal 6

end
end

describe 'draw_tiles(tile_bag)' do
it 'is a collection of letters that the player can play (max 7)' do
player_1 = Scrabble::Player.new("Patrick")
tile_bag = Scrabble::TileBag.new

players_tiles = player_1.draw_tiles(tile_bag)

players_tiles.must_be_kind_of Array
all_letters = players_tiles.all? { |word| word.class == String }
all_letters.must_equal true
end

end

it 'fills tiles array until it has 7 letters from the given tile bag' do
player_1 = Scrabble::Player.new("Patrick")
tile_bag = Scrabble::TileBag.new

players_tiles = player_1.draw_tiles(tile_bag)
players_tiles.length.must_equal 7

end
end
16 changes: 16 additions & 0 deletions specs/scoring_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,37 @@

describe 'highest_score_from' do
it 'returns nil if no words were passed' do
Scrabble::Scoring.highest_score_from().must_be_nil
end

it 'returns the only word in a length-1 array' do
winning_word = Scrabble::Scoring.highest_score_from(["hotdog"])
winning_word.must_equal "hotdog"
winning_word.must_be_kind_of String

end

it 'returns the highest word if there are two words' do
winning_words = Scrabble::Scoring.highest_score_from(["dog", "academy"])

Choose a reason for hiding this comment

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

Since order can potentially be important in the highest_score_from method repeating these tests by varying the order is useful.

Something like:

      winning_words = Scrabble::Scoring.highest_score_from(["academy", "dog"])
      winning_words.must_equal "academy"

winning_words.must_equal "academy"
end

it 'if tied, prefer a word with 7 letters' do
winning_words = Scrabble::Scoring.highest_score_from(["qqqqqj", "aaaaaad"])
winning_words.must_equal "aaaaaad"

end

it 'if tied and no word has 7 letters, prefers the word with fewer letters' do
winning_words = Scrabble::Scoring.highest_score_from(["qq", "kkkk"])
winning_words.must_equal "qq"
end

it 'returns the first word of a tie with same letter count' do
winning_words = Scrabble::Scoring.highest_score_from(["dd", "ba"])
winning_words.must_equal "dd"


end
end
end
Loading