diff --git a/src/Game.tsx b/src/Game.tsx index b4298dec1..958a2096e 100644 --- a/src/Game.tsx +++ b/src/Game.tsx @@ -1,10 +1,10 @@ -import { useEffect, useState } from "react"; +import { useEffect, useState, ChangeEvent } from "react"; import { Row, RowState } from "./Row"; import dictionary from "./dictionary.json"; import { Clue, clue } from "./clue"; import { Keyboard } from "./Keyboard"; import targetList from "./targets.json"; -import { dictionarySet, pick, resetRng, seed } from "./util"; +import { pick, resetRng, seed, initExclusions } from "./util"; enum GameState { Playing, @@ -30,7 +30,9 @@ function Game(props: GameProps) { const [currentGuess, setCurrentGuess] = useState(""); const [wordLength, setWordLength] = useState(5); const [hint, setHint] = useState( - `${targets.length.toLocaleString()} possibilities` + `${targets + .filter(({ length }) => length === wordLength) + .length.toLocaleString()} possibilities` ); const [target, setTarget] = useState(() => { resetRng(); @@ -38,16 +40,8 @@ function Game(props: GameProps) { }); const [gameNumber, setGameNumber] = useState(1); const [exclusions, setExclusions] = useState< - Record - >({ - found: ["", "", "", "", ""], - nowhere: [], - 0: [], - 1: [], - 2: [], - 3: [], - 4: [], - }); + Record<"found" | "nowhere" | number, string[]> + >(initExclusions(wordLength)); const startNextGame = () => { setTarget(randomTarget(wordLength)); @@ -56,6 +50,7 @@ function Game(props: GameProps) { setHint(""); setGameState(GameState.Playing); setGameNumber((x) => x + 1); + setExclusions(initExclusions(wordLength)); }; const onKey = (key: string) => { @@ -68,10 +63,8 @@ function Game(props: GameProps) { if (guesses.length === props.maxGuesses) return; if (/^[a-z]$/.test(key)) { setCurrentGuess((guess) => (guess + key).slice(0, wordLength)); - setHint(""); } else if (key === "Backspace") { setCurrentGuess((guess) => guess.slice(0, -1)); - setHint(""); } else if (key === "Enter") { if (currentGuess.length !== wordLength) { setHint("Too short"); @@ -104,33 +97,22 @@ function Game(props: GameProps) { ) .map(({ letter }) => letter); - setExclusions({ - found: currentClue.reduce((agg, cur, index) => { - if (cur.clue === 2) agg.splice(index, 1, cur.letter); - return agg; - }, exclusions.found), - nowhere: [...exclusions.nowhere, ...notFound], - 0: - currentClue[0].clue === 1 - ? [...exclusions[0], currentClue[0].letter] - : exclusions[0], - 1: - currentClue[1].clue === 1 - ? [...exclusions[1], currentClue[1].letter] - : exclusions[1], - 2: - currentClue[2].clue === 1 - ? [...exclusions[2], currentClue[2].letter] - : exclusions[2], - 3: - currentClue[3].clue === 1 - ? [...exclusions[3], currentClue[3].letter] - : exclusions[3], - 4: - currentClue[4].clue === 1 - ? [...exclusions[4], currentClue[4].letter] - : exclusions[4], - }); + setExclusions( + currentClue.reduce( + (agg, { letter, clue }, index) => ({ + ...agg, + [index]: + clue === 1 ? [...exclusions[index], letter] : exclusions[index], + }), + { + found: currentClue.reduce((agg, cur, index) => { + if (cur.clue === 2) agg.splice(index, 1, cur.letter); + return agg; + }, exclusions.found), + nowhere: [...exclusions.nowhere, ...notFound], + } + ) + ); } } }; @@ -141,37 +123,32 @@ function Game(props: GameProps) { useEffect(() => { if (exclusions.nowhere.length === 0) return; - const nowherePattern = new RegExp(`^[^${exclusions.nowhere.join("")}]+$`); - const notHerePattern = new RegExp( - `^${ - exclusions.found[0] || exclusions[0].length - ? `[^${exclusions[0].join("")}]` - : "." - }${ - exclusions.found[1] || exclusions[1].length - ? `[^${exclusions[1].join("")}]` - : "." - }${ - exclusions.found[2] || exclusions[2].length - ? `[^${exclusions[2].join("")}]` - : "." - }${ - exclusions.found[3] || exclusions[3].length - ? `[^${exclusions[3].join("")}]` - : "." - }${ - exclusions.found[4] || exclusions[4].length - ? `[^${exclusions[4].join("")}]` - : "." - }$` - ); - console.log(exclusions, nowherePattern, notHerePattern); + const { found, nowhere, ...rest } = exclusions; + const nowherePattern = `(?=^[^${exclusions.nowhere.join("")}]+$)`; + const somewherePattern = Object.values(rest) + .reduce((agg: string[], cur: string[]) => [...agg, ...cur], []) + .filter( + (letter: string, index: number, array: string[]) => + array.indexOf(letter) === index && !found.includes(letter) + ) + .map((letter: string) => `(?=.*${letter})`) + .join(""); + const byPositionPattern = `(?=^${exclusions.found + .map((foundLetter, index) => { + return ( + foundLetter || + (exclusions[index].length ? `[^${exclusions[index].join("")}]` : ".") + ); + }) + .join("")}$)`; - const possibilityCount = targets.filter( - (word) => nowherePattern.test(word) && notHerePattern.test(word) - ).length; - setHint(`${possibilityCount.toLocaleString()} possibilities`); + const re = new RegExp( + [somewherePattern, nowherePattern, byPositionPattern].join("") + ); + const possibilities = targets.filter((word) => re.test(word)); + setHint(`${possibilities.length.toLocaleString()} possibilities`); + console.log({ exclusions, possibilities }); }, [exclusions]); useEffect(() => { @@ -212,6 +189,19 @@ function Game(props: GameProps) { ); }); + const handleWordLengthChange = (e: ChangeEvent) => { + const length = Number(e.target.value); + resetRng(); + setGameNumber(1); + setGameState(GameState.Playing); + setGuesses([]); + setTarget(randomTarget(length)); + setWordLength(length); + setHint(`${length} letters`); + setExclusions(initExclusions(length)); + (document.activeElement as HTMLElement)?.blur(); + }; + return (
@@ -226,17 +216,7 @@ function Game(props: GameProps) { (guesses.length > 0 || currentGuess !== "") } value={wordLength} - onChange={(e) => { - const length = Number(e.target.value); - resetRng(); - setGameNumber(1); - setGameState(GameState.Playing); - setGuesses([]); - setTarget(randomTarget(length)); - setWordLength(length); - setHint(`${length} letters`); - (document.activeElement as HTMLElement)?.blur(); - }} + onChange={handleWordLengthChange} >