Build Evil Wordle app
This commit is contained in:
90
src/lib/wordUtils.ts
Normal file
90
src/lib/wordUtils.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { VALID_GUESSES, WORDS_BY_LENGTH } from '../data/words';
|
||||
import type { GuessResult, LetterState } from '../types';
|
||||
|
||||
export const normalizeGuess = (value: string) => value.toLowerCase().replace(/[^a-z]/g, '');
|
||||
|
||||
export const getDateKey = (date = new Date()) => date.toISOString().slice(0, 10);
|
||||
|
||||
const hashString = (value: string) => {
|
||||
let hash = 2166136261;
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
hash ^= value.charCodeAt(index);
|
||||
hash = Math.imul(hash, 16777619);
|
||||
}
|
||||
return hash >>> 0;
|
||||
};
|
||||
|
||||
export const seededIndex = (seed: string, length: number) => hashString(seed) % length;
|
||||
|
||||
export const getWordsByLength = (length: number) => WORDS_BY_LENGTH[length] ?? [];
|
||||
|
||||
export const isValidGuess = (guess: string, length: number) => {
|
||||
const clean = normalizeGuess(guess);
|
||||
return clean.length === length && VALID_GUESSES.has(clean);
|
||||
};
|
||||
|
||||
export const pickDailyWord = (length: number, salt: string) => {
|
||||
const words = getWordsByLength(length);
|
||||
return words[seededIndex(`${getDateKey()}-${salt}-${length}`, words.length)];
|
||||
};
|
||||
|
||||
export const pickSeededWord = (length: number, seed: string) => {
|
||||
const words = getWordsByLength(length);
|
||||
return words[seededIndex(`${seed}-${length}`, words.length)];
|
||||
};
|
||||
|
||||
export const pickRandomWord = (length: number) => {
|
||||
const words = getWordsByLength(length);
|
||||
return words[Math.floor(Math.random() * words.length)];
|
||||
};
|
||||
|
||||
export const getInfiniteLength = (level: number) => {
|
||||
if (level < 3) return 4;
|
||||
if (level < 7) return 5;
|
||||
if (level < 12) return 6;
|
||||
return 7;
|
||||
};
|
||||
|
||||
export const getSuperEvilLength = () => 10 + Math.floor(Math.random() * 6);
|
||||
|
||||
export const evaluateGuess = (guess: string, target: string): GuessResult[] => {
|
||||
const cleanGuess = normalizeGuess(guess);
|
||||
const targetLetters = target.split('');
|
||||
const result: GuessResult[] = cleanGuess.split('').map((letter) => ({ letter, state: 'absent' }));
|
||||
const remaining = new Map<string, number>();
|
||||
|
||||
targetLetters.forEach((letter, index) => {
|
||||
if (cleanGuess[index] === letter) {
|
||||
result[index].state = 'correct';
|
||||
} else {
|
||||
remaining.set(letter, (remaining.get(letter) ?? 0) + 1);
|
||||
}
|
||||
});
|
||||
|
||||
result.forEach((entry, index) => {
|
||||
if (entry.state === 'correct') return;
|
||||
const count = remaining.get(entry.letter) ?? 0;
|
||||
if (count > 0) {
|
||||
result[index].state = 'present';
|
||||
remaining.set(entry.letter, count - 1);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const mergeKeyboardState = (guesses: string[], target: string) => {
|
||||
const rank: Record<LetterState, number> = { empty: 0, absent: 1, present: 2, correct: 3 };
|
||||
const state = new Map<string, LetterState>();
|
||||
|
||||
guesses.forEach((guess) => {
|
||||
evaluateGuess(guess, target).forEach(({ letter, state: letterState }) => {
|
||||
const current = state.get(letter) ?? 'empty';
|
||||
if (rank[letterState] > rank[current]) {
|
||||
state.set(letter, letterState);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return state;
|
||||
};
|
||||
Reference in New Issue
Block a user