win/lose feedback, mobile kb style

This commit is contained in:
Lynn
2022-01-01 19:14:50 +01:00
parent 1219991921
commit 053242dc0b
4 changed files with 66 additions and 19 deletions

View File

@@ -25,6 +25,19 @@ body {
font-weight: bold; font-weight: bold;
} }
.App {
display: flex;
flex-direction: column;
max-width: 500px;
margin: 0 auto;
justify-content: center;
}
.Game {
display: flex;
flex-direction: column;
}
.Game-keyboard { .Game-keyboard {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -33,22 +46,31 @@ body {
.Game-keyboard-row { .Game-keyboard-row {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: stretch;
} }
.Game-keyboard-button { .Game-keyboard-button {
margin: 2px; margin: 2px;
background-color: #cdcdcd; background-color: #cdcdcd;
padding: 4px; padding: 2px;
text-transform: capitalize; text-transform: capitalize;
border-radius: 4px; border-radius: 4px;
min-width: 25px; min-height: 40px;
display: flex;
flex: 1;
align-items: center;
justify-content: center;
font-size: 20px;
color: inherit; color: inherit;
text-decoration: inherit; text-decoration: inherit;
border: inherit; border: inherit;
cursor: pointer; cursor: pointer;
} }
.Game-keyboard-button-wide {
flex: 2;
}
.Game-keyboard-button:focus { .Game-keyboard-button:focus {
outline: none; outline: none;
} }

View File

@@ -26,20 +26,24 @@ function App() {
<h1>hello wordl</h1> <h1>hello wordl</h1>
<input <input
type="range" type="range"
min="3" min="4"
max="15" max="11"
value={wordLength} value={wordLength}
onChange={(e) => { onChange={(e) => {
setTarget(randomTarget(Number(e.target.value))); const length = Number(e.target.value);
setWordLength(Number(e.target.value)); setTarget(randomTarget(length));
setWordLength(length);
}} }}
></input> ></input>
<div className="App"> <div className="App">
<Game <Game
key={wordLength} key={target}
wordLength={wordLength} wordLength={wordLength}
target={target} target={target}
maxGuesses={6} maxGuesses={6}
restart={() => {
setTarget(randomTarget(wordLength));
}}
/> />
</div> </div>
</> </>

View File

@@ -6,23 +6,31 @@ import { Keyboard } from "./Keyboard";
enum GameState { enum GameState {
Playing, Playing,
Over, Won,
Lost,
} }
interface GameProps { interface GameProps {
target: string; target: string;
wordLength: number; wordLength: number;
maxGuesses: number; maxGuesses: number;
restart: () => void;
} }
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 [hint, setHint] = useState<string>(`${props.wordLength} letters`);
const onKey = (key: string) => { const onKey = (key: string) => {
console.log(key); console.log(key);
if (gameState !== GameState.Playing) return; if (gameState !== GameState.Playing) {
if (key === "Enter") {
props.restart();
}
return;
}
if (guesses.length === props.maxGuesses) return; if (guesses.length === props.maxGuesses) return;
if (/^[a-z]$/.test(key)) { if (/^[a-z]$/.test(key)) {
setCurrentGuess((guess) => (guess + key).slice(0, props.wordLength)); setCurrentGuess((guess) => (guess + key).slice(0, props.wordLength));
@@ -30,15 +38,26 @@ function Game(props: GameProps) {
setCurrentGuess((guess) => guess.slice(0, -1)); setCurrentGuess((guess) => guess.slice(0, -1));
} else if (key === "Enter") { } else if (key === "Enter") {
if (currentGuess.length !== props.wordLength) { if (currentGuess.length !== props.wordLength) {
// TODO show a helpful message setHint("Too short");
return; return;
} }
if (!dictionary.includes(currentGuess)) { if (!dictionary.includes(currentGuess)) {
// TODO show a helpful message setHint("Not a valid word");
return; return;
} }
setGuesses((guesses) => guesses.concat([currentGuess])); setGuesses((guesses) => guesses.concat([currentGuess]));
setCurrentGuess((guess) => ""); setCurrentGuess((guess) => "");
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("");
}
} }
}; };
@@ -46,14 +65,11 @@ function Game(props: GameProps) {
const onKeyDown = (e: KeyboardEvent) => { const onKeyDown = (e: KeyboardEvent) => {
onKey(e.key); onKey(e.key);
}; };
document.addEventListener("keydown", onKeyDown); document.addEventListener("keydown", onKeyDown);
return () => { return () => {
document.removeEventListener("keydown", onKeyDown); document.removeEventListener("keydown", onKeyDown);
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps }, [currentGuess, gameState]);
}, [currentGuess]);
let letterInfo = new Map<string, Clue>(); let letterInfo = new Map<string, Clue>();
const rowDivs = Array(props.maxGuesses) const rowDivs = Array(props.maxGuesses)
@@ -61,7 +77,8 @@ function Game(props: GameProps) {
.map((_, i) => { .map((_, i) => {
const guess = [...guesses, currentGuess][i] ?? ""; const guess = [...guesses, currentGuess][i] ?? "";
const cluedLetters = clue(guess, props.target); const cluedLetters = clue(guess, props.target);
if (i < guesses.length) { const lockedIn = i < guesses.length;
if (lockedIn) {
for (const { clue, letter } of cluedLetters) { for (const { clue, letter } of cluedLetters) {
if (clue === undefined) break; if (clue === undefined) break;
const old = letterInfo.get(letter); const old = letterInfo.get(letter);
@@ -74,7 +91,7 @@ function Game(props: GameProps) {
<Row <Row
key={i} key={i}
wordLength={props.wordLength} wordLength={props.wordLength}
rowState={i < guesses.length ? RowState.LockedIn : RowState.Pending} rowState={lockedIn ? RowState.LockedIn : RowState.Pending}
cluedLetters={cluedLetters} cluedLetters={cluedLetters}
/> />
); );
@@ -83,6 +100,7 @@ function Game(props: GameProps) {
return ( return (
<div className="Game"> <div className="Game">
{rowDivs} {rowDivs}
<p>{hint || `\u00a0`}</p>
<Keyboard letterInfo={letterInfo} onKey={onKey} /> <Keyboard letterInfo={letterInfo} onKey={onKey} />
</div> </div>
); );

View File

@@ -22,6 +22,9 @@ export function Keyboard(props: KeyboardProps) {
if (clue !== undefined) { if (clue !== undefined) {
className += " " + clueClass(clue); className += " " + clueClass(clue);
} }
if (label.length > 1) {
className += " Game-keyboard-button-wide";
}
return ( return (
<div <div
tabIndex={-1} tabIndex={-1}
@@ -31,7 +34,7 @@ export function Keyboard(props: KeyboardProps) {
props.onKey(label); props.onKey(label);
}} }}
> >
{label} {label.replace("Backspace", "⌫")}
</div> </div>
); );
})} })}