diff --git a/package.json b/package.json index 50444a7..ec337f6 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "ISC", "dependencies": { "fs": "0.0.1-security", - "npm": "^5.8.0" + "npm": "^5.10.0" }, "scripts": { "test": "jest" diff --git a/scrabble.js b/scrabble.js index e95f352..0b39d10 100644 --- a/scrabble.js +++ b/scrabble.js @@ -1,16 +1,127 @@ - const Scrabble = { + A: 1, + B: 3, + C: 3, + D: 2, + E: 1, + F: 4, + G: 2, + H: 4, + I: 1, + J: 8, + K: 5, + L: 1, + M: 3, + N: 1, + O: 1, + P: 3, + Q: 10, + R: 1, + S: 1, + T: 1, + U: 1, + V: 4, + W: 4, + X: 8, + Y: 4, + Z: 10, + score(word) { + let total = 0; + if (typeof word != 'string' || word.length < 1 || word.length > 7) { + throw "Word must be a non-empty string less than 8 chars."; + } else if (word.length === 7) { + total += 50; + } + + for (let letter of word) { + let capLetter = letter.toUpperCase(); + if (this[capLetter]) { + total += this[capLetter]; + } else { + throw "Word includes an invalid letter."; + } + } + return total; }, + highestScoreFrom(arrayOfWords) { + let highestScore = ["", 0]; - }, + if (arrayOfWords.length < 1 || !Array.isArray(arrayOfWords)) { + throw "Must contain an array."; + } else if (arrayOfWords.length === 1) { + return arrayOfWords[0]; + } + + for (let i = 0; i < arrayOfWords.length; i++) { + if (this.score(arrayOfWords[i]) === highestScore[1]) { + if (arrayOfWords[i].length === 7 || highestScore[0].length === 7) { + return (arrayOfWords[i].length === 7 ? arrayOfWords[i] : highestScore[0]); + } else { + return (arrayOfWords[i].length < highestScore[0].length ? arrayOfWords[i] : highestScore[0]); + } + } else if (this.score(arrayOfWords[i]) >= highestScore[1]) { + highestScore[0] = arrayOfWords[i]; + highestScore[1] = this.score(arrayOfWords[i]); + } + } + return highestScore[0] + } }; + Scrabble.Player = class { + constructor(name) { + this.name = name; + if (this.name.length < 1) { + throw "Player name cannot be empty." + } + this.plays = []; + } -}; + play(word) { + if (word.length < 1 || typeof word != 'string') { + throw "Word is invalid." + } else if (this.hasWon()) { + return false; + } else { + this.plays.push(word); + return this.plays; + } + } + + totalScore() { + let playerScore = 0; + + this.plays.forEach((word) => { + playerScore += Scrabble.score(word) + }); + + return playerScore; + } + hasWon() { + if (this.totalScore() >= 100) { + return true; + } + return false; + } + + highestScoringWord() { + if (this.plays.length < 1) { + throw "No words have been played yet." + } + return Scrabble.highestScoreFrom(this.plays); + } + + highestWordScore() { + if (this.plays.length < 1 ){ + throw "No words have been played yet."; + } + return Scrabble.score(this.highestScoringWord()); + } +}; module.exports = Scrabble; diff --git a/specs/scrabble.spec.js b/specs/scrabble.spec.js index b562037..c9c8f65 100644 --- a/specs/scrabble.spec.js +++ b/specs/scrabble.spec.js @@ -11,46 +11,48 @@ describe('score', () => { expect(Scrabble.score('pig')).toBe(6); }); - test.skip('adds 50 points for a 7-letter word', () => { + test('adds 50 points for a 7-letter word', () => { expect(Scrabble.score('academy')).toBe(65); }); - test.skip('throws on bad characters', () => { + test('throws on bad characters', () => { expect(() => { Scrabble.score('char^'); }).toThrow(); }); - test.skip('handles all upper- and lower-case letters', () => { + test('handles all upper- and lower-case letters', () => { expect(Scrabble.score('dog')).toBe(5); expect(Scrabble.score('DOG')).toBe(5); expect(Scrabble.score('DoG')).toBe(5); }); - test.skip('does not allow words > 7 letters', () => { - expect(() => { Scrabble.score('abcdefgh'); }).toThrow(); - }); - test.skip('does not allow empty words', () => { + test('does not allow empty words', () => { expect(() => { Scrabble.score(''); }).toThrow(); }); + + test('does not allow words > 7 letters', () => { + expect(() => { Scrabble.score('abcdefgh'); }).toThrow(); + }); + }); describe('highestScoreFrom', () => { - test.skip('is defined', () => { + test('is defined', () => { expect(Scrabble.highestScoreFrom).toBeDefined(); }); - test.skip('throws if no words were passed', () => { + test('throws if no words were passed', () => { expect(() => { Scrabble.highestScoreFrom([]); }).toThrow(); expect(() => { Scrabble.highestScoreFrom('not array'); }).toThrow(); }); - test.skip('returns the only word in a length-1 array', () => { + test('returns the only word in a length-1 array', () => { expect(Scrabble.highestScoreFrom(['dog'])).toBe('dog'); }); - test.skip('returns the highest word if there are two words', () => { + test('returns the highest word if there are two words', () => { // Check score assumptions expect(Scrabble.score('dog')).toBe(5); expect(Scrabble.score('pig')).toBe(6); @@ -60,7 +62,7 @@ describe('highestScoreFrom', () => { expect(Scrabble.highestScoreFrom(['pig', 'dog'])).toBe('pig'); }); - test.skip('if tied, prefer a word with 7 letters', () => { + test('if tied, prefer a word with 7 letters', () => { const loser = 'zzzzzz'; const winner = 'iiiiddd'; @@ -73,7 +75,7 @@ describe('highestScoreFrom', () => { expect(Scrabble.highestScoreFrom([winner, loser])).toBe(winner); }); - test.skip('if tied and no word has 7 letters, prefers the word with fewer letters', () => { + test('if tied and no word has 7 letters, prefers the word with fewer letters', () => { // Check score assumptions expect(Scrabble.score('dog')).toBe(5); expect(Scrabble.score('goat')).toBe(5); @@ -83,7 +85,7 @@ describe('highestScoreFrom', () => { expect(Scrabble.highestScoreFrom(['goat', 'dog'])).toBe('dog'); }); - test.skip('returns the first word of a tie with same letter count', () => { + test('returns the first word of a tie with same letter count', () => { // Check score assumptions expect(Scrabble.score('i')).toBe(1); expect(Scrabble.score('dog')).toBe(5); @@ -98,19 +100,19 @@ describe('highestScoreFrom', () => { }); describe('Player', () => { - test.skip('is defined', () => { + test('is defined', () => { expect(Scrabble.Player).toBeDefined(); }); describe('Constructor', () => { - test.skip('Creates a new player', () => { + test('Creates a new player', () => { const name = 'test name'; const player = new Scrabble.Player(name); expect(player.name).toBe(name); }); - test.skip('Requires a name', () => { + test('Requires a name', () => { expect(() => { new Scrabble.Player(); }).toThrow(); @@ -118,7 +120,7 @@ describe('Player', () => { }); describe('play', () => { - test.skip('Records the played word', () => { + test('Records the played word', () => { const word = 'dog'; const player = new Scrabble.Player('test player'); @@ -130,7 +132,7 @@ describe('Player', () => { expect(player.plays[0]).toBe(word); }); - test.skip('Requires a real word', () => { + test('Requires a real word', () => { const player = new Scrabble.Player('test player'); expect(player.plays.length).toBe(0); @@ -142,7 +144,7 @@ describe('Player', () => { expect(player.plays.length).toBe(0); }); - test.skip('Returns false and does not update plays if the player has already won', () => { + test('Returns false and does not update plays if the player has already won', () => { const player = new Scrabble.Player('test player'); expect(player.play('zzzzzzz')).toBeTruthy(); // +120 pts @@ -155,13 +157,13 @@ describe('Player', () => { }); describe('totalScore', () => { - test.skip('Is zero if the player has not played anything', () => { + test('Is zero if the player has not played anything', () => { const player = new Scrabble.Player('test player'); expect(player.totalScore()).toBe(0); }); - test.skip('Is updated by play', () => { + test('Is updated by play', () => { // Arrange const player = new Scrabble.Player('test player'); const words = [{word: 'dog', score: 5}, {word: 'cat', score: 5}, {word: 'goat', score: 5}]; @@ -181,45 +183,69 @@ describe('Player', () => { }); describe('hasWon', () => { - test.skip('returns false when score < 100', () => { + test('returns false when score < 100', () => { + const player = new Scrabble.Player('test'); + player.play('snail'); + player.play('wow'); + expect(player.hasWon()).toBe(false) }); - test.skip('returns true when score == 100', () => { + test('returns true when score == 100', () => { + const player = new Scrabble.Player('test'); + player.play('zzzzz'); + player.play('zzzzz'); + expect(player.hasWon()).toBe(true); }); - test.skip('returns true when score > 100', () => { + test('returns true when score > 100', () => { + const player = new Scrabble.Player('test'); + player.play('zzzzz'); + player.play('zzzzz'); + player.play('quail'); + expect(player.hasWon()).toBe(true); }); }); describe('highestScoringWord', () => { // Tie-breaking logic is already described in the tests // for highestWordFrom, so we will not repeat it here. - test.skip('returns the highest scoring word played', () => { + test('returns the highest scoring word played', () => { + const player = new Scrabble.Player('test'); + player.play('quail'); + player.play('and'); + expect(player.highestScoringWord()).toBe('quail') }); - test.skip('throws an error if no words have been played', () => { - + test('throws an error if no words have been played', () => { + const player = new Scrabble.Player('test'); + expect(() => { player.highestScoringWord(); }).toThrow(); }); }); describe('highestWordScore', () => { - test.skip('returns the score of the highest scoring word played', () => { + test('returns the score of the highest scoring word played', () => { + const player = new Scrabble.Player('test'); + player.play('dog'); + player.play('quail'); + let highest = Scrabble.score('quail'); + expect(player.highestWordScore()).toBe(highest); }); - test.skip('throws an error if no words have been played', () => { - + test('throws an error if no words have been played', () => { + const player = new Scrabble.Player('test'); + expect(() => { player.highestWordScore(); }).toThrow(); }); }); });