hello-wordle/src/Game.tsx

109 wiersze
3.0 KiB
TypeScript
Czysty Zwykły widok Historia

2021-12-31 01:43:09 +00:00
import { useEffect, useState } from "react";
import { Row, RowState } from "./Row";
import dictionary from "./dictionary.json";
2022-01-01 18:35:03 +00:00
import { Clue, clue } from "./clue";
2022-01-01 02:04:48 +00:00
import { Keyboard } from "./Keyboard";
2021-12-31 01:43:09 +00:00
enum GameState {
Playing,
2022-01-01 18:14:50 +00:00
Won,
Lost,
2021-12-31 01:43:09 +00:00
}
interface GameProps {
target: string;
2022-01-01 02:04:48 +00:00
wordLength: number;
maxGuesses: number;
2022-01-01 18:14:50 +00:00
restart: () => void;
2021-12-31 01:43:09 +00:00
}
function Game(props: GameProps) {
const [gameState, setGameState] = useState(GameState.Playing);
const [guesses, setGuesses] = useState<string[]>([]);
const [currentGuess, setCurrentGuess] = useState<string>("");
2022-01-01 18:14:50 +00:00
const [hint, setHint] = useState<string>(`${props.wordLength} letters`);
2022-01-01 02:04:48 +00:00
const onKey = (key: string) => {
2022-01-01 18:14:50 +00:00
if (gameState !== GameState.Playing) {
if (key === "Enter") {
props.restart();
}
return;
}
2022-01-01 02:04:48 +00:00
if (guesses.length === props.maxGuesses) return;
if (/^[a-z]$/.test(key)) {
setCurrentGuess((guess) => (guess + key).slice(0, props.wordLength));
} else if (key === "Backspace") {
setCurrentGuess((guess) => guess.slice(0, -1));
} else if (key === "Enter") {
if (currentGuess.length !== props.wordLength) {
2022-01-01 18:14:50 +00:00
setHint("Too short");
2022-01-01 02:04:48 +00:00
return;
}
if (!dictionary.includes(currentGuess)) {
2022-01-01 18:14:50 +00:00
setHint("Not a valid word");
2022-01-01 02:04:48 +00:00
return;
}
setGuesses((guesses) => guesses.concat([currentGuess]));
setCurrentGuess((guess) => "");
2022-01-01 18:14:50 +00:00
if (currentGuess === props.target) {
setHint("You won! (Enter to play again)");
setGameState(GameState.Won);
} else if (guesses.length + 1 === props.maxGuesses) {
setHint(
`You lost! The answer was ${props.target.toUpperCase()}. (Enter to play again)`
);
setGameState(GameState.Lost);
} else {
setHint("");
}
2022-01-01 02:04:48 +00:00
}
};
2021-12-31 01:43:09 +00:00
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
2022-01-01 02:04:48 +00:00
onKey(e.key);
2021-12-31 01:43:09 +00:00
};
document.addEventListener("keydown", onKeyDown);
return () => {
document.removeEventListener("keydown", onKeyDown);
};
2022-01-01 18:14:50 +00:00
}, [currentGuess, gameState]);
2021-12-31 01:43:09 +00:00
2022-01-01 02:04:48 +00:00
let letterInfo = new Map<string, Clue>();
const rowDivs = Array(props.maxGuesses)
.fill(undefined)
.map((_, i) => {
const guess = [...guesses, currentGuess][i] ?? "";
const cluedLetters = clue(guess, props.target);
2022-01-01 18:14:50 +00:00
const lockedIn = i < guesses.length;
if (lockedIn) {
2022-01-01 02:04:48 +00:00
for (const { clue, letter } of cluedLetters) {
if (clue === undefined) break;
const old = letterInfo.get(letter);
if (old === undefined || clue > old) {
letterInfo.set(letter, clue);
}
}
}
return (
2021-12-31 01:43:09 +00:00
<Row
2022-01-01 02:04:48 +00:00
key={i}
wordLength={props.wordLength}
2022-01-01 18:14:50 +00:00
rowState={lockedIn ? RowState.LockedIn : RowState.Pending}
2022-01-01 02:04:48 +00:00
cluedLetters={cluedLetters}
2021-12-31 01:43:09 +00:00
/>
);
2022-01-01 02:04:48 +00:00
});
2021-12-31 01:43:09 +00:00
2022-01-01 02:04:48 +00:00
return (
<div className="Game">
{rowDivs}
2022-01-01 18:14:50 +00:00
<p>{hint || `\u00a0`}</p>
2022-01-01 02:04:48 +00:00
<Keyboard letterInfo={letterInfo} onKey={onKey} />
</div>
);
2021-12-31 01:43:09 +00:00
}
export default Game;