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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dependencies": {
"core-js": "^3.8.0",
"package.json": "^2.0.1",
"vorpal": "^1.12.0"
}
}
121 changes: 111 additions & 10 deletions src/adagrams.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,116 @@
export const drawLetters = () => {
// Implement this method for wave 1
const letterMap = {
'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 usesAvailableLetters = (input, lettersInHand) => {
// Implement this method for wave 2
const old_score_chart = {
"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
};

export const scoreWord = (word) => {
// Implement this method for wave 3
};
export class Adagrams{

Choose a reason for hiding this comment

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

nice use of OOP!

constructor(){
this.letterPool = [];
for (let alphabet in letterMap){
let times = letterMap[alphabet];
for (let i=0; i<times; ++i)

Choose a reason for hiding this comment

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

ahh, this is how I think about setting up the pool, with the appropriate stats for picking a random letter with a given frequency distribution for each letter, too!

this.letterPool.push(alphabet);
}
this.score_chart = new Map(Object.entries(old_score_chart));
}

export const highestScoreFrom = (words) => {
// Implement this method for wave 4
};
drawLetters(){
const letterBank = [];
let length = this.letterPool.length;

while (letterBank.length < 10){

Choose a reason for hiding this comment

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

This loops makes sense, and I don't think you really have to worry about it here, but when I see
while loops like this, I think about adding some other kind of guard condition- like limiting the
number of attempts, for example-- to catch a possible infinite loop if there weren't enough letters
to be picked.

let index = Math.floor(Math.random()*length);
letterBank.push(this.letterPool[index]);
length --;
[this.letterPool[index], this.letterPool[length]] = [this.letterPool[length],this.letterPool[index]];
}
return letterBank;
};

usesAvailableLetters(input, lettersInHand){
const letterCount = new Map();
for (const letter of lettersInHand){
let count = letterCount.get(letter);
if (count === undefined){
count = 0;
}
count ++;
letterCount.set(letter, count);
}

for (const cha of input){

Choose a reason for hiding this comment

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

I might like to see something like 'inputLetter'-- I think 'cha' is short for 'character', here? ('character' may be a reserved word in javascript-- I'd have to double check)

let cur_count = letterCount.get(cha);
if (cur_count === undefined || cur_count === 0){
return false;
}
cur_count --;
letterCount.set(cha, cur_count);
}
return true;

};

scoreWord(word){
let totalScore = 0;
for (let cha of word){
cha = cha.toUpperCase();
let score = this.score_chart.get(cha);
totalScore += score;
}
if (word.length > 6){
totalScore += 8;
}
return totalScore;
};

highestScoreFrom(words){
let cur_winner;
let cur_score = 0;

for (const word of words){
let score = this.scoreWord(word);
if (score > cur_score){
cur_winner = word;
cur_score = score;
}
else if (score == cur_score){
if (cur_winner.length === 10){
continue;
}
else if (word.length === 10 || word.length < cur_winner.length){
cur_winner = word;
}
}
}
return {word: cur_winner, score: cur_score};
};
}
56 changes: 28 additions & 28 deletions test/adagrams.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
drawLetters,
usesAvailableLetters,
scoreWord,
highestScoreFrom,
Adagrams
} from "adagrams";

const LETTER_POOL = {
Expand Down Expand Up @@ -34,16 +31,19 @@ const LETTER_POOL = {
Z: 1,
};

const adagrams = new Adagrams();


describe("Adagrams", () => {
describe("drawLetters", () => {
it("draws ten letters from the letter pool", () => {
const drawn = drawLetters();
const drawn = adagrams.drawLetters();

expect(drawn).toHaveLength(10);
});

it("returns an array, and each item is a single-letter string", () => {
const drawn = drawLetters();
const drawn = adagrams.drawLetters();

expect(Array.isArray(drawn)).toBe(true);
drawn.forEach((l) => {
Expand All @@ -53,7 +53,7 @@ describe("Adagrams", () => {

it("does not draw a letter too many times", () => {
for (let i = 0; i < 1000; i++) {
const drawn = drawLetters();
const drawn = adagrams.drawLetters();
const letter_freq = {};
for (let letter of drawn) {
if (letter in letter_freq) {
Expand All @@ -75,31 +75,31 @@ describe("Adagrams", () => {
const drawn = ["D", "O", "G", "X", "X", "X", "X", "X", "X", "X"];
const word = "DOG";

const isValid = usesAvailableLetters(word, drawn);
const isValid = adagrams.usesAvailableLetters(word, drawn);
expect(isValid).toBe(true);
});

it("returns false when word contains letters not in the drawn letters", () => {
const drawn = ["D", "O", "X", "X", "X", "X", "X", "X", "X", "X"];
const word = "DOG";

const isValid = usesAvailableLetters(word, drawn);
const isValid = adagrams.usesAvailableLetters(word, drawn);
expect(isValid).toBe(false);
});

it("returns false when word contains repeated letters more than in the drawn letters", () => {
const drawn = ["D", "O", "G", "X", "X", "X", "X", "X", "X", "X"];
const word = "GOOD";

const isValid = usesAvailableLetters(word, drawn);
const isValid = adagrams.usesAvailableLetters(word, drawn);
expect(isValid).toBe(false);
});
});

describe("scoreWord", () => {
const expectScores = (wordScores) => {
Object.entries(wordScores).forEach(([word, score]) => {
expect(scoreWord(word)).toBe(score);
expect(adagrams.scoreWord(word)).toBe(score);
});
};

Expand All @@ -120,7 +120,7 @@ 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,24 +133,24 @@ 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 correct = { word: "XXXX", score: adagrams.scoreWord("XXXX") };

expect(highestScoreFrom(words)).toEqual(correct);
expect(adagrams.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 correct = { word: "XXXX", score: adagrams.scoreWord("XXXX") };

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

describe("in case of tied score", () => {
const expectTie = (words) => {
const scores = words.map((word) => scoreWord(word));
const scores = words.map((word) => adagrams.scoreWord(word));
const highScore = scores.reduce((h, s) => (h < s ? s : h), 0);
const tiedWords = scores.filter((s) => s == highScore);

Expand All @@ -162,37 +162,37 @@ describe("Adagrams", () => {
const words = ["AAAAAAAAAA", "BBBBBB"];
const correct = {
word: "AAAAAAAAAA",
score: scoreWord("AAAAAAAAAA"),
score: adagrams.scoreWord("AAAAAAAAAA"),
};
expectTie(words);

expect(highestScoreFrom(words)).toEqual(correct);
expect(highestScoreFrom(words.reverse())).toEqual(correct);
expect(adagrams.highestScoreFrom(words)).toEqual(correct);
expect(adagrams.highestScoreFrom(words.reverse())).toEqual(correct);
});

it("selects the word with fewer letters when neither are 10 letters", () => {
const words = ["MMMM", "WWW"];
const correct = { word: "WWW", score: scoreWord("WWW") };
const correct = { word: "WWW", score: adagrams.scoreWord("WWW") };
expectTie(words);

expect(highestScoreFrom(words)).toEqual(correct);
expect(highestScoreFrom(words.reverse())).toEqual(correct);
expect(adagrams.highestScoreFrom(words)).toEqual(correct);
expect(adagrams.highestScoreFrom(words.reverse())).toEqual(correct);
});

it("selects the first word when both have same length", () => {
const words = ["AAAAAAAAAA", "EEEEEEEEEE"];
const first = {
word: "AAAAAAAAAA",
score: scoreWord("AAAAAAAAAA"),
score: adagrams.scoreWord("AAAAAAAAAA"),
};
const second = {
word: "EEEEEEEEEE",
score: scoreWord("EEEEEEEEEE"),
score: adagrams.scoreWord("EEEEEEEEEE"),
};
expectTie(words);

expect(highestScoreFrom(words)).toEqual(first);
expect(highestScoreFrom(words.reverse())).toEqual(second);
expect(adagrams.highestScoreFrom(words)).toEqual(first);
expect(adagrams.highestScoreFrom(words.reverse())).toEqual(second);
});
});
});
Expand Down
Loading