Add challenge button

This commit is contained in:
Lynn
2022-01-18 14:58:23 +01:00
parent 96ba2fd52e
commit 02418ab815
3 changed files with 77 additions and 17 deletions

View File

@@ -4,7 +4,8 @@ import dictionary from "./dictionary.json";
import { Clue, clue, describeClue, violation } from "./clue"; import { Clue, clue, describeClue, violation } from "./clue";
import { Keyboard } from "./Keyboard"; import { Keyboard } from "./Keyboard";
import targetList from "./targets.json"; 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 { enum GameState {
Playing, Playing,
@@ -20,24 +21,54 @@ interface GameProps {
const targets = targetList.slice(0, targetList.indexOf("murky") + 1); // Words no rarer than this one 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); const eligible = targets.filter((word) => word.length === wordLength);
return pick(eligible); 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) { function Game(props: GameProps) {
const [gameState, setGameState] = useState(GameState.Playing); const [gameState, setGameState] = useState(GameState.Playing);
const [guesses, setGuesses] = useState<string[]>([]); const [guesses, setGuesses] = useState<string[]>([]);
const [currentGuess, setCurrentGuess] = useState<string>(""); const [currentGuess, setCurrentGuess] = useState<string>("");
const [wordLength, setWordLength] = useState(5); const [hint, setHint] = useState<string>(
const [hint, setHint] = useState<string>(`Make your first guess!`); challengeError
? `Invalid challenge string, playing random game.`
: `Make your first guess!`
);
const [challenge, setChallenge] = useState<string>(challengeString);
const [wordLength, setWordLength] = useState(
challenge ? challenge.length : 5
);
const [target, setTarget] = useState(() => { const [target, setTarget] = useState(() => {
resetRng(); resetRng();
return randomTarget(wordLength); return challenge || randomTarget(wordLength);
}); });
const [gameNumber, setGameNumber] = useState(1); const [gameNumber, setGameNumber] = useState(1);
const startNextGame = () => { const startNextGame = () => {
if (challenge) {
window.history.replaceState("", "", "/");
}
setChallenge("");
setTarget(randomTarget(wordLength)); setTarget(randomTarget(wordLength));
setGuesses([]); setGuesses([]);
setCurrentGuess(""); setCurrentGuess("");
@@ -87,15 +118,17 @@ function Game(props: GameProps) {
} }
setGuesses((guesses) => guesses.concat([currentGuess])); setGuesses((guesses) => guesses.concat([currentGuess]));
setCurrentGuess((guess) => ""); 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) { if (currentGuess === target) {
setHint( setHint(gameOver("won"));
`You won! The answer was ${target.toUpperCase()}. (Enter to play again)`
);
setGameState(GameState.Won); setGameState(GameState.Won);
} else if (guesses.length + 1 === props.maxGuesses) { } else if (guesses.length + 1 === props.maxGuesses) {
setHint( setHint(gameOver("lost"));
`You lost! The answer was ${target.toUpperCase()}. (Enter to play again)`
);
setGameState(GameState.Lost); setGameState(GameState.Lost);
} else { } else {
setHint(""); setHint("");
@@ -162,7 +195,7 @@ function Game(props: GameProps) {
id="wordLength" id="wordLength"
disabled={ disabled={
gameState === GameState.Playing && gameState === GameState.Playing &&
(guesses.length > 0 || currentGuess !== "") (guesses.length > 0 || currentGuess !== "" || challenge !== "")
} }
value={wordLength} value={wordLength}
onChange={(e) => { onChange={(e) => {
@@ -196,7 +229,21 @@ function Game(props: GameProps) {
</table> </table>
<p role="alert">{hint || `\u00a0`}</p> <p role="alert">{hint || `\u00a0`}</p>
<Keyboard letterInfo={letterInfo} onKey={onKey} /> <Keyboard letterInfo={letterInfo} onKey={onKey} />
{seed ? ( {gameState !== GameState.Playing && !challenge && (
<p>
<button
onClick={() => {
navigator.clipboard.writeText(getChallengeUrl(target));
setHint("Challenge link to clipboard!");
}}
>
Challenge a friend to this word
</button>
</p>
)}
{challenge ? (
<div className="Game-seed-info">playing a challenge game</div>
) : seed ? (
<div className="Game-seed-info"> <div className="Game-seed-info">
seed {seed}, length {wordLength}, game {gameNumber} seed {seed}, length {wordLength}, game {gameNumber}
</div> </div>

11
src/base64.ts Normal file
View File

@@ -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, "+"));
}

View File

@@ -13,9 +13,11 @@ function mulberry32(a: number) {
}; };
} }
export const seed = Number( export function urlParam(name: string): string | null {
new URLSearchParams(window.location.search).get("seed") return new URLSearchParams(window.location.search).get(name);
); }
export const seed = Number(urlParam("seed"));
const makeRandom = () => (seed ? mulberry32(seed) : () => Math.random()); const makeRandom = () => (seed ? mulberry32(seed) : () => Math.random());
let random = makeRandom(); let random = makeRandom();