Add hard mode
This commit is contained in:
parent
83dc909cc4
commit
d7e2f52f11
71
src/App.css
71
src/App.css
|
@ -5,6 +5,7 @@
|
|||
body {
|
||||
text-align: center;
|
||||
background-color: #eeeeee;
|
||||
transition: 0.3s background-color ease-out;
|
||||
}
|
||||
|
||||
.Row {
|
||||
|
@ -14,7 +15,7 @@ body {
|
|||
|
||||
.Row-letter {
|
||||
margin: 2px;
|
||||
border: 2px solid rgba(0, 0, 0, 0.4);
|
||||
border: 2px solid rgba(128, 128, 128, 0.8);
|
||||
flex: 1;
|
||||
max-width: 40px;
|
||||
height: 40px;
|
||||
|
@ -39,10 +40,14 @@ body {
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
.Game,
|
||||
h1 {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.Game {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
table.Game-rows {
|
||||
|
@ -99,51 +104,29 @@ table.Game-rows > tbody {
|
|||
|
||||
.letter-correct {
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
background-color: rgb(87, 172, 87);
|
||||
color: white;
|
||||
background-color: rgb(87, 172, 120);
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.letter-elsewhere {
|
||||
border: 2px dotted rgba(0, 0, 0, 0.3);
|
||||
background-color: #e9c601;
|
||||
color: white;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.letter-absent {
|
||||
border: 2px solid transparent;
|
||||
background-color: rgb(162, 162, 162);
|
||||
color: white;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background-color: #404040;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
body.dark {
|
||||
background-color: #404040;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.Game-keyboard-button {
|
||||
color: #404040;
|
||||
}
|
||||
|
||||
.Row-letter {
|
||||
border: 2px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.letter-correct {
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.letter-elsewhere {
|
||||
border: 2px dotted rgba(0, 0, 0, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.letter-absent {
|
||||
border: 2px solid transparent;
|
||||
background-color: rgb(142, 142, 142);
|
||||
color: white;
|
||||
}
|
||||
body.dark .Game-keyboard-button {
|
||||
color: #404040;
|
||||
}
|
||||
|
||||
a,
|
||||
|
@ -196,3 +179,23 @@ a:active {
|
|||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.Settings {
|
||||
text-align: left;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.Settings-setting {
|
||||
margin: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Settings-setting input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.Settings-setting label {
|
||||
margin-inline-start: 8px;
|
||||
}
|
||||
|
|
23
src/App.tsx
23
src/App.tsx
|
@ -1,10 +1,13 @@
|
|||
import "./App.css";
|
||||
import { maxGuesses, seed } from "./util";
|
||||
import Game from "./Game";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { About } from "./About";
|
||||
|
||||
function useSetting<T>(key: string, initial: T): [T, (value: T | ((t: T) => T)) => void] {
|
||||
function useSetting<T>(
|
||||
key: string,
|
||||
initial: T
|
||||
): [T, (value: T | ((t: T) => T)) => void] {
|
||||
const [current, setCurrent] = useState<T>(() => {
|
||||
try {
|
||||
const item = window.localStorage.getItem(key);
|
||||
|
@ -26,13 +29,20 @@ function useSetting<T>(key: string, initial: T): [T, (value: T | ((t: T) => T))
|
|||
function App() {
|
||||
const [page, setPage] = useState<"game" | "about" | "settings">("game");
|
||||
const prefersDark =
|
||||
window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
window.matchMedia &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||
const [dark, setDark] = useSetting<boolean>("dark", prefersDark);
|
||||
const [hard, setHard] = useSetting<boolean>("hard", false);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.className = dark ? "dark" : "";
|
||||
}, [dark]);
|
||||
|
||||
return (
|
||||
<div className="App-container">
|
||||
<h1>hello wordl</h1>
|
||||
<h1>
|
||||
<span style={hard ? { color: "#e66" } : {}}>hell</span>o wordl
|
||||
</h1>
|
||||
<div style={{ position: "absolute", right: 5, top: 5 }}>
|
||||
{page !== "game" ? (
|
||||
<a href="#" onClick={() => setPage("game")}>
|
||||
|
@ -63,7 +73,8 @@ function App() {
|
|||
onClick={() =>
|
||||
(document.location = seed
|
||||
? "?"
|
||||
: "?seed=" + new Date().toISOString().replace(/-/g, "").slice(0, 8))
|
||||
: "?seed=" +
|
||||
new Date().toISOString().replace(/-/g, "").slice(0, 8))
|
||||
}
|
||||
>
|
||||
{seed ? "Random" : "Today's"}
|
||||
|
@ -88,7 +99,7 @@ function App() {
|
|||
checked={hard}
|
||||
onChange={() => setHard((x: boolean) => !x)}
|
||||
/>
|
||||
<label htmlFor="hard-setting">Hard mode (must use clues)</label>
|
||||
<label htmlFor="hard-setting">Hard mode (must use all clues)</label>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
16
src/Game.tsx
16
src/Game.tsx
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { Row, RowState } from "./Row";
|
||||
import dictionary from "./dictionary.json";
|
||||
import { Clue, clue, describeClue } from "./clue";
|
||||
import { Clue, clue, describeClue, violation } from "./clue";
|
||||
import { Keyboard } from "./Keyboard";
|
||||
import targetList from "./targets.json";
|
||||
import { dictionarySet, pick, resetRng, seed, speak } from "./util";
|
||||
|
@ -58,6 +58,11 @@ function Game(props: GameProps) {
|
|||
setCurrentGuess((guess) =>
|
||||
(guess + key.toLowerCase()).slice(0, wordLength)
|
||||
);
|
||||
// When typing a guess, make sure a later "Enter" press won't activate a link or button.
|
||||
const active = document.activeElement as HTMLElement;
|
||||
if (active && ["A", "BUTTON"].includes(active.tagName)) {
|
||||
active.blur();
|
||||
}
|
||||
setHint("");
|
||||
} else if (key === "Backspace") {
|
||||
setCurrentGuess((guess) => guess.slice(0, -1));
|
||||
|
@ -71,6 +76,15 @@ function Game(props: GameProps) {
|
|||
setHint("Not a valid word");
|
||||
return;
|
||||
}
|
||||
if (props.hard) {
|
||||
for (const g of guesses) {
|
||||
const feedback = violation(clue(g, target), currentGuess);
|
||||
if (feedback) {
|
||||
setHint(feedback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setGuesses((guesses) => guesses.concat([currentGuess]));
|
||||
setCurrentGuess((guess) => "");
|
||||
if (currentGuess === target) {
|
||||
|
|
24
src/clue.ts
24
src/clue.ts
|
@ -1,3 +1,5 @@
|
|||
import { ordinal } from "./util";
|
||||
|
||||
export enum Clue {
|
||||
Absent,
|
||||
Elsewhere,
|
||||
|
@ -55,3 +57,25 @@ export function describeClue(clue: CluedLetter[]): string {
|
|||
.map(({ letter, clue }) => letter.toUpperCase() + " " + clueWord(clue!))
|
||||
.join(", ");
|
||||
}
|
||||
|
||||
export function violation(
|
||||
clues: CluedLetter[],
|
||||
guess: string
|
||||
): string | undefined {
|
||||
let i = 0;
|
||||
for (const { letter, clue } of clues) {
|
||||
if (clue === Clue.Absent) {
|
||||
// Apparently Wordle doesn't enforce this?
|
||||
// if (guess.includes(letter))
|
||||
// return "Guess can't contain " + letter.toUpperCase();
|
||||
} else if (clue === Clue.Correct) {
|
||||
if (guess[i] !== letter)
|
||||
return ordinal(i + 1) + " letter must be " + letter.toUpperCase();
|
||||
} else if (clue === Clue.Elsewhere) {
|
||||
if (!guess.includes(letter))
|
||||
return "Guess must contain " + letter.toUpperCase();
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -6714,8 +6714,6 @@
|
|||
"reminder",
|
||||
"exodus",
|
||||
"clash",
|
||||
"bobby",
|
||||
"syphilis",
|
||||
"demon",
|
||||
"authorization",
|
||||
"richness",
|
||||
|
@ -20795,7 +20793,6 @@
|
|||
"nicety",
|
||||
"intrepidity",
|
||||
"shopper",
|
||||
"empyema",
|
||||
"whitewash",
|
||||
"uncontrollably",
|
||||
"atelectasis",
|
||||
|
|
|
@ -47,3 +47,7 @@ export function speak(
|
|||
document.body.removeChild(document.getElementById(id)!);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
export function ordinal(n: number): string {
|
||||
return n + ([, "st", "nd", "rd"][(n % 100 >> 3) ^ 1 && n % 10] || "th");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue