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
278 changes: 273 additions & 5 deletions src/adagrams.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,283 @@
"use strict";

export const drawLetters = () => {
// Implement this method for wave 1
const lettersPool = [
"A",
"A",
"A",
"A",
"A",
"A",
"A",
"A",
"A",
"B",
"B",
"C",
"C",
"D",
"D",
"D",
"D",
"E",
"E",
"E",
"E",
"E",
"E",
"E",
"E",
"E",
"E",
"E",
"E",
"F",
"F",
"G",
"G",
"G",
"H",
"H",
"I",
"I",
"I",
"I",
"I",
"I",
"I",
"I",
"I",
"J",
"K",
"L",
"L",
"L",
"L",
"M",
"M",
"N",
"N",
"N",
"N",
"N",
"N",
"O",
"O",
"O",
"O",
"O",
"O",
"O",
"O",
"P",
"P",
"Q",
"R",
"R",
"R",
"R",
"R",
"R",
"S",
"S",
"S",
"S",
"T",
"T",
"T",
"T",
"T",
"T",
"U",
"U",
"U",
"U",
"V",
"V",
"W",
"W",
"X",
"Y",
"Y",
"Z",
];
const lettersPoolDeepcopy = JSON.parse(JSON.stringify(lettersPool));

Choose a reason for hiding this comment

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

Performing a copy like this is a great place to use the spread operator! What could that look like?

Since lettersPool is declared inside the function, the data structure gets re-created every time we call the function. This means we shouldn't need to copy lettersPool to ensure that changes aren't persisted across calls.

const hand = [];

for (let i = 0; i < 10; i++) {
const random_number = Math.floor(
Math.random() * lettersPoolDeepcopy.length
);
hand.push(lettersPoolDeepcopy[random_number]);
lettersPoolDeepcopy.splice(random_number, 1);
}
return hand;
Comment on lines +105 to +114

Choose a reason for hiding this comment

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

Really nice approach!

};

export const usesAvailableLetters = (input, lettersInHand) => {
// Implement this method for wave 2
input = input.toUpperCase();
for (const letter of input) {
if (lettersInHand.includes(letter)) {
const index = lettersInHand.indexOf(letter);
lettersInHand.splice(index, 1);
} else {
return false;

Choose a reason for hiding this comment

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

I like the early exit =]

}
}
Comment on lines +119 to +126

Choose a reason for hiding this comment

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

Nice approach! If we needed to reduce the time complexity of our solution, another approach could be to build a frequency map of our hand then loop over the characters in the input, checking if the character is in our frequency map, and if it is, then check the value to see if there are still tiles left in our hand for that letter.

return true;
};

export const scoreWord = (word) => {
// Implement this method for wave 3
const lettersValue = {

Choose a reason for hiding this comment

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

I like keeping these in the function that uses them so they're close to where they are referenced.

A: 1,
E: 1,
I: 1,
O: 1,
U: 1,
L: 1,
N: 1,
R: 1,
S: 1,
T: 1,
D: 2,
G: 2,
B: 3,
C: 3,
M: 3,
P: 3,
F: 4,
H: 4,
V: 4,
W: 4,
Y: 4,
K: 5,
J: 8,
X: 8,
Q: 10,
Z: 10,
};
let score = 0;
word = word.toUpperCase();
for (const letter of word) {

Choose a reason for hiding this comment

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

Nice use of a for...of loop.

score += lettersValue[letter];
}
Comment on lines +161 to +163

Choose a reason for hiding this comment

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

This could have an unexpected result if the string word has characters that aren't in lettersValue. If we have an input like "ABC DEF" then score will hold NaN (Not a Number) after the loop. If we wanted to skip non-alphabetic characters or characters that aren't in lettersValue, how could we do that?

if (word.length > 6) {
score += 8;
}
return score;
};

export const highestScoreFrom = (words) => {
// Implement this method for wave 4
// ~~~~~~~~~~~~~~~~~~~ pseudo code for wave 4 ~~~~~~~~~~~~~~~~~~
// - create an empty dictionary
// - loop over the list "words": add the word as the key to the dict and the score (accessed through the last function) as the value
// - create an empty dcit: wordsWithHighestScore
// - find these words manually in a for loop (after finding the maximum value by the builtin function) and add them as the keys and their length as the value
// - if length of the dict is one: return this word
// - else: find the words with the score 10 and put them in a list --> if the length is one --> return this word
// - find the minumum word length in the dict and find out how many words share that length by adding these word to a dict where the values are the indeces in the original list
//- --> if only one has the minLength --> return this word
//- return the min of this dictionary


const createScoresObject = (words) => {
const scoresObject = {};
for (const word of words) {
scoresObject[word] = scoreWord(word);
}
return scoresObject;
};

const createWordsWithMaxScoreObject = (scoresObject, maxScore) => {
const wordsWithMaxScore = {};
for (const word in scoresObject) {
if (scoresObject[word] === maxScore) {
wordsWithMaxScore[word] = word.length;
}
}
return wordsWithMaxScore;
};

const findIfOneWordWith10Letters = (wordsWithMaxScore) => {
let wordsWithMaxScore10Letters = 0;
let winnerWord;
for (const word of Object.keys(wordsWithMaxScore)) {
if (wordsWithMaxScore[word] === 10) {
wordsWithMaxScore10Letters++;
winnerWord = word;
}
}
if (wordsWithMaxScore10Letters === 1) {
return winnerWord;
}
return "";
};

const findWordsWithMinLength = (wordsWithMaxScore, words) => {
const wordLengthsArray = Object.values(wordsWithMaxScore);
const minWordLength = Math.min(...wordLengthsArray);
const wordsWithMinLength = {};
for (const word in wordsWithMaxScore) {
if (wordsWithMaxScore[word] === minWordLength) {
wordsWithMinLength[word] = words.indexOf(word);
}
}
return wordsWithMinLength;
};

const findWordWithMinIndex = (wordsWithMinLength) => {
const indexArray = Object.values(wordsWithMinLength);
const minIndex = Math.min(...indexArray);
for (const word in wordsWithMinLength) {
if (wordsWithMinLength[word] === minIndex) {
return word;
}
}
};

export const highestScoreFrom = (words) => {
// create scoresObject where the keys are the words and the values are the scores
const scoresObject = createScoresObject(words);
const scoresArray = Object.values(scoresObject);
const maxScore = Math.max(...scoresArray);

// create object wordsWithMaxScore
// where the keys are the words which have the max score
// and the values are the length of each word
const wordsWithMaxScore = createWordsWithMaxScoreObject(
scoresObject,
maxScore
);
if (Object.keys(wordsWithMaxScore).length === 1) {
return {
word: Object.keys(wordsWithMaxScore)[0],
score: maxScore,
};
}

//the function findIfOneWordWith10Letters() returns the word
// which is made of 10 letters if there's only one among the words with maxScore
// else the function returns an empty string
const oneWordWith10Letters = findIfOneWordWith10Letters(wordsWithMaxScore);
if (oneWordWith10Letters) {
return {
word: oneWordWith10Letters,
score: maxScore,
};
}

// create object wordsWithMinLength where the keys are the words
// with the smallest length and the values are the words' indices
// in the input array
const wordsWithMinLength = findWordsWithMinLength(wordsWithMaxScore, words);
if (Object.keys(wordsWithMinLength).length === 1) {
return {
word: Object.keys(wordsWithMinLength)[0],
score: maxScore,
};
}

return {
word: findWordWithMinIndex(wordsWithMinLength),
score: maxScore,
};
};
9 changes: 6 additions & 3 deletions test/adagrams.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ describe("Adagrams", () => {
});

it("returns a score of 0 if given an empty input", () => {
throw "Complete test";
expectScores({
"": 0
});
});

it("adds an extra 8 points if word is 7 or more characters long", () => {
Expand All @@ -133,7 +135,7 @@ describe("Adagrams", () => {
});
});

describe.skip("highestScoreFrom", () => {
describe("highestScoreFrom", () => {
it("returns a hash that contains the word and score of best word in an array", () => {
const words = ["X", "XX", "XXX", "XXXX"];
const correct = { word: "XXXX", score: scoreWord("XXXX") };
Expand All @@ -145,7 +147,8 @@ describe("Adagrams", () => {
const words = ["XXX", "XXXX", "X", "XX"];
const correct = { word: "XXXX", score: scoreWord("XXXX") };

throw "Complete test by adding an assertion";
// throw "Complete test by adding an assertion";
expect(highestScoreFrom(words)).toEqual(correct)
});

describe("in case of tied score", () => {
Expand Down
2 changes: 1 addition & 1 deletion test/demo/model.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Model from 'demo/model';
import Adagrams from 'demo/adagrams';

describe.skip('Game Model', () => {
describe('Game Model', () => {
const config = {
players: [
'Player A',
Expand Down
Loading