Skip to content

Commit

Permalink
adding lots more unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
kiprobinson committed Jan 10, 2022
1 parent b1eaaa3 commit ea05125
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 23 deletions.
16 changes: 2 additions & 14 deletions app/cli/wordle-cheater.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
import CliUtils from "../lib/cli-utils";
import { getWordListStats, getSortedWordList, RateWordCriteria, WordListStats } from "../lib/word-list";
import { getWordListStats, getSortedWordList, RateWordCriteria, WordListStats, getEmptyRateWordCriteria } from "../lib/word-list";
import { updateCriteriaPerResult } from "../lib/wordle-engine";



export const cheatAtWordle = async ():Promise<void> => {
const stats = getWordListStats();
const criteria: Required<RateWordCriteria> = {
correctLetters: [null, null, null, null, null],
requiredLetters: [],
invalidLetters: new Set<string>(),
invalidLettersByPosition: [
new Set<string>(),
new Set<string>(),
new Set<string>(),
new Set<string>(),
new Set<string>(),
],
knownLetterCounts: {},
}
const criteria: Required<RateWordCriteria> = getEmptyRateWordCriteria();

let guessCount = 0;
while(true) {
Expand Down
17 changes: 13 additions & 4 deletions app/lib/word-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,28 @@ export type RateWordCriteria = {
knownLetterCounts?: Record<string, number>;
}

export const getEmptyRateWordCriteria = ():Required<RateWordCriteria> => ({
invalidLetters: new Set(),
invalidLettersByPosition: [new Set(), new Set(), new Set(), new Set(), new Set()],
correctLetters: [null, null, null, null, null],
requiredLetters: [],
knownLetterCounts: {},
});

const VALID_WORD_REGEX = /^[a-z]{5}$/;

/**
* The list of all five-letter English words.
* The list of all five-letter English words. We can pass a different list to the functions
* for unit testing.
*/
export const wordList:string[] = fs.readFileSync('app/resources/word-list.txt').toString()
export const DEFAULT_WORD_LIST:string[] = fs.readFileSync('app/resources/word-list.txt').toString()
.split(/\s+/)
.filter(s => VALID_WORD_REGEX.test(s));

/**
* Parse the word list to calculate frequencies of each character overall and per-character.
*/
export const getWordListStats = ():WordListStats => {
export const getWordListStats = (wordList:string[]=DEFAULT_WORD_LIST):WordListStats => {
const stats:WordListStats = {
overallFrequencies: new FrequencyTable(),
characterFrequencies: [
Expand Down Expand Up @@ -136,7 +145,7 @@ export const rateWord = (word:string, stats:WordListStats, criteria:RateWordCrit
/**
* Gets a sorted word list, evaluating the score for each word.
*/
export const getSortedWordList = (stats:WordListStats, criteria:RateWordCriteria={}):SortedWordList => {
export const getSortedWordList = (stats:WordListStats, criteria:RateWordCriteria={}, wordList:string[]=DEFAULT_WORD_LIST):SortedWordList => {
const list:SortedWordList = wordList.map(word => ({
word,
score: rateWord(word, stats, criteria),
Expand Down
5 changes: 0 additions & 5 deletions app/lib/wordle-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,4 @@ export const updateCriteriaPerResult = (guess: string, result: string, criteria:
for(const letter of multicolorLetters) {
criteria.knownLetterCounts[letter] = arrayCount(nonBlackLetters, letter);
}

//console.log();
//console.log('current criteria:')
//console.log(JsonStringifySets(criteria));
//console.log();
}
131 changes: 131 additions & 0 deletions test/lib/word-list.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { expect } from "chai";
import { getWordListStats, rateWord } from "../../app/lib/word-list";


describe('test word-list.ts methods', () => {
it('getWordListStats', () => {
const stats = getWordListStats(['hello', 'world', 'aloha', 'earth']);

expect(stats.overallFrequencies.count).to.equal(20);
expect(stats.overallFrequencies.getLetterCount('h')).to.equal(3);
expect(stats.overallFrequencies.getLetterCount('e')).to.equal(2);
expect(stats.overallFrequencies.getLetterCount('l')).to.equal(4);
expect(stats.overallFrequencies.getLetterCount('o')).to.equal(3);
expect(stats.overallFrequencies.getLetterCount('w')).to.equal(1);
expect(stats.overallFrequencies.getLetterCount('r')).to.equal(2);
expect(stats.overallFrequencies.getLetterCount('d')).to.equal(1);
expect(stats.overallFrequencies.getLetterCount('a')).to.equal(3);
expect(stats.overallFrequencies.getLetterCount('t')).to.equal(1);
expect(stats.overallFrequencies.getLetterCount('i')).to.equal(0);
expect(stats.overallFrequencies.getLetterPercent('h')).to.equal(15);
expect(stats.overallFrequencies.getLetterPercent('e')).to.equal(10);
expect(stats.overallFrequencies.getLetterPercent('l')).to.equal(20);
expect(stats.overallFrequencies.getLetterPercent('o')).to.equal(15);
expect(stats.overallFrequencies.getLetterPercent('w')).to.equal(5);
expect(stats.overallFrequencies.getLetterPercent('r')).to.equal(10);
expect(stats.overallFrequencies.getLetterPercent('d')).to.equal(5);
expect(stats.overallFrequencies.getLetterPercent('a')).to.equal(15);
expect(stats.overallFrequencies.getLetterPercent('t')).to.equal(5);
expect(stats.overallFrequencies.getLetterPercent('i')).to.equal(0);

expect(stats.characterFrequencies[0].count).to.equal(4);
expect(stats.characterFrequencies[0].getLetterCount('h')).to.equal(1);
expect(stats.characterFrequencies[0].getLetterCount('w')).to.equal(1);
expect(stats.characterFrequencies[0].getLetterCount('a')).to.equal(1);
expect(stats.characterFrequencies[0].getLetterCount('e')).to.equal(1);
expect(stats.characterFrequencies[0].getLetterCount('d')).to.equal(0);
expect(stats.characterFrequencies[0].getLetterPercent('h')).to.equal(25);
expect(stats.characterFrequencies[0].getLetterPercent('w')).to.equal(25);
expect(stats.characterFrequencies[0].getLetterPercent('a')).to.equal(25);
expect(stats.characterFrequencies[0].getLetterPercent('e')).to.equal(25);
expect(stats.characterFrequencies[0].getLetterPercent('d')).to.equal(0);

expect(stats.characterFrequencies[1].count).to.equal(4);
expect(stats.characterFrequencies[1].getLetterCount('e')).to.equal(1);
expect(stats.characterFrequencies[1].getLetterCount('o')).to.equal(1);
expect(stats.characterFrequencies[1].getLetterCount('l')).to.equal(1);
expect(stats.characterFrequencies[1].getLetterCount('a')).to.equal(1);
expect(stats.characterFrequencies[1].getLetterCount('h')).to.equal(0);
expect(stats.characterFrequencies[1].getLetterPercent('e')).to.equal(25);
expect(stats.characterFrequencies[1].getLetterPercent('o')).to.equal(25);
expect(stats.characterFrequencies[1].getLetterPercent('l')).to.equal(25);
expect(stats.characterFrequencies[1].getLetterPercent('a')).to.equal(25);
expect(stats.characterFrequencies[1].getLetterPercent('h')).to.equal(0);

expect(stats.characterFrequencies[2].count).to.equal(4);
expect(stats.characterFrequencies[2].getLetterCount('l')).to.equal(1);
expect(stats.characterFrequencies[2].getLetterCount('r')).to.equal(2);
expect(stats.characterFrequencies[2].getLetterCount('o')).to.equal(1);
expect(stats.characterFrequencies[2].getLetterCount('h')).to.equal(0);
expect(stats.characterFrequencies[2].getLetterPercent('l')).to.equal(25);
expect(stats.characterFrequencies[2].getLetterPercent('r')).to.equal(50);
expect(stats.characterFrequencies[2].getLetterPercent('o')).to.equal(25);
expect(stats.characterFrequencies[2].getLetterPercent('h')).to.equal(0);

expect(stats.characterFrequencies[3].count).to.equal(4);
expect(stats.characterFrequencies[3].getLetterCount('l')).to.equal(2);
expect(stats.characterFrequencies[3].getLetterCount('h')).to.equal(1);
expect(stats.characterFrequencies[3].getLetterCount('t')).to.equal(1);
expect(stats.characterFrequencies[3].getLetterCount('a')).to.equal(0);
expect(stats.characterFrequencies[3].getLetterPercent('l')).to.equal(50);
expect(stats.characterFrequencies[3].getLetterPercent('h')).to.equal(25);
expect(stats.characterFrequencies[3].getLetterPercent('t')).to.equal(25);
expect(stats.characterFrequencies[3].getLetterPercent('a')).to.equal(0);

expect(stats.characterFrequencies[4].count).to.equal(4);
expect(stats.characterFrequencies[4].getLetterCount('o')).to.equal(1);
expect(stats.characterFrequencies[4].getLetterCount('d')).to.equal(1);
expect(stats.characterFrequencies[4].getLetterCount('a')).to.equal(1);
expect(stats.characterFrequencies[4].getLetterCount('h')).to.equal(1);
expect(stats.characterFrequencies[4].getLetterCount('t')).to.equal(0);
expect(stats.characterFrequencies[4].getLetterPercent('o')).to.equal(25);
expect(stats.characterFrequencies[4].getLetterPercent('d')).to.equal(25);
expect(stats.characterFrequencies[4].getLetterPercent('a')).to.equal(25);
expect(stats.characterFrequencies[4].getLetterPercent('h')).to.equal(25);
expect(stats.characterFrequencies[4].getLetterPercent('t')).to.equal(0);
});

it('rateWord', () => {
//a few real words for particular tests, then other fake words to ensure all letters are represented
//const words = ['myths', 'truss', 'tessa', 'sassy', 'thequ', 'ickbr', 'ownfo', 'xjump', 'sover', 'thela', 'zydog'];

const stats = getWordListStats();

//note to self - i'm not trying to test the particular score, just whether it is zero or not
expect(rateWord('myths', stats)).to.be.above(0);

expect(rateWord('myths', stats, {invalidLetters: new Set([])})).to.be.above(0);
expect(rateWord('myths', stats, {invalidLetters: new Set(['m'])})).to.equal(0);
expect(rateWord('myths', stats, {invalidLetters: new Set(['x'])})).to.be.above(0);
expect(rateWord('myths', stats, {invalidLetters: new Set(['x', 'h', 'p'])})).to.equal(0);

expect(rateWord('myths', stats, {invalidLettersByPosition: [ new Set([]), new Set([]), new Set([]), new Set([]), new Set([]) ]})).to.be.above(0);
expect(rateWord('myths', stats, {invalidLettersByPosition: [ new Set(['m']), new Set(['a']), new Set(['b']), new Set(['c']), new Set(['d']) ]})).to.equal(0);
expect(rateWord('myths', stats, {invalidLettersByPosition: [ new Set(['s']), new Set(['m']), new Set(['y']), new Set(['t']), new Set(['h']) ]})).to.be.above(0);

expect(rateWord('myths', stats, {correctLetters: ['m', null, null, null, null]})).to.be.above(0);
expect(rateWord('myths', stats, {correctLetters: [null, null, null, null, 's']})).to.be.above(0);
expect(rateWord('myths', stats, {correctLetters: [null, null, 'y', null, null]})).to.equal(0);
expect(rateWord('myths', stats, {correctLetters: [null, null, null, null, null]})).to.be.above(0);

expect(rateWord('myths', stats, {requiredLetters: []})).to.be.above(0);
expect(rateWord('myths', stats, {requiredLetters: ['e']})).to.equal(0);
expect(rateWord('myths', stats, {requiredLetters: ['t']})).to.be.above(0);
expect(rateWord('myths', stats, {requiredLetters: ['s']})).to.be.above(0);
expect(rateWord('myths', stats, {requiredLetters: ['s', 't']})).to.be.above(0);
expect(rateWord('myths', stats, {requiredLetters: ['s', 't', 's']})).to.equal(0);
expect(rateWord('truss', stats, {requiredLetters: ['s', 't', 's']})).to.be.above(0);
expect(rateWord('truss', stats, {requiredLetters: ['s', 's', 's']})).to.equal(0);
expect(rateWord('sassy', stats, {requiredLetters: ['s', 's', 's']})).to.be.above(0);

expect(rateWord('myths', stats, {knownLetterCounts: {}})).to.be.above(0);
expect(rateWord('myths', stats, {knownLetterCounts: {s:1}})).to.be.above(0);
expect(rateWord('myths', stats, {knownLetterCounts: {s:2}})).to.equal(0);
expect(rateWord('truss', stats, {knownLetterCounts: {s:1}})).to.equal(0);
expect(rateWord('truss', stats, {knownLetterCounts: {s:2}})).to.be.above(0);
expect(rateWord('truss', stats, {knownLetterCounts: {s:3}})).to.equal(0);
expect(rateWord('sassy', stats, {knownLetterCounts: {s:1}})).to.equal(0);
expect(rateWord('sassy', stats, {knownLetterCounts: {s:2}})).to.equal(0);
expect(rateWord('sassy', stats, {knownLetterCounts: {s:3}})).to.be.above(0);
});
});
88 changes: 88 additions & 0 deletions test/lib/wordle-engine.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { expect } from "chai";
import { getEmptyRateWordCriteria } from "../../app/lib/word-list";
import { updateCriteriaPerResult } from "../../app/lib/wordle-engine";


describe('test wordle-engine.ts methods', () => {
it('updateCriteriaPerResult - word is "truss" and we guess "tares" then "tours"', () => {
let criteria = getEmptyRateWordCriteria();
updateCriteriaPerResult('tares', 'gbybg', criteria);
expect(criteria.correctLetters).to.deep.equal(['t', null, null, null, 's']);
expect([...criteria.invalidLetters]).to.deep.equal(['a', 'e']);
expect([...criteria.invalidLettersByPosition[0]]).to.deep.equal([]);
expect([...criteria.invalidLettersByPosition[1]]).to.deep.equal(['a']);
expect([...criteria.invalidLettersByPosition[2]]).to.deep.equal(['r']);
expect([...criteria.invalidLettersByPosition[3]]).to.deep.equal(['e']);
expect([...criteria.invalidLettersByPosition[4]]).to.deep.equal([]);
expect(criteria.knownLetterCounts).to.deep.equal({});
expect(criteria.requiredLetters).to.deep.equal(['t', 'r', 's']);

updateCriteriaPerResult('tours', 'gbgyg', criteria);
expect(criteria.correctLetters).to.deep.equal(['t', null, 'u', null, 's']);
expect([...criteria.invalidLetters]).to.deep.equal(['a', 'e', 'o']);
expect([...criteria.invalidLettersByPosition[0]]).to.deep.equal([]);
expect([...criteria.invalidLettersByPosition[1]]).to.deep.equal(['a', 'o']);
expect([...criteria.invalidLettersByPosition[2]]).to.deep.equal(['r']);
expect([...criteria.invalidLettersByPosition[3]]).to.deep.equal(['e', 'r']);
expect([...criteria.invalidLettersByPosition[4]]).to.deep.equal([]);
expect(criteria.knownLetterCounts).to.deep.equal({});
expect(criteria.requiredLetters).to.deep.equal(['t', 'r', 's', 'u']);
});

//If word is "myths" and we guess "truss" (ybbbg) or "tessa" (ybybb),
it('updateCriteriaPerResult - word is "myths" and we guess "truss"', () => {
let criteria = getEmptyRateWordCriteria();
updateCriteriaPerResult('truss', 'ybbbg', criteria);
expect(criteria.correctLetters).to.deep.equal([null, null, null, null, 's']);
expect([...criteria.invalidLetters]).to.deep.equal(['r', 'u']);
expect([...criteria.invalidLettersByPosition[0]]).to.deep.equal(['t']);
expect([...criteria.invalidLettersByPosition[1]]).to.deep.equal(['r']);
expect([...criteria.invalidLettersByPosition[2]]).to.deep.equal(['u']);
expect([...criteria.invalidLettersByPosition[3]]).to.deep.equal(['s']);
expect([...criteria.invalidLettersByPosition[4]]).to.deep.equal([]);
expect(criteria.knownLetterCounts).to.deep.equal({s:1});
expect(criteria.requiredLetters).to.deep.equal(['t', 's']);
});

it('updateCriteriaPerResult - word is "myths" and we guess "tessa"', () => {
let criteria = getEmptyRateWordCriteria();
updateCriteriaPerResult('tessa', 'ybybb', criteria);
expect(criteria.correctLetters).to.deep.equal([null, null, null, null, null]);
expect([...criteria.invalidLetters]).to.deep.equal(['e', 'a']);
expect([...criteria.invalidLettersByPosition[0]]).to.deep.equal(['t']);
expect([...criteria.invalidLettersByPosition[1]]).to.deep.equal(['e']);
expect([...criteria.invalidLettersByPosition[2]]).to.deep.equal(['s']);
expect([...criteria.invalidLettersByPosition[3]]).to.deep.equal(['s']);
expect([...criteria.invalidLettersByPosition[4]]).to.deep.equal(['a']);
expect(criteria.knownLetterCounts).to.deep.equal({s:1});
expect(criteria.requiredLetters).to.deep.equal(['t', 's']);
});

it('updateCriteriaPerResult - word is "truss" and we guess "tessa"', () => {
let criteria = getEmptyRateWordCriteria();
updateCriteriaPerResult('tessa', 'gbygb', criteria);
expect(criteria.correctLetters).to.deep.equal(['t', null, null, 's', null]);
expect([...criteria.invalidLetters]).to.deep.equal(['e', 'a']);
expect([...criteria.invalidLettersByPosition[0]]).to.deep.equal([]);
expect([...criteria.invalidLettersByPosition[1]]).to.deep.equal(['e']);
expect([...criteria.invalidLettersByPosition[2]]).to.deep.equal(['s']);
expect([...criteria.invalidLettersByPosition[3]]).to.deep.equal([]);
expect([...criteria.invalidLettersByPosition[4]]).to.deep.equal(['a']);
expect(criteria.knownLetterCounts).to.deep.equal({});
expect(criteria.requiredLetters).to.deep.equal(['t', 's', 's']);
});

it('updateCriteriaPerResult - word is "truss" and we guess "sassy"', () => {
let criteria = getEmptyRateWordCriteria();
updateCriteriaPerResult('sassy', 'ybbgb', criteria);
expect(criteria.correctLetters).to.deep.equal([null, null, null, 's', null]);
expect([...criteria.invalidLetters]).to.deep.equal(['a', 'y']);
expect([...criteria.invalidLettersByPosition[0]]).to.deep.equal(['s']);
expect([...criteria.invalidLettersByPosition[1]]).to.deep.equal(['a']);
expect([...criteria.invalidLettersByPosition[2]]).to.deep.equal(['s']);
expect([...criteria.invalidLettersByPosition[3]]).to.deep.equal([]);
expect([...criteria.invalidLettersByPosition[4]]).to.deep.equal(['y']);
expect(criteria.knownLetterCounts).to.deep.equal({s: 2});
expect(criteria.requiredLetters).to.deep.equal(['s', 's']);
});
});

0 comments on commit ea05125

Please sign in to comment.