diff --git a/src/Game.tsx b/src/Game.tsx index 7ce94db..edd700d 100644 --- a/src/Game.tsx +++ b/src/Game.tsx @@ -4,7 +4,8 @@ import dictionary from "./dictionary.json"; import { Clue, clue, describeClue, violation } from "./clue"; import { Keyboard } from "./Keyboard"; import targetList from "./targets.json"; -import { dictionarySet, pick, resetRng, seed, speak } from "./util"; +import { pick, resetRng, seed, speak, urlParam } from "./util"; +import { decode, encode } from "./base64"; enum GameState { Playing, @@ -20,24 +21,54 @@ interface GameProps { const targets = targetList.slice(0, targetList.indexOf("murky") + 1); // Words no rarer than this one -function randomTarget(wordLength: number) { +function randomTarget(wordLength: number): string { const eligible = targets.filter((word) => word.length === wordLength); return pick(eligible); } +function getChallengeUrl(target: string): string { + return window.location.href.replace( + /(\?.*)?$/, + "?challenge=" + encode(target) + ); +} + +let challengeString = ""; +let challengeError = false; +try { + challengeString = decode(urlParam("challenge") ?? "").toLowerCase(); +} catch (e) { + console.warn(e); + challengeError = true; +} +if (challengeString && !targets.includes(challengeString)) { + challengeString = ""; + challengeError = true; +} + function Game(props: GameProps) { const [gameState, setGameState] = useState(GameState.Playing); const [guesses, setGuesses] = useState([]); const [currentGuess, setCurrentGuess] = useState(""); - const [wordLength, setWordLength] = useState(5); - const [hint, setHint] = useState(`Make your first guess!`); + const [hint, setHint] = useState( + challengeError + ? `Invalid challenge string, playing random game.` + : `Make your first guess!` + ); + const [challenge, setChallenge] = useState(challengeString); + const [wordLength, setWordLength] = useState( + challenge ? challenge.length : 5 + ); const [target, setTarget] = useState(() => { resetRng(); - return randomTarget(wordLength); + return challenge || randomTarget(wordLength); }); const [gameNumber, setGameNumber] = useState(1); - const startNextGame = () => { + if (challenge) { + window.history.replaceState("", "", "/"); + } + setChallenge(""); setTarget(randomTarget(wordLength)); setGuesses([]); setCurrentGuess(""); @@ -87,15 +118,17 @@ function Game(props: GameProps) { } setGuesses((guesses) => guesses.concat([currentGuess])); setCurrentGuess((guess) => ""); + + const gameOver = (verbed: string) => + `You ${verbed}! The answer was ${target.toUpperCase()}. (Enter to ${ + challenge ? "play a random game" : "play again" + })`; + if (currentGuess === target) { - setHint( - `You won! The answer was ${target.toUpperCase()}. (Enter to play again)` - ); + setHint(gameOver("won")); setGameState(GameState.Won); } else if (guesses.length + 1 === props.maxGuesses) { - setHint( - `You lost! The answer was ${target.toUpperCase()}. (Enter to play again)` - ); + setHint(gameOver("lost")); setGameState(GameState.Lost); } else { setHint(""); @@ -162,7 +195,7 @@ function Game(props: GameProps) { id="wordLength" disabled={ gameState === GameState.Playing && - (guesses.length > 0 || currentGuess !== "") + (guesses.length > 0 || currentGuess !== "" || challenge !== "") } value={wordLength} onChange={(e) => { @@ -196,7 +229,21 @@ function Game(props: GameProps) {

{hint || `\u00a0`}

- {seed ? ( + {gameState !== GameState.Playing && !challenge && ( +

+ +

+ )} + {challenge ? ( +
playing a challenge game
+ ) : seed ? (
seed {seed}, length {wordLength}, game {gameNumber}
diff --git a/src/base64.ts b/src/base64.ts new file mode 100644 index 0000000..600d002 --- /dev/null +++ b/src/base64.ts @@ -0,0 +1,11 @@ +export function encode(text: string): string { + return window + .btoa(text) + .replace(/\//g, "_") + .replace(/\+/g, "-") + .replace(/=*$/, ""); +} + +export function decode(text: string): string { + return window.atob(text.replace(/_/g, "/").replace(/-/g, "+")); +} diff --git a/src/util.ts b/src/util.ts index ae11274..3ebe6e2 100644 --- a/src/util.ts +++ b/src/util.ts @@ -13,9 +13,11 @@ function mulberry32(a: number) { }; } -export const seed = Number( - new URLSearchParams(window.location.search).get("seed") -); +export function urlParam(name: string): string | null { + return new URLSearchParams(window.location.search).get(name); +} + +export const seed = Number(urlParam("seed")); const makeRandom = () => (seed ? mulberry32(seed) : () => Math.random()); let random = makeRandom();