diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..358454d5 --- /dev/null +++ b/Rakefile @@ -0,0 +1,9 @@ +require 'rake/testtask' + +Rake::TestTask.new do |t| + t.libs = ["lib"] + t.warning = true + t.test_files = FileList['specs/*-spec.rb'] +end + +task default: :test diff --git a/lib/board.rb b/lib/board.rb new file mode 100644 index 00000000..80894ab0 --- /dev/null +++ b/lib/board.rb @@ -0,0 +1,61 @@ +class Board + attr_reader :board + + def initialize + @board = make_board + end + + def make_board + a = [] + 15.times do # fills array with 15 new arrays + a << Array.new(15) # each array has 15 elements, creating a 15 x 15 grid + end + + return a + end + + def can_be_played(row, col, direction, word) + can_play = true + letter_array = word.split(//) + + letter_array.each do |letter| + # if there's something in the specific index on the board that does not match the current letter + if @board[row][col] != nil && @board[row][col] != letter + can_play = false + end + + # increment either the row or column (depending on which direction specified by player) + row, col = increment_row_or_col(row, col, direction) + end + + can_play + end + + def play_word(row, col, direction, word) + # do not play the word if a conflicting word is already on the board + return "can't play there" if can_be_played(row, col, direction, word) == false + + letter_array = word.split(//) + + i = 0 + while i < letter_array.length + @board[row][col] = letter_array[i] + i += 1 + + row, col = increment_row_or_col(row, col, direction) + end + end + + def increment_row_or_col(row, col, direction) + if direction.downcase.include? 'right' + col += 1 + else + row += 1 + end + + return [row, col] + end + + + +end \ No newline at end of file diff --git a/lib/player.rb b/lib/player.rb new file mode 100644 index 00000000..9cce2a4f --- /dev/null +++ b/lib/player.rb @@ -0,0 +1,44 @@ +require_relative './tilebag' +require_relative './scoring' + +class Player + attr_reader :name, :plays ,:total_score, :tiles + + def initialize(name) + @name = name + @plays = [] + @total_score = 0 + @already_won = false + @tiles = [] + end + + def play(word) + return false if @already_won == true # can't play more words if already won + @plays << word # adds word to list of words played + score = Scoring.score(word) + @total_score += score + won? # check to see if the game is won before giving back score + score + end + + def won? + return false unless total_score > 100 + puts "You win!" + @already_won = true # changes to true when score > 100 + end + + def highest_scoring_word + Scoring.highest_score_from(@plays) + end + + def highest_word_score + highest_word = Scoring.highest_score_from(@plays) + Scoring.score(highest_word) + end + + def draw_tiles(tile_bag) + @tiles.push *tile_bag.draw_tiles(7 - @tiles.length) + @tiles + + end +end diff --git a/lib/scoring.rb b/lib/scoring.rb new file mode 100644 index 00000000..6a1568d6 --- /dev/null +++ b/lib/scoring.rb @@ -0,0 +1,60 @@ +class Scoring + + LETTER_SCORES = { + 1 => ['A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T'], + 2 => ['D', 'G'], + 3 => ['B', 'C', 'M', 'P'], + 4 => ['F', 'H', 'V', 'W', 'Y'], + 5 => ['K'], + 8 => ['J', 'X'], + 10 => ['Q', 'Z'] + } + + def self.score(word) + word_score = 0 + word = word.upcase + letter_array = word.split(//) + + letter_array.each do |letter| + LETTER_SCORES.each do |key, value| + if value.include? letter + word_score += key + end + end + end + + word_score += 50 if letter_array.length == 7 + + word_score + end + + def self.highest_score_from(array_of_words) + word_hash = {} + # create a hash where the key is the word and the value is its score + array_of_words.each do |word| + word_hash[word] = score(word) + end + + word_list = [] + # find the highest score value + max_value = word_hash.max_by { |key, value| value } + + # put all the words with the highest score into an array + word_hash.each do |key, value| + if value == max_value[1] + word_list << key + end + end + + # if the word used all 7 letters, it wins + seven_long = word_list.find {|word| word.length == 7} + + return seven_long unless seven_long.nil? + + # if no words used 7 letters, the shortest one wins + # or if multiple are shortest, the first one in the array wins + word_list.min_by {|words| words.length} + + end + +end diff --git a/lib/tilebag.rb b/lib/tilebag.rb new file mode 100644 index 00000000..c695f8e1 --- /dev/null +++ b/lib/tilebag.rb @@ -0,0 +1,56 @@ +class TileBag + TILE_HASH = { + "A" => 9, + "B" => 2, + "C" => 2, + "D" => 4, + "E" => 12, + "F" => 2, + "G" => 2, + "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 + + } + + def initialize + @tile_bag = fill_tile_bag + end + + def fill_tile_bag + tile_array = [] + + TILE_HASH.each do |letter, number| + a = letter * number + a = a.split(//) + tile_array.push *a + end + tile_array + end + + def draw_tiles(num) + @tile_bag.shuffle! + @tile_bag.shift(num) + end + + def tiles_remaining + @tile_bag.length + end + +end diff --git a/scrabble.rb b/scrabble.rb new file mode 100644 index 00000000..30bcfab7 --- /dev/null +++ b/scrabble.rb @@ -0,0 +1,14 @@ +module Scrabble + include Scoring + + include Player + + include TileBag + + include Board +end + +require_relative './lib/scoring' +require_relative './lib/player' +require_relative './lib/tilebag' +require_relative './lib/board' \ No newline at end of file diff --git a/specs/board-spec.rb b/specs/board-spec.rb new file mode 100644 index 00000000..1071d9a9 --- /dev/null +++ b/specs/board-spec.rb @@ -0,0 +1,63 @@ +require_relative '../lib/board' +require_relative './spec_helper' + +describe Board do + before do + @board = Board.new + end + + it "is an object we have access to" do + @board.wont_be_nil + end + + + it "should be an instance of Array" do + @board.board.must_be_instance_of Array + end + + it "should return an array with a length of 15" do + @board.board.length.must_equal 15 + end + + describe "letter in indexes" do + before do + @board.play_word(0,0,'down','cat') + end + + it "index [0][0] should be c" do + @board.board[0][0].must_equal "c" + end + + it "index [1][0] should be a" do + @board.board[1][0].must_equal "a" + + end + + it "index [2][0] should be t" do + @board.board[2][0].must_equal "t" + end + + end + + describe "overlapping words on board" do + before do + @board.play_word(0,0,'down','cat') + @board.play_word(1,0,"right","apple") + end + + it "index[1][1] should be p" do + @board.board[1][1].must_equal "p" + end + + it "should return can't play there if word overlaps" do + @board.play_word(1,3,"down","dog").must_equal "can't play there" + end + + it "index [1][3] should still be l" do + @board.play_word(1,3,"down","dog") + @board.board[1][3].must_equal "l" + + end + end + +end diff --git a/specs/player-spec.rb b/specs/player-spec.rb new file mode 100644 index 00000000..3f97fdab --- /dev/null +++ b/specs/player-spec.rb @@ -0,0 +1,88 @@ +require_relative './spec_helper' +require_relative '../lib/player' +require_relative '../lib/tilebag' + +describe Player do + before do + @sarah = Player.new("sarah") + @lisa = Player.new("lisa") + end + + it "is an object we have access to" do + Player.wont_be_nil + end + + describe "Player#name" do + it "returns sarah when initialized with sarah" do + @sarah.name.must_equal "sarah" + end + end + + describe "words played by player" do + ABOVE_100 = %w[quiz array scores lizard jokes lemon class stack cherry] + WORDS_PLAYED = %w[cat dog zebra elephant bird cow snake puma] + + before do + WORDS_PLAYED.each do |word| + @sarah.play(word) + end + + ABOVE_100.each do |word| + @lisa.play(word) + end + end + + + it "returns the score of the word for the play(word) method" do + @sarah.play("cat").must_equal 5 + end + + it "returns an array of words gussed for the plays method" do + @sarah.plays.must_equal WORDS_PLAYED + end + + it "returns array of words equal to ABOVE_100 for the plays method" do + @lisa.plays.must_equal ABOVE_100 + end + + it "returns the total score of all words played for the total_score method" do + @sarah.total_score.must_equal 71 + end + + it "returns false if the player has under 100 points for the won? method" do + @sarah.won?.must_equal false + end + + it "it will return true if the player has over 100 points for the won? method" do + @lisa.won?.must_equal true + end + + it "will return false if the player has already won for the play word method" do + @lisa.play("cat").must_equal false + end + + it "will return the highest scoring word from all words played using the highest_scoring_word method" do + @lisa.highest_scoring_word.must_equal "quiz" + end + + it "will return the score of the highest word in the list using the highest_word_score method" do + @lisa.highest_word_score.must_equal 22 + end + + + it "will return an empty array" do + @sarah.tiles.must_equal [] + end + + it "will return an array using the draw tile method" do + tile_bag = TileBag.new + @sarah.draw_tiles(tile_bag).must_be_instance_of Array + end + + it "will return an array of length 7" do + tile_bag = TileBag.new + @sarah.draw_tiles(tile_bag).length.must_equal 7 + end + + end +end diff --git a/specs/scoring-spec.rb b/specs/scoring-spec.rb new file mode 100644 index 00000000..52d7a4dd --- /dev/null +++ b/specs/scoring-spec.rb @@ -0,0 +1,47 @@ +require_relative './spec_helper' +require_relative '../lib/scoring' + +describe Scoring do + it "is an object we have access to" do + Scoring.wont_be_nil + end + + describe "Scoring#score" do + TEST_CASES = { + "cat" => 5, + "dog" => 5, + "zoo" => 12, + "AAAAAAA" => 57 + } + + + TEST_CASES.each do |word,score| + it "should return #{score} for the word #{word}" do + Scoring.score(word).must_equal(score) + end + end + end + + describe "Scoring#highest_score_from" do + TEST_ARRAYS = { + ['cat', 'dog', 'zoo'] => 'zoo', + ['zebra', 'program', 'candy'] => 'program', + ['cat', 'dog', 'sit'] => 'cat', + ['cat', 'AAAAAAA', 'EEEEEEE'] => 'AAAAAAA', + ['QZ', 'JX'] => 'QZ', + ['JX', 'QZ'] => 'QZ', + ['AAAAAAA', 'FFFFFFF'] => 'FFFFFFF', + ['AAAA', 'DG'] => 'DG', + ['QQQQQQ', 'FAAAAAA', 'FEEEEEE'] => 'FAAAAAA' + # old test flawed: 'BDG' and 'AAAAAAA' both have scores of 7, + # but 'AAAAAAA' got 50 bonus points and that's why it won in + # the test (not because the word.length == 7 did anything--it didn't) + } + + TEST_ARRAYS.each do |list, word| + it "should return #{word} for the list #{list}" do + Scoring.highest_score_from(list).must_equal(word) + end + end + end +end diff --git a/specs/spec_helper.rb b/specs/spec_helper.rb new file mode 100644 index 00000000..24450977 --- /dev/null +++ b/specs/spec_helper.rb @@ -0,0 +1,8 @@ +require 'simplecov' +SimpleCov.start + +require 'minitest' +require 'minitest/spec' +require 'minitest/autorun' +require 'minitest/reporters' +Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new diff --git a/specs/tilebag-spec.rb b/specs/tilebag-spec.rb new file mode 100644 index 00000000..200eb545 --- /dev/null +++ b/specs/tilebag-spec.rb @@ -0,0 +1,36 @@ +require_relative './spec_helper' +require_relative '../lib/tilebag' + +describe TileBag do + + it "is an object we have access to" do + TileBag.new.wont_be_nil + end + + describe "tiles drawn from tile bag" do + + before do + @tiles = TileBag.new + @tiles_five_drawn = @tiles.draw_tiles(5) + end + + it "returns number of tiles from tile bag using draw_tiles method" do + @tiles_five_drawn.length.must_equal 5 + end + + it "returns an array of tiles from the tile bag using draw_tiles method" do + @tiles_five_drawn.must_be_instance_of Array + end + + it "returns the remaining number of tiles in the tile bag using tiles_remaining method" do + @tiles.tiles_remaining.must_equal 92 + end + + it "returns 92 if we draw 5 tiles using the tiles_remaining method" do + @tiles.draw_tiles(5) + @tiles.tiles_remaining.must_equal 87 + end + + end + +end