hello-wordle/src/clue.ts

108 wiersze
2.8 KiB
TypeScript

2022-01-22 21:43:34 +00:00
import { Difficulty, englishNumbers, ordinal } from "./util";
2022-01-17 22:25:44 +00:00
2022-01-01 02:04:48 +00:00
export enum Clue {
Absent,
Elsewhere,
Correct,
}
export interface CluedLetter {
clue?: Clue;
letter: string;
}
export function clue(word: string, target: string): CluedLetter[] {
2022-01-01 18:35:03 +00:00
let elusive: string[] = [];
target.split("").forEach((letter, i) => {
2022-01-01 02:04:48 +00:00
if (word[i] !== letter) {
2022-01-01 18:35:03 +00:00
elusive.push(letter);
2022-01-01 02:04:48 +00:00
}
});
return word.split("").map((letter, i) => {
let j: number;
if (target[i] === letter) {
return { clue: Clue.Correct, letter };
2022-01-01 18:35:03 +00:00
} else if ((j = elusive.indexOf(letter)) > -1) {
// "use it up" so we don't clue at it twice
elusive[j] = "";
2022-01-01 02:04:48 +00:00
return { clue: Clue.Elsewhere, letter };
} else {
return { clue: Clue.Absent, letter };
}
});
}
export function clueClass(clue: Clue): string {
if (clue === Clue.Absent) {
return "letter-absent";
} else if (clue === Clue.Elsewhere) {
return "letter-elsewhere";
} else {
return "letter-correct";
}
}
2022-01-13 23:16:27 +00:00
export function clueWord(clue: Clue): string {
if (clue === Clue.Absent) {
return "no";
} else if (clue === Clue.Elsewhere) {
return "elsewhere";
2022-01-13 23:16:27 +00:00
} else {
return "correct";
}
}
export function describeClue(clue: CluedLetter[]): string {
return clue
.map(({ letter, clue }) => letter.toUpperCase() + " " + clueWord(clue!))
.join(", ");
}
2022-01-17 22:25:44 +00:00
export function violation(
2022-01-22 16:46:14 +00:00
difficulty: Difficulty,
2022-01-17 22:25:44 +00:00
clues: CluedLetter[],
guess: string
): string | undefined {
2022-01-22 16:46:14 +00:00
if (difficulty === Difficulty.Normal) {
return undefined;
}
const ultra = difficulty === Difficulty.UltraHard;
2022-01-17 22:25:44 +00:00
let i = 0;
for (const { letter, clue } of clues) {
const clueCount = clues.filter(
(c) => c.letter === letter && c.clue !== Clue.Absent
).length;
const guessCount = guess.split(letter).length - 1;
const glyph = letter.toUpperCase();
const glyphs = glyph + (clueCount !== 1 ? "s" : "");
2022-01-22 16:46:14 +00:00
const nth = ordinal(i + 1);
// Hard: enforce greens stay in place.
if (clue === Clue.Correct && guess[i] !== letter) {
return nth + " letter must be " + glyph;
}
// Hard: enforce yellows are used.
if (guessCount < clueCount) {
const atLeastN =
clueCount > 1 ? `at least ${englishNumbers[clueCount]} ` : "";
return `Guess must contain ${atLeastN}${glyphs}`;
2022-01-17 22:25:44 +00:00
}
// Ultra Hard: disallow would-be greens.
if (ultra && clue !== Clue.Correct && guess[i] === letter) {
return nth + " letter can't be " + glyph;
}
// Ultra Hard: if the exact amount is known because of an Absent clue, enforce it.
if (ultra && clue === Clue.Absent && guessCount !== clueCount) {
return clueCount === 0
? `Guess can't contain ${glyph}`
: `Guess must contain exactly ${englishNumbers[clueCount]} ${glyphs}`;
}
2022-01-17 22:25:44 +00:00
++i;
}
return undefined;
}