From 85715dc4c2be528da15eb593abe06d7e440a2ed8 Mon Sep 17 00:00:00 2001 From: Eva Liu Date: Tue, 17 Jan 2023 21:12:09 -0500 Subject: [PATCH 1/4] drawLetters function --- src/adagrams.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/adagrams.js b/src/adagrams.js index 7ec5afc7..48a01de9 100644 --- a/src/adagrams.js +++ b/src/adagrams.js @@ -1,5 +1,48 @@ +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, +}; + export const drawLetters = () => { - // Implement this method for wave 1 + // const copy_pool = structuredClone(LETTER_POOL); #not able to use deep copy + const copy_pool = { ...LETTER_POOL }; + let random_ten_letters = []; + const keys_arr = Object.keys(copy_pool); + let random_key; + while (random_ten_letters.length < 10) { + random_key = keys_arr[Math.floor(Math.random() * keys_arr.length)]; + if (copy_pool[random_key] > 0) { + random_ten_letters.push(random_key); + copy_pool[random_key] -= 1; + } else if (copy_pool[random_key] === 0) { + continue; + } + } + return random_ten_letters; }; export const usesAvailableLetters = (input, lettersInHand) => { From 545b23a27dcbc1d075d8fcea817e8f3f8ceb28da Mon Sep 17 00:00:00 2001 From: Eva Liu Date: Thu, 19 Jan 2023 02:54:09 -0500 Subject: [PATCH 2/4] usesAvailableLetters and scoreWord functions completed --- src/adagrams.js | 55 +++++++++++++++++++++++++++++++++++++++++-- test/adagrams.test.js | 10 +++++++- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/adagrams.js b/src/adagrams.js index 48a01de9..e8a67312 100644 --- a/src/adagrams.js +++ b/src/adagrams.js @@ -27,11 +27,32 @@ const LETTER_POOL = { Z: 1, }; +// const LETTER_SCORE = { +// 1: ["A", "E", "I", "O", "U", "L", "N", "R", "S", "T"], +// 2: ["D", "G"], +// 3: ["B", "C", "M", "P"], +// 4: ["F", "H", "V", "W", "Y"], +// 5: "K", +// 8: ["J", "X"], +// 10: ["Q", "Z"], +// }; + +const LETTER_SCORE = { + AEIOUNRST: 1, + DG: 2, + BCMP: 3, + FHVWY: 4, + K: 5, + JX: 8, + QZ: 10, +}; + export const drawLetters = () => { // const copy_pool = structuredClone(LETTER_POOL); #not able to use deep copy const copy_pool = { ...LETTER_POOL }; let random_ten_letters = []; const keys_arr = Object.keys(copy_pool); + let random_key; while (random_ten_letters.length < 10) { random_key = keys_arr[Math.floor(Math.random() * keys_arr.length)]; @@ -46,11 +67,41 @@ export const drawLetters = () => { }; export const usesAvailableLetters = (input, lettersInHand) => { - // Implement this method for wave 2 + const input_arr = input.split(""); + if (input_arr.length > lettersInHand.length) { + return false; + } + + for (let i = 0; i < input_arr.length; ++i) { + if (lettersInHand.includes(input_arr[i]) === false) { + return false; + } + let index = lettersInHand.indexOf(input_arr[i]); + lettersInHand.splice(index, index + 1); + } + return true; }; export const scoreWord = (word) => { - // Implement this method for wave 3 + // if word is empty str return 0 + if (word === "") return 0; + + word = word.toUpperCase(); + let total = 0; + let keys_arr = Object.keys(LETTER_SCORE); + + for (let i = 0; i < word.length; ++i) { + for (let j = 0; j < keys_arr.length; ++j) { + if (keys_arr[j].indexOf(word[i]) !== -1) { + total += LETTER_SCORE[keys_arr[j]]; + } + } + } + + if (word.length >= 7) { + total += 8; + } + return total; }; export const highestScoreFrom = (words) => { diff --git a/test/adagrams.test.js b/test/adagrams.test.js index 1a0dc94e..99e690ee 100644 --- a/test/adagrams.test.js +++ b/test/adagrams.test.js @@ -37,14 +37,18 @@ const LETTER_POOL = { describe("Adagrams", () => { describe("drawLetters", () => { it("draws ten letters from the letter pool", () => { + //Act const drawn = drawLetters(); + //Assert expect(drawn).toHaveLength(10); }); it("returns an array, and each item is a single-letter string", () => { + //Act const drawn = drawLetters(); + //Assert expect(Array.isArray(drawn)).toBe(true); drawn.forEach((l) => { expect(l).toMatch(/^[A-Z]$/); @@ -52,6 +56,7 @@ describe("Adagrams", () => { }); it("does not draw a letter too many times", () => { + //Act for (let i = 0; i < 1000; i++) { const drawn = drawLetters(); const letter_freq = {}; @@ -63,6 +68,7 @@ describe("Adagrams", () => { } } + //Assert for (let letter of drawn) { expect(letter_freq[letter]).toBeLessThanOrEqual(LETTER_POOL[letter]); } @@ -120,7 +126,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", () => { From 59392bfb1eaf98e846c83b5eb5dac9a90e4b27ad Mon Sep 17 00:00:00 2001 From: Eva Liu Date: Thu, 19 Jan 2023 23:57:02 -0500 Subject: [PATCH 3/4] all 4 waves with tests. --- src/adagrams.js | 97 ++++++++++++++++++++++++++++--------------- test/adagrams.test.js | 12 +++--- 2 files changed, 70 insertions(+), 39 deletions(-) diff --git a/src/adagrams.js b/src/adagrams.js index e8a67312..c11e46c3 100644 --- a/src/adagrams.js +++ b/src/adagrams.js @@ -27,16 +27,6 @@ const LETTER_POOL = { Z: 1, }; -// const LETTER_SCORE = { -// 1: ["A", "E", "I", "O", "U", "L", "N", "R", "S", "T"], -// 2: ["D", "G"], -// 3: ["B", "C", "M", "P"], -// 4: ["F", "H", "V", "W", "Y"], -// 5: "K", -// 8: ["J", "X"], -// 10: ["Q", "Z"], -// }; - const LETTER_SCORE = { AEIOUNRST: 1, DG: 2, @@ -48,35 +38,34 @@ const LETTER_SCORE = { }; export const drawLetters = () => { - // const copy_pool = structuredClone(LETTER_POOL); #not able to use deep copy - const copy_pool = { ...LETTER_POOL }; - let random_ten_letters = []; - const keys_arr = Object.keys(copy_pool); + // # not sure why not be able to use deep copy. + // const copy_pool = structuredClone(LETTER_POOL); + const copyPool = { ...LETTER_POOL }; + let randomTenLetters = []; + const keysArr = Object.keys(copyPool); - let random_key; - while (random_ten_letters.length < 10) { - random_key = keys_arr[Math.floor(Math.random() * keys_arr.length)]; - if (copy_pool[random_key] > 0) { - random_ten_letters.push(random_key); - copy_pool[random_key] -= 1; - } else if (copy_pool[random_key] === 0) { - continue; + let randomKey; + while (randomTenLetters.length < 10) { + randomKey = keysArr[Math.floor(Math.random() * keysArr.length)]; + if (copyPool[randomKey] > 0) { + randomTenLetters.push(randomKey); + copyPool[randomKey] -= 1; } } - return random_ten_letters; + return randomTenLetters; }; export const usesAvailableLetters = (input, lettersInHand) => { - const input_arr = input.split(""); - if (input_arr.length > lettersInHand.length) { + const inputArr = input.split(""); + if (inputArr.length > lettersInHand.length) { return false; } - for (let i = 0; i < input_arr.length; ++i) { - if (lettersInHand.includes(input_arr[i]) === false) { + for (let i = 0; i < inputArr.length; ++i) { + if (lettersInHand.includes(inputArr[i]) === false) { return false; } - let index = lettersInHand.indexOf(input_arr[i]); + let index = lettersInHand.indexOf(inputArr[i]); lettersInHand.splice(index, index + 1); } return true; @@ -88,12 +77,12 @@ export const scoreWord = (word) => { word = word.toUpperCase(); let total = 0; - let keys_arr = Object.keys(LETTER_SCORE); + let keysArr = Object.keys(LETTER_SCORE); for (let i = 0; i < word.length; ++i) { - for (let j = 0; j < keys_arr.length; ++j) { - if (keys_arr[j].indexOf(word[i]) !== -1) { - total += LETTER_SCORE[keys_arr[j]]; + for (let j = 0; j < keysArr.length; ++j) { + if (keysArr[j].indexOf(word[i]) !== -1) { + total += LETTER_SCORE[keysArr[j]]; } } } @@ -105,5 +94,47 @@ export const scoreWord = (word) => { }; export const highestScoreFrom = (words) => { - // Implement this method for wave 4 + console.log(`words: ${words}`); + + let words_socres_Obj = {}; + let maxScore = 0; + + // find each word's score and the max score + for (let i = 0; i < words.length; ++i) { + let eachScore = scoreWord(words[i]); + words_socres_Obj[words[i]] = eachScore; + if (eachScore > maxScore) maxScore = eachScore; + } + + // loop through the words_socres_obj to get the sames max score words + for (let eachWord in words_socres_Obj) { + if (words_socres_Obj[eachWord] !== maxScore) { + delete words_socres_Obj[eachWord]; + } + } + + let result = { word: "", score: 0 }; + + if (Object.keys(words_socres_Obj).length > 1) { + let wordsArr = Object.keys(words_socres_Obj); + let wordsLengthArr = wordsArr.map((word) => word.length); + let minLengh = Math.min(...wordsLengthArr); + // if the words is 10 letters => choose the 10 letters (find) + if (wordsArr.find((word) => word.length === 10) !== undefined) { + let tenLettersWord = wordsArr.find((word) => word.length === 10); + result.word = tenLettersWord; + result.score = words_socres_Obj[tenLettersWord]; + return result; + } + // if words different length => pick the shortest word (min) + // if words in same length => pick the first (if all the same min will retrun the only num) + let minOrfirstWord = wordsArr.find((word) => word.length === minLengh); + result.word = minOrfirstWord; + result.score = words_socres_Obj[minOrfirstWord]; + return result; + } else { + result.word = Object.keys(words_socres_Obj)[0]; + result.score = Object.values(words_socres_Obj)[0]; + return result; + } }; diff --git a/test/adagrams.test.js b/test/adagrams.test.js index 99e690ee..abc216c8 100644 --- a/test/adagrams.test.js +++ b/test/adagrams.test.js @@ -141,19 +141,19 @@ 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") }; + const words = ["a", "DOG", "king", "Quit"]; + const correct = { word: "Quit", score: scoreWord("Quit") }; expect(highestScoreFrom(words)).toEqual(correct); }); it("accurately finds best scoring word even if not sorted", () => { - const words = ["XXX", "XXXX", "X", "XX"]; - const correct = { word: "XXXX", score: scoreWord("XXXX") }; + const words = ["king", "Quantizers", "a", "DOG"]; + const correct = { word: "Quantizers", score: scoreWord("Quantizers") }; - throw "Complete test by adding an assertion"; + expect(highestScoreFrom(words)).toEqual(correct); }); describe("in case of tied score", () => { From 38f1130aa6443d204b859f316993b71e615605f6 Mon Sep 17 00:00:00 2001 From: Eva Liu Date: Fri, 20 Jan 2023 17:39:32 -0500 Subject: [PATCH 4/4] clean up the console.log --- src/adagrams.js | 3 +- test/demo/model.test.js | 118 +++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/adagrams.js b/src/adagrams.js index c11e46c3..be085f92 100644 --- a/src/adagrams.js +++ b/src/adagrams.js @@ -94,8 +94,6 @@ export const scoreWord = (word) => { }; export const highestScoreFrom = (words) => { - console.log(`words: ${words}`); - let words_socres_Obj = {}; let maxScore = 0; @@ -119,6 +117,7 @@ export const highestScoreFrom = (words) => { let wordsArr = Object.keys(words_socres_Obj); let wordsLengthArr = wordsArr.map((word) => word.length); let minLengh = Math.min(...wordsLengthArr); + // if the words is 10 letters => choose the 10 letters (find) if (wordsArr.find((word) => word.length === 10) !== undefined) { let tenLettersWord = wordsArr.find((word) => word.length === 10); diff --git a/test/demo/model.test.js b/test/demo/model.test.js index 49bf9599..9616ec8b 100644 --- a/test/demo/model.test.js +++ b/test/demo/model.test.js @@ -1,48 +1,45 @@ -import Model from 'demo/model'; -import Adagrams from 'demo/adagrams'; +import Model from "demo/model"; +import Adagrams from "demo/adagrams"; -describe.skip('Game Model', () => { +describe("Game Model", () => { const config = { - players: [ - 'Player A', - 'Player B', - ], + players: ["Player A", "Player B"], rounds: 3, time: 60, // Seconds }; - describe('constructor', () => { - it('creates a new Model instance', () => { + describe("constructor", () => { + it("creates a new Model instance", () => { const model = new Model(config); expect(model).toBeInstanceOf(Model); }); - it('requires a config parameter', () => { + it("requires a config parameter", () => { expect(() => { const model = new Model(); }).toThrow(/config/); }); - it('initializes the round number to zero', () => { + it("initializes the round number to zero", () => { const model = new Model(config); expect(model.round).toBe(0); }); - it('initializes the current player to null', () => { + it("initializes the current player to null", () => { const model = new Model(config); expect(model.currentPlayer).toBe(null); }); - it('initializes the letter bank to null', () => { + it("initializes the letter bank to null", () => { const model = new Model(config); expect(model.letterBank).toBe(null); }); - it('initializes the plays history', () => { + it("initializes the plays history", () => { const model = new Model(config); expect(model.plays).toBeInstanceOf(Object); @@ -54,14 +51,14 @@ describe.skip('Game Model', () => { }); }); - describe('.currentPlayerName()', () => { - it('is defined', () => { + describe(".currentPlayerName()", () => { + it("is defined", () => { const model = new Model(config); expect(model.currentPlayerName).toBeDefined(); }); - it('returns the name of the current player when game is on-going', () => { + it("returns the name of the current player when game is on-going", () => { const model = new Model(config); model.nextRound(); @@ -69,21 +66,21 @@ describe.skip('Game Model', () => { expect(model.currentPlayerName()).toEqual(model.config.players[0]); }); - it('returns null when the game is not on-going', () => { + it("returns null when the game is not on-going", () => { const model = new Model(config); expect(model.currentPlayerName()).toBe(null); }); }); - describe('.nextRound', () => { - it('is defined', () => { + describe(".nextRound", () => { + it("is defined", () => { const model = new Model(config); expect(model.nextRound).toBeDefined(); }); - it('increments the round number', () => { + it("increments the round number", () => { const model = new Model(config); const roundBefore = model.round; @@ -92,7 +89,7 @@ describe.skip('Game Model', () => { expect(model.round).toBe(roundBefore + 1); }); - it('initializes the current player number to first player', () => { + it("initializes the current player number to first player", () => { const model = new Model(config); model.nextRound(); @@ -100,7 +97,7 @@ describe.skip('Game Model', () => { expect(model.currentPlayer).toBe(0); }); - it('initializes the round play history for first player', () => { + it("initializes the round play history for first player", () => { const model = new Model(config); model.nextRound(); @@ -113,7 +110,7 @@ describe.skip('Game Model', () => { }); }); - it('draws a new hand of letters', () => { + it("draws a new hand of letters", () => { const model = new Model(config); model.nextRound(); @@ -125,8 +122,8 @@ describe.skip('Game Model', () => { }); }); - describe('returns game state', () => { - it('gameOver', () => { + describe("returns game state", () => { + it("gameOver", () => { const model = new Model({ ...config, rounds: 1 }); const gameState = model.nextRound(); @@ -138,8 +135,8 @@ describe.skip('Game Model', () => { expect(gameOverState.gameOver).toBe(true); }); - it('winner', () => { - const model = new Model({ ...config, rounds : 2 }); + it("winner", () => { + const model = new Model({ ...config, rounds: 2 }); // Start game, no one has won yet let gameState = model.nextRound(); @@ -147,7 +144,7 @@ describe.skip('Game Model', () => { // First player plays a word let p1Score = 0; - let word = model.letterBank.slice(0, 5).join(''); + let word = model.letterBank.slice(0, 5).join(""); p1Score += model.playWord(word); // Second player does not play @@ -157,7 +154,7 @@ describe.skip('Game Model', () => { expect(gameState.winner).toBe(null); // First player plays another word - word = model.letterBank.slice(0, 5).join(''); + word = model.letterBank.slice(0, 5).join(""); p1Score += model.playWord(word); // Second player does not play again @@ -173,7 +170,7 @@ describe.skip('Game Model', () => { }); }); - describe('.nextTurn', () => { + describe(".nextTurn", () => { const getModel = () => { const model = new Model(config); model.nextRound(); @@ -181,13 +178,13 @@ describe.skip('Game Model', () => { return model; }; - it('is defined', () => { + it("is defined", () => { const model = getModel(); expect(model.nextTurn).toBeDefined(); }); - it('increments the current player index', () => { + it("increments the current player index", () => { const model = getModel(); const origPlayer = model.currentPlayer; @@ -198,8 +195,8 @@ describe.skip('Game Model', () => { expect(model.currentPlayer).toBe(origPlayer + 2); }); - describe('returns round state', () => { - it('roundOver', () => { + describe("returns round state", () => { + it("roundOver", () => { const model = getModel(); const roundState = model.nextTurn(); @@ -208,7 +205,7 @@ describe.skip('Game Model', () => { expect(config.players.length).toBeGreaterThan(1); expect(roundState).toBeInstanceOf(Object); - expect(roundState).toHaveProperty('roundOver'); + expect(roundState).toHaveProperty("roundOver"); expect(roundState.roundOver).toBe(false); // Advance to the final turn @@ -221,7 +218,7 @@ describe.skip('Game Model', () => { expect(roundOverState.roundOver).toBe(true); }); - it('winner', () => { + it("winner", () => { const model = getModel(); const roundState = model.nextTurn(); @@ -236,7 +233,7 @@ describe.skip('Game Model', () => { }); // Play a word as the last player - const word = model.letterBank.slice(0, 5).join(''); + const word = model.letterBank.slice(0, 5).join(""); const score = model.playWord(word); // Complete the final turn, round is over and winner should be set @@ -253,7 +250,7 @@ describe.skip('Game Model', () => { }); }); - describe('.playWord', () => { + describe(".playWord", () => { const getModel = () => { const model = new Model(config); model.nextRound(); @@ -265,18 +262,18 @@ describe.skip('Game Model', () => { return [...(model.plays[player][round - 1] || [])]; }; - it('is defined', () => { + it("is defined", () => { const model = getModel(); expect(model.playWord).toBeDefined(); }); - describe('for valid words', () => { + describe("for valid words", () => { const getWord = (model) => { - return model.letterBank.slice(0, 5).join(''); + return model.letterBank.slice(0, 5).join(""); }; - it('it returns the word score', () => { + it("it returns the word score", () => { const model = getModel(); const word = getWord(model); const score = Adagrams.scoreWord(word); @@ -284,21 +281,28 @@ describe.skip('Game Model', () => { expect(model.playWord(word)).toBe(score); }); - it('adds word to plays history for current player', () => { + it("adds word to plays history for current player", () => { const model = getModel(); const player = model.currentPlayerName(); const origPlays = getPlays(model, player, model.round); const word1 = getWord(model); model.playWord(word1); - expect(getPlays(model, player, model.round)).toEqual([...origPlays, word1]); + expect(getPlays(model, player, model.round)).toEqual([ + ...origPlays, + word1, + ]); const word2 = getWord(model); model.playWord(word2); - expect(getPlays(model, player, model.round)).toEqual([...origPlays, word1, word2]); + expect(getPlays(model, player, model.round)).toEqual([ + ...origPlays, + word1, + word2, + ]); }); - it('validates word case-insensitively', () => { + it("validates word case-insensitively", () => { const model = getModel(); const word = getWord(model); const score = Adagrams.scoreWord(word); @@ -307,27 +311,29 @@ describe.skip('Game Model', () => { }); }); - describe('for invalid words', () => { + describe("for invalid words", () => { const getWord = (model) => { const letter = model.letterBank[0]; - return letter.repeat(model.letterBank.filter((l) => { - return l === letter; - }).length + 1); + return letter.repeat( + model.letterBank.filter((l) => { + return l === letter; + }).length + 1 + ); }; - it('it returns null', () => { + it("it returns null", () => { const model = getModel(); const word = getWord(model); expect(model.playWord(word)).toBe(null); - expect(model.playWord('123')).toBe(null); - expect(model.playWord('')).toBe(null); + expect(model.playWord("123")).toBe(null); + expect(model.playWord("")).toBe(null); }); - it('does not add word to history', () => { + it("does not add word to history", () => { const model = getModel(); const word = getWord(model); - const origPlays = {...model.plays}; + const origPlays = { ...model.plays }; model.playWord(word);