diff --git a/src/App.tsx b/src/App.tsx index ed9a490..41175ae 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef } from "react"; import "./App.css"; -import useAppState from "./useAppState"; +import useAppState, { normalizeWord } from "./useAppState"; function App() { const [state, dispatch] = useAppState(); @@ -20,14 +20,21 @@ function App() { type: "load-data", wordPack: text .split("\n") - .map((word) => - word.toLowerCase().trim().replaceAll(/\s+/g, " "), - ) + .map((word) => normalizeWord(word)) .filter(Boolean), }), - 3000, + 0, ); }); + + fetch("https://unpkg.com/naught-words@1.2.0/en.json") + .then((response) => response.json()) + .then((jsonObj) => { + dispatch({ + type: "load-banned-words", + bannedWords: jsonObj.map((word: string) => normalizeWord(word)), + }); + }); }, [dispatch]); // Handle the focus of the buttons / input box, even when clicked off @@ -76,7 +83,6 @@ function App() { > Begin new game -
{JSON.stringify(state, null, 2)}
);
}
@@ -108,7 +114,6 @@ function App() {
Correct Guesses: {state.numWordsGuessed} || Skipped words:{" "}
{state.numWordsSkipped}
- {JSON.stringify(state, null, 2)}
);
}
@@ -131,7 +136,6 @@ function App() {
>
Play again
- {JSON.stringify(state, null, 2)}
);
}
diff --git a/src/useAppState.ts b/src/useAppState.ts
index dac2372..734aa9d 100644
--- a/src/useAppState.ts
+++ b/src/useAppState.ts
@@ -4,6 +4,7 @@ type State = Readonly<
| {
phase: "pre-game";
wordPack: readonly string[] | null;
+ bannedWords: readonly string[] | null;
}
| {
phase: "in-game";
@@ -13,6 +14,7 @@ type State = Readonly<
numWordsGuessed: number;
numWordsSkipped: number;
wordPack: readonly string[] | null;
+ bannedWords: readonly string[] | null;
}
| {
phase: "post-game";
@@ -20,6 +22,7 @@ type State = Readonly<
numWordsGuessed: number;
numWordsSkipped: number;
wordPack: readonly string[] | null;
+ bannedWords: readonly string[] | null;
}
>;
@@ -31,6 +34,7 @@ type Round = Readonly<{
type Action =
| { type: "load-data"; wordPack: readonly string[] }
+ | { type: "load-banned-words"; bannedWords: readonly string[] }
| { type: "start-game" }
| { type: "end-game" }
| { type: "skip-word" }
@@ -42,6 +46,7 @@ function getInitialState(): State {
return {
phase: "pre-game",
wordPack: null,
+ bannedWords: null,
};
}
@@ -50,20 +55,33 @@ function getRandomWord(state: State): string {
return words[Math.floor(Math.random() * words.length)];
}
-function normalizeWord(word: string): string {
- return word.toLowerCase().trim().replaceAll(/\s+/g, " ");
+export function normalizeWord(word: string): string {
+ return word.toUpperCase().trim().replaceAll(/\s+/g, " ");
}
-function scrambleWord(word: string): string {
+function scrambleWord(word: string, bannedWords: readonly string[] | null): string {
if (word.length < 2) return word; // Shouldn't matter in this game, but works as a safety net
let res = word;
- while (res === word) {
+ let attempts = 0;
+ while (res === word && attempts < 10) {
+ // Scramble the word
const wordArray = word.split("");
for (let i = wordArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[wordArray[i], wordArray[j]] = [wordArray[j], wordArray[i]];
}
res = wordArray.join("");
+ attempts++;
+
+ // Confirm there's no naughty words in the scramble.
+ if (bannedWords) {
+ for (const bannedWord of bannedWords) {
+ if (res.includes(bannedWord)) {
+ res = word; // Reset to original word
+ break;
+ }
+ }
+ }
}
return res;
}
@@ -72,7 +90,7 @@ function startNewRound(state: State): Round {
const base = getRandomWord(state);
return {
goal: base,
- scrambledWord: scrambleWord(base),
+ scrambledWord: scrambleWord(base, state.bannedWords),
wasGuessed: false,
};
}
@@ -97,6 +115,7 @@ function reducer(state: State, action: Action): State {
numWordsGuessed: 0,
numWordsSkipped: 0,
wordPack: state.wordPack,
+ bannedWords: state.bannedWords,
};
case "end-game":
if (state.phase !== "in-game") break;
@@ -106,6 +125,7 @@ function reducer(state: State, action: Action): State {
numWordsGuessed: state.numWordsGuessed,
numWordsSkipped: state.numWordsSkipped,
wordPack: state.wordPack,
+ bannedWords: state.bannedWords,
};
case "skip-word":
if (state.phase !== "in-game") break;