Refactor, "Give up" button
This commit is contained in:
10
src/App.css
10
src/App.css
@@ -134,15 +134,19 @@ a:active {
|
|||||||
color: #cc77ff;
|
color: #cc77ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-option {
|
.Game-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-option input {
|
.Game-options > * + * {
|
||||||
margin-left: 0.5rem;
|
margin-inline-start: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Game-options button {
|
||||||
|
min-width: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.App-footer {
|
.App-footer {
|
||||||
|
|||||||
41
src/App.tsx
41
src/App.tsx
@@ -5,54 +5,17 @@ import Game from "./Game";
|
|||||||
import { names } from "./names";
|
import { names } from "./names";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
const targets = common
|
|
||||||
.slice(0, 20000) // adjust for max target freakiness
|
|
||||||
.filter((word) => dictionarySet.has(word) && !names.has(word));
|
|
||||||
|
|
||||||
function randomTarget(wordLength: number) {
|
|
||||||
const eligible = targets.filter((word) => word.length === wordLength);
|
|
||||||
return pick(eligible);
|
|
||||||
}
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [wordLength, setWordLength] = useState(5);
|
|
||||||
const [target, setTarget] = useState(randomTarget(wordLength));
|
|
||||||
if (target.length !== wordLength) {
|
|
||||||
throw new Error("length mismatch");
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1>hello wordl</h1>
|
<h1>hello wordl</h1>
|
||||||
<footer className="App-footer">
|
<footer className="App-footer">
|
||||||
by <a href="https://twitter.com/chordbug">@chordbug</a>, inspired by{" "}
|
by <a href="https://twitter.com/chordbug">@chordbug</a>, inspired by{" "}
|
||||||
<a href="https://www.powerlanguage.co.uk/wordle/">wordle</a>. report
|
<a href="https://www.powerlanguage.co.uk/wordle/">wordle</a>. report
|
||||||
issues <a href="https://github.com/lynn/hello-wordl">here</a>
|
issues <a href="https://github.com/lynn/hello-wordl/issues">here</a>
|
||||||
</footer>
|
</footer>
|
||||||
<div className="App-option">
|
|
||||||
<label htmlFor="wordLength">Letters:</label>
|
|
||||||
<input
|
|
||||||
type="range"
|
|
||||||
min="4"
|
|
||||||
max="11"
|
|
||||||
id="wordLength"
|
|
||||||
value={wordLength}
|
|
||||||
onChange={(e) => {
|
|
||||||
const length = Number(e.target.value);
|
|
||||||
setTarget(randomTarget(length));
|
|
||||||
setWordLength(length);
|
|
||||||
}}
|
|
||||||
></input>
|
|
||||||
</div>
|
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<Game
|
<Game maxGuesses={6} />
|
||||||
key={target}
|
|
||||||
wordLength={wordLength}
|
|
||||||
target={target}
|
|
||||||
maxGuesses={6}
|
|
||||||
restart={() => {
|
|
||||||
setTarget(randomTarget(wordLength));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
71
src/Game.tsx
71
src/Game.tsx
@@ -3,6 +3,9 @@ import { Row, RowState } from "./Row";
|
|||||||
import dictionary from "./dictionary.json";
|
import dictionary from "./dictionary.json";
|
||||||
import { Clue, clue } from "./clue";
|
import { Clue, clue } from "./clue";
|
||||||
import { Keyboard } from "./Keyboard";
|
import { Keyboard } from "./Keyboard";
|
||||||
|
import common from "./common.json";
|
||||||
|
import { dictionarySet, pick } from "./util";
|
||||||
|
import { names } from "./names";
|
||||||
|
|
||||||
enum GameState {
|
enum GameState {
|
||||||
Playing,
|
Playing,
|
||||||
@@ -11,34 +14,50 @@ enum GameState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface GameProps {
|
interface GameProps {
|
||||||
target: string;
|
|
||||||
wordLength: number;
|
|
||||||
maxGuesses: number;
|
maxGuesses: number;
|
||||||
restart: () => void;
|
}
|
||||||
|
|
||||||
|
const targets = common
|
||||||
|
.slice(0, 20000) // adjust for max target freakiness
|
||||||
|
.filter((word) => dictionarySet.has(word) && !names.has(word));
|
||||||
|
|
||||||
|
function randomTarget(wordLength: number) {
|
||||||
|
const eligible = targets.filter((word) => word.length === wordLength);
|
||||||
|
return pick(eligible);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 [wordLength, setWordLength] = useState(5);
|
||||||
|
const [hint, setHint] = useState<string>(`Make your first guess!`);
|
||||||
|
const [target, setTarget] = useState(randomTarget(wordLength));
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
setTarget(randomTarget(wordLength));
|
||||||
|
setGuesses([]);
|
||||||
|
setCurrentGuess("");
|
||||||
|
setHint("");
|
||||||
|
setGameState(GameState.Playing);
|
||||||
|
};
|
||||||
|
|
||||||
const onKey = (key: string) => {
|
const onKey = (key: string) => {
|
||||||
if (gameState !== GameState.Playing) {
|
if (gameState !== GameState.Playing) {
|
||||||
if (key === "Enter") {
|
if (key === "Enter") {
|
||||||
props.restart();
|
reset();
|
||||||
}
|
}
|
||||||
return;
|
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, wordLength));
|
||||||
setHint("");
|
setHint("");
|
||||||
} else if (key === "Backspace") {
|
} else if (key === "Backspace") {
|
||||||
setCurrentGuess((guess) => guess.slice(0, -1));
|
setCurrentGuess((guess) => guess.slice(0, -1));
|
||||||
setHint("");
|
setHint("");
|
||||||
} else if (key === "Enter") {
|
} else if (key === "Enter") {
|
||||||
if (currentGuess.length !== props.wordLength) {
|
if (currentGuess.length !== wordLength) {
|
||||||
setHint("Too short");
|
setHint("Too short");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -48,12 +67,12 @@ function Game(props: GameProps) {
|
|||||||
}
|
}
|
||||||
setGuesses((guesses) => guesses.concat([currentGuess]));
|
setGuesses((guesses) => guesses.concat([currentGuess]));
|
||||||
setCurrentGuess((guess) => "");
|
setCurrentGuess((guess) => "");
|
||||||
if (currentGuess === props.target) {
|
if (currentGuess === target) {
|
||||||
setHint("You won! (Enter to play again)");
|
setHint("You won! (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(
|
||||||
`You lost! The answer was ${props.target.toUpperCase()}. (Enter to play again)`
|
`You lost! The answer was ${target.toUpperCase()}. (Enter to play again)`
|
||||||
);
|
);
|
||||||
setGameState(GameState.Lost);
|
setGameState(GameState.Lost);
|
||||||
} else {
|
} else {
|
||||||
@@ -79,7 +98,7 @@ function Game(props: GameProps) {
|
|||||||
.fill(undefined)
|
.fill(undefined)
|
||||||
.map((_, i) => {
|
.map((_, i) => {
|
||||||
const guess = [...guesses, currentGuess][i] ?? "";
|
const guess = [...guesses, currentGuess][i] ?? "";
|
||||||
const cluedLetters = clue(guess, props.target);
|
const cluedLetters = clue(guess, target);
|
||||||
const lockedIn = i < guesses.length;
|
const lockedIn = i < guesses.length;
|
||||||
if (lockedIn) {
|
if (lockedIn) {
|
||||||
for (const { clue, letter } of cluedLetters) {
|
for (const { clue, letter } of cluedLetters) {
|
||||||
@@ -93,7 +112,7 @@ function Game(props: GameProps) {
|
|||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
key={i}
|
key={i}
|
||||||
wordLength={props.wordLength}
|
wordLength={wordLength}
|
||||||
rowState={lockedIn ? RowState.LockedIn : RowState.Pending}
|
rowState={lockedIn ? RowState.LockedIn : RowState.Pending}
|
||||||
cluedLetters={cluedLetters}
|
cluedLetters={cluedLetters}
|
||||||
/>
|
/>
|
||||||
@@ -102,6 +121,36 @@ function Game(props: GameProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Game">
|
<div className="Game">
|
||||||
|
<div className="Game-options">
|
||||||
|
<label htmlFor="wordLength">Letters:</label>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="4"
|
||||||
|
max="11"
|
||||||
|
id="wordLength"
|
||||||
|
disabled={guesses.length > 0 || currentGuess !== ""}
|
||||||
|
value={wordLength}
|
||||||
|
onChange={(e) => {
|
||||||
|
const length = Number(e.target.value);
|
||||||
|
setTarget(randomTarget(length));
|
||||||
|
setWordLength(length);
|
||||||
|
setHint(`${length} letters`);
|
||||||
|
}}
|
||||||
|
></input>
|
||||||
|
<button
|
||||||
|
style={{ flex: "0" }}
|
||||||
|
disabled={gameState !== GameState.Playing || guesses.length === 0}
|
||||||
|
onClick={() => {
|
||||||
|
setHint(
|
||||||
|
`The answer was ${target.toUpperCase()}. (Enter to play again)`
|
||||||
|
);
|
||||||
|
setGameState(GameState.Lost);
|
||||||
|
(document.activeElement as HTMLElement)?.blur();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Give up
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{rowDivs}
|
{rowDivs}
|
||||||
<p>{hint || `\u00a0`}</p>
|
<p>{hint || `\u00a0`}</p>
|
||||||
<Keyboard letterInfo={letterInfo} onKey={onKey} />
|
<Keyboard letterInfo={letterInfo} onKey={onKey} />
|
||||||
|
|||||||
Reference in New Issue
Block a user