From 8032a835c94b0dfd4e361179171bc02575836039 Mon Sep 17 00:00:00 2001 From: ashastral Date: Mon, 28 Feb 2022 19:30:23 -0800 Subject: [PATCH] dim letters that are part of the puzzle; improve uncommon word handling; add example game to about --- web-optimle/www/src/About.tsx | 103 ++++++++++++++++++++++++++++++- web-optimle/www/src/App.css | 13 +++- web-optimle/www/src/Game.tsx | 29 +++++---- web-optimle/www/src/Row.tsx | 7 ++- web-optimle/www/src/puzzles.json | 5 +- 5 files changed, 136 insertions(+), 21 deletions(-) diff --git a/web-optimle/www/src/About.tsx b/web-optimle/www/src/About.tsx index 02b5183..a509a7e 100644 --- a/web-optimle/www/src/About.tsx +++ b/web-optimle/www/src/About.tsx @@ -1,4 +1,6 @@ import { h } from 'preact'; +import { Clue } from './clue'; +import { Row, RowState } from './Row'; import { gameName } from "./util"; export function About() { @@ -25,9 +27,11 @@ export function About() {

Like Wordle & hello wordl, the game has two separate word lists: a - "common" list for secret words, and a broader list including "uncommon" - words for your guesses. The game will stop you from guessing an uncommon - word for your second guess, since it has no chance of being the secret word. + "secret list" containing only reasonably common words, and a larger list of + words you're allowed to guess. The game will let you know when you've typed + 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!

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!


+ + + +

+ We know the word has an O in the second position + and ends with TH. What words could it be? +

+ + +

Not quite! The answer could've been WORTH.

+

Looks like we didn't cover all our bases. Let's see if we can account for that W:

+ + +

You won in 2 tries! Great job.

+

There! The game had no choice but to narrow it down to one word.

+

Like hello wordl, this game will be free and ad-free for as long as it's online. diff --git a/web-optimle/www/src/App.css b/web-optimle/www/src/App.css index d8d6444..feb2de3 100644 --- a/web-optimle/www/src/App.css +++ b/web-optimle/www/src/App.css @@ -142,18 +142,27 @@ table.Game-rows > tbody { background-color: rgb(87, 172, 120); color: white !important; } +.letter-correct.dim { + background-color: rgba(87, 172, 120, 0.75); +} .letter-elsewhere { border: 2px dotted rgba(0, 0, 0, 0.3); - background-color: #e9c601; + background-color: rgb(233, 198, 1); color: white !important; } +.letter-elsewhere.dim { + background-color: rgba(233, 198, 1, 0.75); +} .letter-absent { 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; } +.letter-absent.dim { + opacity: .75; +} body.dark { background-color: #0b3644; diff --git a/web-optimle/www/src/Game.tsx b/web-optimle/www/src/Game.tsx index 1ec1ec9..b3b9c15 100644 --- a/web-optimle/www/src/Game.tsx +++ b/web-optimle/www/src/Game.tsx @@ -122,11 +122,21 @@ export function Game(props: GameProps) { } if (guesses.length === MAX_GUESSES) return; 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) => (guess + key.toLowerCase()).slice(0, WORD_LENGTH) ); 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") { setCurrentGuess((guess) => guess.slice(0, -1)); setHint(""); @@ -135,16 +145,11 @@ export function Game(props: GameProps) { setHint("Too short"); return; } - if (!COMMON_WORDS.includes(currentGuess)) { - if (!UNCOMMON_WORDS.includes(currentGuess)) { - setHint("Not a valid word"); - return; - } else if (guesses.length === puzzle.history.length) { - setHint("(Uncommon word allowed for first guess)"); - } else { - setHint("(Uncommon word not allowed for second guess)"); - return; - } + if (!COMMON_WORDS.includes(currentGuess) + && !UNCOMMON_WORDS.includes(currentGuess) + ) { + setHint("Not a valid word"); + return; } for (const g of guesses) { const c = clue(g); @@ -200,6 +205,7 @@ export function Game(props: GameProps) { const tableRows = Array(puzzle.history.length + MAX_GUESSES) .fill(undefined) .map((_, i) => { + const isPartOfPuzzle = i < puzzle.history.length; const result = [ ...guesses, { word: currentGuess, matches: [0, 0, 0, 0, 0] }, @@ -227,6 +233,7 @@ export function Game(props: GameProps) { : RowState.Pending } cluedLetters={cluedLetters} + isPartOfPuzzle={isPartOfPuzzle} /> ); }); diff --git a/web-optimle/www/src/Row.tsx b/web-optimle/www/src/Row.tsx index 4ddbf56..636e07f 100644 --- a/web-optimle/www/src/Row.tsx +++ b/web-optimle/www/src/Row.tsx @@ -1,4 +1,4 @@ -import { h } from "preact"; +import { h, JSX } from "preact"; import { Clue, clueClass, CluedLetter, clueWord } from "./clue"; export enum RowState { @@ -11,7 +11,8 @@ interface RowProps { rowState: RowState; wordLength: number; cluedLetters: CluedLetter[]; - annotation?: string; + annotation?: string | JSX.Element; + isPartOfPuzzle: boolean; } export function Row(props: RowProps) { @@ -28,7 +29,7 @@ export function Row(props: RowProps) { return (