dim letters that are part of the puzzle; improve uncommon word handling; add example game to about
This commit is contained in:
parent
6fced58fb7
commit
8032a835c9
5 changed files with 136 additions and 21 deletions
|
@ -1,4 +1,6 @@
|
||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import { Clue } from './clue';
|
||||||
|
import { Row, RowState } from './Row';
|
||||||
import { gameName } from "./util";
|
import { gameName } from "./util";
|
||||||
|
|
||||||
export function About() {
|
export function About() {
|
||||||
|
@ -25,9 +27,11 @@ export function About() {
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
Like Wordle & hello wordl, the game has two separate word lists: a
|
Like Wordle & hello wordl, the game has two separate word lists: a
|
||||||
"common" list for secret words, and a broader list including "uncommon"
|
"secret list" containing only reasonably common words, and a larger list of
|
||||||
words for your guesses. The game will stop you from guessing an uncommon
|
words you're allowed to guess. The game will let you know when you've typed
|
||||||
word for your second guess, since it has no chance of being the secret word.
|
a word that doesn't appear in the secret list; you can still guess it if you
|
||||||
|
want, but it can't be the secret word, so try something else if you're on your
|
||||||
|
second guess!
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
There's a new puzzle every day, starting at 6 AM in all timezones.
|
There's a new puzzle every day, starting at 6 AM in all timezones.
|
||||||
|
@ -37,6 +41,99 @@ export function About() {
|
||||||
forced to guess an uncommon word to win!
|
forced to guess an uncommon word to win!
|
||||||
</p>
|
</p>
|
||||||
<hr />
|
<hr />
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={true}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Absent, letter: "b" },
|
||||||
|
{ clue: Clue.Absent, letter: "a" },
|
||||||
|
{ clue: Clue.Elsewhere, letter: "t" },
|
||||||
|
{ clue: Clue.Absent, letter: "c" },
|
||||||
|
{ clue: Clue.Correct, letter: "h" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={true}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Absent, letter: "y" },
|
||||||
|
{ clue: Clue.Correct, letter: "o" },
|
||||||
|
{ clue: Clue.Absent, letter: "u" },
|
||||||
|
{ clue: Clue.Correct, letter: "t" },
|
||||||
|
{ clue: Clue.Correct, letter: "h" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={true}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Absent, letter: "t" },
|
||||||
|
{ clue: Clue.Correct, letter: "o" },
|
||||||
|
{ clue: Clue.Absent, letter: "o" },
|
||||||
|
{ clue: Clue.Correct, letter: "t" },
|
||||||
|
{ clue: Clue.Correct, letter: "h" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
We know the word has an <b class="green-bg">O</b> in the second position
|
||||||
|
and ends with <b class="green-bg">TH</b>. What words could it be?
|
||||||
|
</p>
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={false}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Absent, letter: "n" },
|
||||||
|
{ clue: Clue.Correct, letter: "o" },
|
||||||
|
{ clue: Clue.Correct, letter: "r" },
|
||||||
|
{ clue: Clue.Correct, letter: "t" },
|
||||||
|
{ clue: Clue.Correct, letter: "h" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={false}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Absent, letter: "f" },
|
||||||
|
{ clue: Clue.Correct, letter: "o" },
|
||||||
|
{ clue: Clue.Correct, letter: "r" },
|
||||||
|
{ clue: Clue.Correct, letter: "t" },
|
||||||
|
{ clue: Clue.Correct, letter: "h" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<p style={{ fontSize: "80%" }}>Not quite! The answer could've been <strong>WORTH</strong>.</p>
|
||||||
|
<p>Looks like we didn't cover all our bases. Let's see if we can account for that <b>W</b>:</p>
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={false}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Absent, letter: "s" },
|
||||||
|
{ clue: Clue.Absent, letter: "w" },
|
||||||
|
{ clue: Clue.Elsewhere, letter: "o" },
|
||||||
|
{ clue: Clue.Elsewhere, letter: "r" },
|
||||||
|
{ clue: Clue.Absent, letter: "n" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Row
|
||||||
|
rowState={RowState.LockedIn}
|
||||||
|
wordLength={5}
|
||||||
|
isPartOfPuzzle={false}
|
||||||
|
cluedLetters={[
|
||||||
|
{ clue: Clue.Correct, letter: "f" },
|
||||||
|
{ clue: Clue.Correct, letter: "o" },
|
||||||
|
{ clue: Clue.Correct, letter: "r" },
|
||||||
|
{ clue: Clue.Correct, letter: "t" },
|
||||||
|
{ clue: Clue.Correct, letter: "h" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<p style={{ fontSize: "80%" }}>You won in 2 tries! Great job.</p>
|
||||||
|
<p>There! The game had no choice but to narrow it down to one word.</p>
|
||||||
|
<hr />
|
||||||
<p>
|
<p>
|
||||||
Like hello wordl, this game will be free and ad-free for as long as
|
Like hello wordl, this game will be free and ad-free for as long as
|
||||||
it's online.
|
it's online.
|
||||||
|
|
|
@ -142,18 +142,27 @@ table.Game-rows > tbody {
|
||||||
background-color: rgb(87, 172, 120);
|
background-color: rgb(87, 172, 120);
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
.letter-correct.dim {
|
||||||
|
background-color: rgba(87, 172, 120, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
.letter-elsewhere {
|
.letter-elsewhere {
|
||||||
border: 2px dotted rgba(0, 0, 0, 0.3);
|
border: 2px dotted rgba(0, 0, 0, 0.3);
|
||||||
background-color: #e9c601;
|
background-color: rgb(233, 198, 1);
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
.letter-elsewhere.dim {
|
||||||
|
background-color: rgba(233, 198, 1, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
.letter-absent {
|
.letter-absent {
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
background-color: hsla(190, 12.5%, 50%, 0.6);
|
background-color: hsla(190, 12.5%, 40%, 0.6);
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
.letter-absent.dim {
|
||||||
|
opacity: .75;
|
||||||
|
}
|
||||||
|
|
||||||
body.dark {
|
body.dark {
|
||||||
background-color: #0b3644;
|
background-color: #0b3644;
|
||||||
|
|
|
@ -122,11 +122,21 @@ export function Game(props: GameProps) {
|
||||||
}
|
}
|
||||||
if (guesses.length === MAX_GUESSES) return;
|
if (guesses.length === MAX_GUESSES) return;
|
||||||
if (/^[a-z]$/i.test(key)) {
|
if (/^[a-z]$/i.test(key)) {
|
||||||
|
// We're about to update the current guess, but
|
||||||
|
// we can't rely on the value updating in this render
|
||||||
|
const newGuess = currentGuess + key.toLowerCase();
|
||||||
setCurrentGuess((guess) =>
|
setCurrentGuess((guess) =>
|
||||||
(guess + key.toLowerCase()).slice(0, WORD_LENGTH)
|
(guess + key.toLowerCase()).slice(0, WORD_LENGTH)
|
||||||
);
|
);
|
||||||
tableRef.current?.focus();
|
tableRef.current?.focus();
|
||||||
setHint("");
|
if (newGuess.length === WORD_LENGTH
|
||||||
|
&& !COMMON_WORDS.includes(newGuess)
|
||||||
|
&& UNCOMMON_WORDS.includes(newGuess)
|
||||||
|
) {
|
||||||
|
setHint(`(The word ${newGuess.toUpperCase()} isn't in the secret list)`);
|
||||||
|
} else {
|
||||||
|
setHint("");
|
||||||
|
}
|
||||||
} else if (key === "Backspace") {
|
} else if (key === "Backspace") {
|
||||||
setCurrentGuess((guess) => guess.slice(0, -1));
|
setCurrentGuess((guess) => guess.slice(0, -1));
|
||||||
setHint("");
|
setHint("");
|
||||||
|
@ -135,16 +145,11 @@ export function Game(props: GameProps) {
|
||||||
setHint("Too short");
|
setHint("Too short");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!COMMON_WORDS.includes(currentGuess)) {
|
if (!COMMON_WORDS.includes(currentGuess)
|
||||||
if (!UNCOMMON_WORDS.includes(currentGuess)) {
|
&& !UNCOMMON_WORDS.includes(currentGuess)
|
||||||
setHint("Not a valid word");
|
) {
|
||||||
return;
|
setHint("Not a valid word");
|
||||||
} else if (guesses.length === puzzle.history.length) {
|
return;
|
||||||
setHint("(Uncommon word allowed for first guess)");
|
|
||||||
} else {
|
|
||||||
setHint("(Uncommon word not allowed for second guess)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (const g of guesses) {
|
for (const g of guesses) {
|
||||||
const c = clue(g);
|
const c = clue(g);
|
||||||
|
@ -200,6 +205,7 @@ export function Game(props: GameProps) {
|
||||||
const tableRows = Array(puzzle.history.length + MAX_GUESSES)
|
const tableRows = Array(puzzle.history.length + MAX_GUESSES)
|
||||||
.fill(undefined)
|
.fill(undefined)
|
||||||
.map((_, i) => {
|
.map((_, i) => {
|
||||||
|
const isPartOfPuzzle = i < puzzle.history.length;
|
||||||
const result = [
|
const result = [
|
||||||
...guesses,
|
...guesses,
|
||||||
{ word: currentGuess, matches: [0, 0, 0, 0, 0] },
|
{ word: currentGuess, matches: [0, 0, 0, 0, 0] },
|
||||||
|
@ -227,6 +233,7 @@ export function Game(props: GameProps) {
|
||||||
: RowState.Pending
|
: RowState.Pending
|
||||||
}
|
}
|
||||||
cluedLetters={cluedLetters}
|
cluedLetters={cluedLetters}
|
||||||
|
isPartOfPuzzle={isPartOfPuzzle}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { h } from "preact";
|
import { h, JSX } from "preact";
|
||||||
import { Clue, clueClass, CluedLetter, clueWord } from "./clue";
|
import { Clue, clueClass, CluedLetter, clueWord } from "./clue";
|
||||||
|
|
||||||
export enum RowState {
|
export enum RowState {
|
||||||
|
@ -11,7 +11,8 @@ interface RowProps {
|
||||||
rowState: RowState;
|
rowState: RowState;
|
||||||
wordLength: number;
|
wordLength: number;
|
||||||
cluedLetters: CluedLetter[];
|
cluedLetters: CluedLetter[];
|
||||||
annotation?: string;
|
annotation?: string | JSX.Element;
|
||||||
|
isPartOfPuzzle: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Row(props: RowProps) {
|
export function Row(props: RowProps) {
|
||||||
|
@ -28,7 +29,7 @@ export function Row(props: RowProps) {
|
||||||
return (
|
return (
|
||||||
<td
|
<td
|
||||||
key={i}
|
key={i}
|
||||||
className={letterClass}
|
className={`${letterClass} ${props.isPartOfPuzzle ? "dim" : ""}`}
|
||||||
aria-live={isEditing ? "assertive" : "off"}
|
aria-live={isEditing ? "assertive" : "off"}
|
||||||
aria-label={
|
aria-label={
|
||||||
isLockedIn
|
isLockedIn
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[
|
[
|
||||||
{"history":[{"word":"batch","matches":[0,0,1,0,2]},{"word":"youth","matches":[0,2,0,2,2]},{"word":"tooth","matches":[0,2,0,2,2]}],"seed":0,"solutions":40},
|
{"history":[{"word":"batch","matches":[0,0,1,0,2]},{"word":"youth","matches":[0,2,0,2,2]},{"word":"tooth","matches":[0,2,0,2,2]}],"seed":1,"solutions":40},
|
||||||
{"history":[{"word":"grope","matches":[0,1,0,0,1]},{"word":"teary","matches":[0,1,1,1,0]},{"word":"maker","matches":[0,2,0,2,2]},{"word":"waver","matches":[0,2,0,2,2]},{"word":"baler","matches":[0,2,0,2,2]}],"seed":1,"solutions":29},
|
{"history":[{"word":"batch","matches":[0,0,1,0,2]},{"word":"youth","matches":[0,2,0,2,2]},{"word":"tooth","matches":[0,2,0,2,2]}],"seed":1,"solutions":40},
|
||||||
|
|
||||||
{"history":[{"word":"decal","matches":[0,1,0,0,0]},{"word":"inner","matches":[1,1,0,1,0]},{"word":"ovine","matches":[0,0,2,2,2]}],"seed":2,"solutions":32},
|
{"history":[{"word":"decal","matches":[0,1,0,0,0]},{"word":"inner","matches":[1,1,0,1,0]},{"word":"ovine","matches":[0,0,2,2,2]}],"seed":2,"solutions":32},
|
||||||
{"history":[{"word":"glean","matches":[2,0,1,1,0]},{"word":"grade","matches":[2,2,2,0,2]},{"word":"grace","matches":[2,2,2,0,2]}],"seed":3,"solutions":2},
|
{"history":[{"word":"glean","matches":[2,0,1,1,0]},{"word":"grade","matches":[2,2,2,0,2]},{"word":"grace","matches":[2,2,2,0,2]}],"seed":3,"solutions":2},
|
||||||
{"history":[{"word":"sassy","matches":[0,2,0,0,0]},{"word":"rabbi","matches":[0,2,0,0,0]},{"word":"naval","matches":[1,2,0,0,0]},{"word":"oaken","matches":[0,2,0,0,1]},{"word":"haunt","matches":[0,2,2,2,2]}],"seed":4,"solutions":1},
|
{"history":[{"word":"sassy","matches":[0,2,0,0,0]},{"word":"rabbi","matches":[0,2,0,0,0]},{"word":"naval","matches":[1,2,0,0,0]},{"word":"oaken","matches":[0,2,0,0,1]},{"word":"haunt","matches":[0,2,2,2,2]}],"seed":4,"solutions":1},
|
||||||
|
|
Loading…
Reference in a new issue