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
143 changes: 141 additions & 2 deletions scrabble.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,154 @@
const scoreTable = {
'a': { 'points': 1, 'tiles': 9 },
'b': { 'points': 3, 'tiles': 2 },
'c': { 'points': 3, 'tiles': 2 },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I like that you've consolidated the points and number of tiles into one data structure - clever thinking!

'd': { 'points': 2, 'tiles': 4 },
'e': { 'points': 1, 'tiles': 12 },
'f': { 'points': 4, 'tiles': 2 },
'g': { 'points': 2, 'tiles': 3 },
'h': { 'points': 4, 'tiles': 2 },
'i': { 'points': 1, 'tiles': 9 },
'j': { 'points': 8, 'tiles': 1 },
'k': { 'points': 5, 'tiles': 1 },
'l': { 'points': 1, 'tiles': 4 },
'm': { 'points': 3, 'tiles': 2 },
'n': { 'points': 1, 'tiles': 6 },
'o': { 'points': 1, 'tiles': 8 },
'p': { 'points': 3, 'tiles': 2 },
'q': { 'points': 10, 'tiles': 1 },
'r': { 'points': 1, 'tiles': 6 },
's': { 'points': 1, 'tiles': 4 },
't': { 'points': 1, 'tiles': 6 },
'u': { 'points': 1, 'tiles': 4 },
'v': { 'points': 4, 'tiles': 2 },
'w': { 'points': 4, 'tiles': 2 },
'x': { 'points': 8, 'tiles': 1 },
'y': { 'points': 4, 'tiles': 2 },
'z': { 'points': 10, 'tiles': 1 },
'blank': { 'tiles': 2}
}


const findPoints = function findPoints(letter) {
let lowercase_letter = letter.toLowerCase();
let points = scoreTable[lowercase_letter]['points'];

return points;
}

const checkWord = function permissableWord(word) {
let pattern = /[^a-zA-Z]/;

if (word === '') {
throw "Word cannot be empty";
} else if (pattern.test(word)) {
throw "Must be a real word";
} else if (word.length > 7) {
throw "More than 7 letters not allowed";
}
}

const checkArrayOfWords = function checkArrayofWords(arrayOfWords) {
if (arrayOfWords.length === 0) {
throw "List of words cannot be empty";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Great use of helper functions through here.

}

// const highestScoreTieBreaker = function highestScoreTieBreaker(maxWord, word) {
// if (maxWord.length != 7 && word.length == 7) {
// maxWord = word
// }
//
// if (maxWord.length != 7 && word.length != 7 && maxWord.length > word.length) {
// maxWord = word;
// }
// }

const Scrabble = {
score(word) {
checkWord(word);

let score = 0;
if (word.length === 7) {
score += 50;
}
for (let i = 0; i < word.length; i++) {
let points = findPoints(word[i]);
score += points;
}
return score;
},

highestScoreFrom(arrayOfWords) {
checkArrayOfWords(arrayOfWords);

let maxWord = null;
let maxPoints = null;

arrayOfWords.forEach(function (word) {
let score = Scrabble.score(word);


if (maxPoints === score) {

// highestScoreTieBreaker(maxWord, word);

if (maxWord.length != 7 && word.length == 7) {
maxWord = word
}
if (maxWord.length != 7 && word.length != 7 && maxWord.length > word.length) {
maxWord = word;
}

} else if (maxPoints < score) {
maxWord = word;
maxPoints = score;
}

});
return maxWord;
}
}

},
};

Scrabble.Player = class {
constructor(name, plays = []) {
if (name === undefined ) {
throw "Player must have a name"
}

this.name = name;
this.plays = plays;
}

play(word) {
checkWord(word);
return (this.hasWon() ? false : this.plays.push(word));
}

totalScore() {
let wordsPlayed = this.plays;
let total = 0;
wordsPlayed.forEach(function (word) {
total += Scrabble.score(word);
})
return total;
}

hasWon() {
let total = this.totalScore();
return (total >= 100);
}

highestScoringWord() {
let plays = this.plays;
return Scrabble.highestScoreFrom(plays);
}

highestWordScore() {
let word = this.highestScoringWord();
return Scrabble.score(word);
}
};


Expand Down
94 changes: 60 additions & 34 deletions specs/scrabble.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,46 +11,46 @@ 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', () => {
test('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();
});
});

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 +60,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 +73,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 +83,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 +98,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 +130,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 +142,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 +155,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 +181,71 @@ describe('Player', () => {
});

describe('hasWon', () => {
test.skip('returns false when score < 100', () => {

test('returns false when score < 100', () => {
const player = new Scrabble.Player('test player');

expect(player.play('zzz')).toBeTruthy();
expect(player.plays.length).toBe(1);
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');

expect(player.play('zzzzz')).toBeTruthy();
expect(player.play('zzzzz')).toBeTruthy();
expect(player.plays.length).toBe(2);
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');

expect(player.play('zzzzzzz')).toBeTruthy(); // +120 pts
expect(player.plays.length).toBe(1);
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');

expect(player.play('dog')).toBeTruthy();
expect(player.play('zzzzz')).toBeTruthy();
expect(player.plays.length).toBe(2);
expect(player.highestScoringWord()).toBe('zzzzz');
});

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 player');
expect(player.plays.length).toBe(0);

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');

expect(player.play('dog')).toBeTruthy();
expect(player.play('zzzzz')).toBeTruthy();
expect(player.plays.length).toBe(2);
expect(player.highestWordScore()).toBe(50);
});

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 player');
expect(player.plays.length).toBe(0);

expect(() => {
player.highestWordScore();
}).toThrow();
});
});
});