-
Notifications
You must be signed in to change notification settings - Fork 44
C22- Phoenix- Amber Edwards #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c50a064
abff505
ded3a84
efb4706
b362768
62e766e
f102be8
aaf83f5
b2298a6
175a18a
026ad15
4438b37
bfbce2c
e2ab087
c650f10
1f5b867
3620aa4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,154 @@ | ||
export const drawLetters = () => { | ||
// Implement this method for wave 1 | ||
const LETTER_POOL = { | ||
'A': 9, | ||
'B': 2, | ||
'C': 2, | ||
'D': 4, | ||
'E': 12, | ||
'F': 2, | ||
'G': 3, | ||
'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 | ||
}; | ||
|
||
const LETTER_SCORE = { | ||
'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 | ||
}; | ||
|
||
const HAND_SIZE = 10 | ||
const MIN_BONUS_LENGTH = 7 | ||
const LENGTH_BONUS = 8 | ||
|
||
const createWeightedLetterPool = () =>{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice helper to expand the letter weight information into a pool that actually has the appropriate number of copies. Since this will get used in each call to |
||
let weightedLetterPool = []; | ||
for (const [letter, freq] of Object.entries(LETTER_POOL)){ | ||
for (let i=0; i<freq; i++){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Include spaces around binary operators (and throughout). for (let i = 0; i < freq; i++){ |
||
weightedLetterPool.push(letter); | ||
} | ||
} | ||
return weightedLetterPool; | ||
} | ||
|
||
export const drawLetters = () => { | ||
const hand = []; | ||
const weightedLetterPool = createWeightedLetterPool() | ||
|
||
while (hand.length < HAND_SIZE){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Leave a space between the conditional and the open of the block (and throughout). while (hand.length < HAND_SIZE) { There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still a bit on the fence about whether I prefer the |
||
let randomIdx = Math.floor(Math.random()*weightedLetterPool.length); | ||
let randomLetter = weightedLetterPool[randomIdx]; | ||
hand.push(randomLetter); | ||
weightedLetterPool.splice(randomIdx,1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Much like |
||
} | ||
return hand; | ||
}; | ||
|
||
export const usesAvailableLetters = (input, lettersInHand) => { | ||
// Implement this method for wave 2 | ||
const inputLower = input.toUpperCase(); | ||
const copy = [...lettersInHand]; // I not sure how lettersInHand is used in the program as a whole, so I made a copy | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the approach here is destructive to |
||
|
||
for (let letter of inputLower){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer for (const letter of inputLower){ |
||
if (copy.includes(letter)){ | ||
let idx = copy.indexOf(letter); | ||
copy.splice(idx,1); | ||
Comment on lines
+91
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that each of these calls on copy is linear, nested while iterating over the hand. This gives this logic time complexity of O(m * n), where m and n are the lengths of the input and the hand. Of course, we know those sizes are fixed for the game, (neither is longer than 10), but we should still consider the general case. If we calculated a frequency map over the hand, this would become O(m + n) instead. To know which actually has better performance for the game sizes, we would need to collect actual data, but from a complexity perspective, a frequency map approach is superior. |
||
} else { | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
|
||
|
||
|
||
|
||
|
||
export const scoreWord = (word) => { | ||
// Implement this method for wave 3 | ||
if (!word.trim()) { | ||
return 0; | ||
} | ||
Comment on lines
+106
to
+108
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would leave off this trim behavior. This is granting special status to spaces at the start or end of the word (without granting the same status to spaces within the word). Personally, I'm not even a fan of this function needing to deal with case (though that's a test requirement). I would see this function as scoring a valid word, which should already have accounted for any invalid tiles or vagueries of case before getting here. |
||
word = word.toUpperCase(); | ||
let score = 0; | ||
for (let letter of word){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer const here for (const letter of word){ |
||
score += LETTER_SCORE[letter]; | ||
} | ||
Comment on lines
+111
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great use of a score lookup table to keep this simple. We could also use let score = word.split('').reduce((acc, letter) => acc + LETTER_SCORE[letter], 0); Personally, I find the loop approach you used more readable, but the reduce approach is also popular with JS programmers. |
||
if(word.length >= MIN_BONUS_LENGTH){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Include space between |
||
score += LENGTH_BONUS; | ||
Comment on lines
+114
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice use of constants to avoid magic numbers. |
||
} | ||
return score; | ||
}; | ||
|
||
const getMaxScore = (words) =>{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice helper to find the maximum score among all the words. |
||
let maxScore = 0; | ||
for (let word of words){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer const for (const word of words){ |
||
maxScore = Math.max(maxScore, scoreWord(word)); | ||
}; | ||
Comment on lines
+121
to
+124
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Finding the max is another area where JS programmers like to use reduce. const maxScore = words.reduce((max, word) => Math.max(max, scoreWord(word)), 0); Alternatively, we could first translate all the words into their scores, then find the max as follows: const scores = words.map(scoreWord);
const maxScore = Math.max(...scores); |
||
return maxScore; | ||
} | ||
|
||
const getHighScoreWords = (words, maxScore) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice helper to filter the words down to those with the max score. |
||
let highScoreWords = [] | ||
for (let word of words){ | ||
Comment on lines
+129
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer const const highScoreWords = []
for (const word of words){ |
||
if(scoreWord(word) === maxScore){ | ||
highScoreWords.push(word); | ||
}; | ||
}; | ||
Comment on lines
+129
to
+134
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a place where JS programmers might choose to use const highScoreWords = words.filter(word => scoreWord(word) === maxScore); |
||
return highScoreWords; | ||
} | ||
|
||
export const highestScoreFrom = (words) => { | ||
// Implement this method for wave 4 | ||
let maxScore = getMaxScore(words); | ||
let highScoreWords = getHighScoreWords(words, maxScore); | ||
Comment on lines
+139
to
+140
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Working with only those words having the highest score simplifies the rest of the logic in this function. |
||
|
||
let winningWord = highScoreWords[0]; | ||
|
||
for (let word of highScoreWords){ | ||
if (winningWord.length === 10){ | ||
return {word: winningWord, score: maxScore}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As soon as we find a word that is 10 letters, we know nothing else could win, so we can exit here. Rather than duplicating the return, we could |
||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No semicolon here } |
||
if (winningWord.length > word.length || word.length === 10){ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would probably turn this around so that it reads if the new word is shorter, rather than if the old word is longer. if (word.length < winningWord.length || word.length === 10){ This keeps the two conditions more consistent. |
||
winningWord = word; | ||
}; | ||
}; | ||
return {word: winningWord, score: maxScore}; | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -120,7 +120,11 @@ describe("Adagrams", () => { | |
}); | ||
|
||
it("returns a score of 0 if given an empty input", () => { | ||
throw "Complete test"; | ||
expect(scoreWord("")).toBe(0); | ||
expect(scoreWord(" ")).toBe(0); | ||
expect(scoreWord(" ")).toBe(0); | ||
Comment on lines
+123
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: prefer Checking for the empty string is what I would expect here. I would tend not to include the inputs that have spaces in them. I would expect |
||
|
||
|
||
}); | ||
|
||
it("adds an extra 8 points if word is 7 or more characters long", () => { | ||
|
@@ -133,7 +137,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") }; | ||
|
@@ -145,7 +149,7 @@ describe("Adagrams", () => { | |
const words = ["XXX", "XXXX", "X", "XX"]; | ||
const correct = { word: "XXXX", score: scoreWord("XXXX") }; | ||
|
||
throw "Complete test by adding an assertion"; | ||
expect(highestScoreFrom(words)).toEqual(correct); | ||
}); | ||
|
||
describe("in case of tied score", () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you find that adding this resulted in less fighting with prettier? Personally, I prefer to use the default VS Code Js Formatter.