UNPKG

@hp4k1h5/terminordle

Version:

> multiplayer [wordle](https://www.powerlanguage.co.uk/wordle/) clone in your terminal

106 lines (94 loc) 3.23 kB
//@ts-strict import { Visibility, Option, Row, ServerMessage } from './lib/structs' import { words, letters } from './util' export function wordToRow(word: string): Array<Option> { return word.split('').map(letter => ({ letter, visibility: Visibility.hidden, })) } export function validateResponse(response: ServerMessage): void { if (!response.content || typeof response.content !== 'string') { throw 'bad guess' } else if (/[^a-z]/i.test(response.content)) { throw 'only a-zA-Z' } else if (response.content.length !== 5) { throw '5 letters only' } else if (!words[response.content.toLocaleLowerCase()]) { throw 'not in wordlist' } } // update guess and alphabet Rows in place // each guess is a new object // alphabet (letters) is a global export function evaluateGuess(guess: Row, answer: Row): void { // update guess Row status guess.forEach((option: Option, i: number) => { const oL = option.letter.toLocaleLowerCase() const aL = answer[i].letter.toLocaleLowerCase() if (oL === aL) { option.visibility = Visibility.revealed } else if (letterExists(guess, i, answer)) { option.visibility = Visibility.exists } else { option.visibility = Visibility.guessed } // update alphabet Row status if (letters[oL] !== Visibility.revealed) letters[oL] = option.visibility }) } // update alphabet when the evaluated guess comes from server export function updateAlphabet(guess: Row): void { guess.forEach((option: Option) => { const oL = option.letter.toLocaleLowerCase() if (letters[oL] !== Visibility.revealed) { letters[oL] = option.visibility } }) } // determine whether to mark a letter exists (yellow) // i.e. whether the letter // - exists somewhere in the answer // - does not exist in that position // - is not already accounted for by previous guess instances of the letter function letterExists(guess: Row, i: number, answer: Row): boolean { const option = guess[i] if (!answer.find(l => l.letter === option.letter)) { return false } // get intersection of letter instances in guess and answer function sameLetterIndices(row: Row): number[] { return row.slice().reduce((a: number[], v, j) => { if (v.letter === option.letter) { a.push(j) } return a }, []) } const sameAnswerLetterIndices = sameLetterIndices(answer) const sameGuessLetterIndices = sameLetterIndices(guess) const intersection = sameAnswerLetterIndices.filter( i => !sameGuessLetterIndices.includes(i), ) // get difference between needed instances of the letter and provided // instances sameGuessLetterIndices.forEach(i => { if (guess[i].visibility === Visibility.exists) { intersection.shift() } }) // there are unaccounted for instances if (intersection.length) { return true } // there are too many instances return false } // this does not check against the answer because in a client-server model the // client never receives the answer, only the evaluated guess export function isCorrect(guess: Row): boolean { if (guess.some(letter => letter.visibility !== Visibility.revealed)) { return false } return true }