diff --git a/src/store/selectors.ts b/src/store/selectors.ts index fc2c99bde..26d21cb56 100644 --- a/src/store/selectors.ts +++ b/src/store/selectors.ts @@ -17,6 +17,9 @@ import { LOCAL_STORAGE, SUPPORTED_DICTIONARY_BY_LANG } from '@const'; import { getHasSpecialCharacters } from '@utils/normilzeWord'; +import { getLetterOccuranceInWord } from './utils/getLetterOccuranceInWord'; +import { getKeyboardState } from './utils/getKeyboardState'; + export const selectIsProcessing = (state: RootState) => state.game.isProcessing; export const selectWordToGuess = (state: RootState) => state.game.wordToGuess; export const selectWordToSubmit = (state: RootState) => state.game.wordToSubmit; @@ -94,10 +97,6 @@ const selectPositionLetters = (state: RootState) => state.game.letters.position; export const selectGuesses = (state: RootState) => state.game.guesses; -// hello -> 2 -// https://stackoverflow.com/a/72347965/6743808 - claims is the fastest -const getLetterOccuranceInWord = (letter: string, word: string) => word.length - word.replaceAll(letter, '').length; - const getLetterState = ( letter: string, wordToSubmit: string, @@ -274,29 +273,6 @@ export const selectWordState = (word: string) => createSelector( }, ); -const getIsTextMatchingOrder = (text: string, order: string[]) => { - const { - isMatching, - } = order.reduce((stack, subtext) => { - if (!stack.restString.includes(subtext)) { - return { - restString: '', - isMatching: false, - }; - } - - const [, ...rest] = stack.restString.split(subtext); - stack.restString = rest.join(''); - - return stack; - }, { - restString: text, - isMatching: true, - }); - - return isMatching; -}; - export const selectKeyboardState = createSelector( selectWordToGuess, selectWordToSubmit, @@ -304,118 +280,9 @@ export const selectKeyboardState = createSelector( selectPositionLetters, selectFlatAffixes, (wordToGuess, wordToSubmit, incorrectLetter, positionLetters, flatAffixes) => { - if (!wordToSubmit || !wordToSubmit.replaceAll(' ', '')) { - return { - status: AffixStatus.Unknown, - }; - } - - const uniqueWordLetters = [...(new Set(wordToSubmit.split('')))].filter(letter => letter !== ' '); - - const incorrectTyppedLetters = uniqueWordLetters.filter((uniqueLetter) => { - const isIncorrect = incorrectLetter[uniqueLetter] > 0; - if (!isIncorrect) { - return false; - } - - const isCorrectSometimes = positionLetters[uniqueLetter] > 0; - if (isCorrectSometimes) { - const occurrencesOfLetterInSubmitWord = getLetterOccuranceInWord(uniqueLetter, wordToSubmit); - - return occurrencesOfLetterInSubmitWord > positionLetters[uniqueLetter]; - } - - return true; + return getKeyboardState({ + wordToGuess, wordToSubmit, incorrectLetter, positionLetters, flatAffixes, }); - - const hasIncorrectLetterTyped = incorrectTyppedLetters.length > 0; - if (hasIncorrectLetterTyped) { - return { - status: AffixStatus.Incorrect, - details: incorrectTyppedLetters.join(', '), - }; - } - - // TODO: add info at the end if no special letter typed - const hasWordToGuessSpecialCharacters = wordToGuess && getHasSpecialCharacters(wordToGuess); - const hasWordToSubmitSpecialCharacters = wordToSubmit && getHasSpecialCharacters(wordToSubmit); - const specialCharacterTypedWhenNotNeeded = !hasWordToGuessSpecialCharacters && hasWordToSubmitSpecialCharacters; - if (specialCharacterTypedWhenNotNeeded) { - return { - status: AffixStatus.Incorrect, - details: [], // TODO: add special - }; - } - - const uniqueRequiredLetters = Object.keys(positionLetters); - const allKnownLettersAreTyped = uniqueRequiredLetters.every((uniqueLetter) => { - const occurrencesOfLetterInSubmitWord = getLetterOccuranceInWord(uniqueLetter, wordToSubmit); - - return occurrencesOfLetterInSubmitWord >= positionLetters[uniqueLetter]; - }); - - if (allKnownLettersAreTyped) { - if (flatAffixes) { - const isWrongStart = !wordToSubmit.startsWith(flatAffixes.start); - if (isWrongStart) { - return { - status: AffixStatus.IncorrectStart, - details: flatAffixes.start, - }; - } - - if (flatAffixes.notStart.includes(wordToSubmit[0])) { - return { - status: AffixStatus.IncorrectStart, - details: wordToSubmit[0], - }; - } - - const isWrongEnd = !wordToSubmit.endsWith(flatAffixes.end); - if (isWrongEnd) { - return { - status: AffixStatus.IncorrectEnd, - details: flatAffixes.end, - }; - } - - if (flatAffixes.notEnd.includes(wordToSubmit[wordToSubmit.length - 1])) { - return { - status: AffixStatus.IncorrectEnd, - details: wordToSubmit[wordToSubmit.length - 1], - }; - } - - const wrongMiddles = flatAffixes.middle.filter(flatAffix => !wordToSubmit.includes(flatAffix)); - const isWrongMiddle = wrongMiddles.length > 0; - if (isWrongMiddle) { - return { - status: AffixStatus.IncorrectMiddle, - details: wrongMiddles.join(', '), - }; - } - - if (flatAffixes.correctOrders.length > 0) { - const wrongOrders = flatAffixes.correctOrders.filter(order => !getIsTextMatchingOrder(wordToSubmit, order)); - const isWrongOrder = wrongOrders.length > 0; - if (isWrongOrder) { - return { - status: AffixStatus.IncorrectOrder, - details: wrongOrders.join(', '), - }; - } - } - } - - return { - status: AffixStatus.Correct, - }; - } - - // If not all known letters are typed, we know that the word is incorrect, but we don't display it. - return { - status: AffixStatus.Unknown, - }; }, ); diff --git a/src/store/utils/getKeyboardState.test.ts b/src/store/utils/getKeyboardState.test.ts new file mode 100644 index 000000000..ab9527f5e --- /dev/null +++ b/src/store/utils/getKeyboardState.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from '@jest/globals'; + +// import { AffixStatus } from '@common-types'; + +import { getKeyboardState } from './getKeyboardState'; + +describe('getKeyboardState', () => { + describe('getKeyboardState', () => { + it('should embed date inside number', () => { + expect(getKeyboardState({ + wordToGuess: 'start', + wordToSubmit: 'start', + incorrectLetter: {}, + positionLetters: {}, + flatAffixes: { + start: '', + notStart: [], + middle: [], + correctOrders: [], + notEnd: [], + end: '', + }, + })).toBe({ + status: '', + }); + }); + }); +}); diff --git a/src/store/utils/getKeyboardState.ts b/src/store/utils/getKeyboardState.ts new file mode 100644 index 000000000..d0ca25c0e --- /dev/null +++ b/src/store/utils/getKeyboardState.ts @@ -0,0 +1,155 @@ +import { AffixStatus, FlatAffixes, UsedLetters } from '@common-types'; + +import { getHasSpecialCharacters } from '@utils/normilzeWord'; + +import { getLetterOccuranceInWord } from './getLetterOccuranceInWord'; + +const getIsTextMatchingOrder = (text: string, order: string[]) => { + const { + isMatching, + } = order.reduce((stack, subtext) => { + if (!stack.restString.includes(subtext)) { + return { + restString: '', + isMatching: false, + }; + } + + const [, ...rest] = stack.restString.split(subtext); + stack.restString = rest.join(''); + + return stack; + }, { + restString: text, + isMatching: true, + }); + + return isMatching; +}; + +export const getKeyboardState = ({ + wordToGuess, + wordToSubmit, + incorrectLetter, + positionLetters, + flatAffixes, +}: { + wordToGuess: string, + wordToSubmit: string, + incorrectLetter: UsedLetters, + positionLetters: UsedLetters, + flatAffixes: FlatAffixes, +}) => { + if (!wordToSubmit || !wordToSubmit.replaceAll(' ', '')) { + return { + status: AffixStatus.Unknown, + }; + } + + const uniqueWordLetters = [...(new Set(wordToSubmit.split('')))].filter(letter => letter !== ' '); + + const incorrectTyppedLetters = uniqueWordLetters.filter((uniqueLetter) => { + const isIncorrect = incorrectLetter[uniqueLetter] > 0; + if (!isIncorrect) { + return false; + } + + const isCorrectSometimes = positionLetters[uniqueLetter] > 0; + if (isCorrectSometimes) { + const occurrencesOfLetterInSubmitWord = getLetterOccuranceInWord(uniqueLetter, wordToSubmit); + + return occurrencesOfLetterInSubmitWord > positionLetters[uniqueLetter]; + } + + return true; + }); + + const hasIncorrectLetterTyped = incorrectTyppedLetters.length > 0; + if (hasIncorrectLetterTyped) { + return { + status: AffixStatus.Incorrect, + details: incorrectTyppedLetters.join(', '), + }; + } + + // TODO: add info at the end if no special letter typed + const hasWordToGuessSpecialCharacters = wordToGuess && getHasSpecialCharacters(wordToGuess); + const hasWordToSubmitSpecialCharacters = wordToSubmit && getHasSpecialCharacters(wordToSubmit); + const specialCharacterTypedWhenNotNeeded = !hasWordToGuessSpecialCharacters && hasWordToSubmitSpecialCharacters; + if (specialCharacterTypedWhenNotNeeded) { + return { + status: AffixStatus.Incorrect, + details: [], // TODO: add special + }; + } + + const uniqueRequiredLetters = Object.keys(positionLetters); + const allKnownLettersAreTyped = uniqueRequiredLetters.every((uniqueLetter) => { + const occurrencesOfLetterInSubmitWord = getLetterOccuranceInWord(uniqueLetter, wordToSubmit); + + return occurrencesOfLetterInSubmitWord >= positionLetters[uniqueLetter]; + }); + + if (allKnownLettersAreTyped) { + if (flatAffixes) { + const isWrongStart = !wordToSubmit.startsWith(flatAffixes.start); + if (isWrongStart) { + return { + status: AffixStatus.IncorrectStart, + details: flatAffixes.start, + }; + } + + if (flatAffixes.notStart.includes(wordToSubmit[0])) { + return { + status: AffixStatus.IncorrectStart, + details: wordToSubmit[0], + }; + } + + const isWrongEnd = !wordToSubmit.endsWith(flatAffixes.end); + if (isWrongEnd) { + return { + status: AffixStatus.IncorrectEnd, + details: flatAffixes.end, + }; + } + + if (flatAffixes.notEnd.includes(wordToSubmit[wordToSubmit.length - 1])) { + return { + status: AffixStatus.IncorrectEnd, + details: wordToSubmit[wordToSubmit.length - 1], + }; + } + + const wrongMiddles = flatAffixes.middle.filter(flatAffix => !wordToSubmit.includes(flatAffix)); + const isWrongMiddle = wrongMiddles.length > 0; + if (isWrongMiddle) { + return { + status: AffixStatus.IncorrectMiddle, + details: wrongMiddles.join(', '), + }; + } + + if (flatAffixes.correctOrders.length > 0) { + const wrongOrders = flatAffixes.correctOrders.filter(order => !getIsTextMatchingOrder(wordToSubmit, order)); + const isWrongOrder = wrongOrders.length > 0; + if (isWrongOrder) { + return { + status: AffixStatus.IncorrectOrder, + details: wrongOrders.join(', '), + }; + } + } + } + + return { + status: AffixStatus.Correct, + }; + } + + // If not all known letters are typed, we know that the word is incorrect, but we don't display it. + return { + status: AffixStatus.Unknown, + }; +}; diff --git a/src/store/utils/getLetterOccuranceInWord.ts b/src/store/utils/getLetterOccuranceInWord.ts new file mode 100644 index 000000000..dbda8867e --- /dev/null +++ b/src/store/utils/getLetterOccuranceInWord.ts @@ -0,0 +1,3 @@ +// hello -> 2 +// https://stackoverflow.com/a/72347965/6743808 - claims is the fastest +export const getLetterOccuranceInWord = (letter: string, word: string) => word.length - word.replaceAll(letter, '').length; \ No newline at end of file