Skip to content
Open
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license": "ISC",
"dependencies": {
"fs": "0.0.1-security",
"npm": "^5.8.0"
"npm": "^5.10.0"
},
"scripts": {
"test": "jest"
Expand Down
117 changes: 114 additions & 3 deletions scrabble.js
Original file line number Diff line number Diff line change
@@ -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;
88 changes: 57 additions & 31 deletions specs/scrabble.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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';

Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -98,27 +100,27 @@ 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();
});
});

describe('play', () => {
test.skip('Records the played word', () => {
test('Records the played word', () => {
const word = 'dog';
const player = new Scrabble.Player('test player');

Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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}];
Expand All @@ -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();
});
});
});