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(); 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 = { empty: 0, absent: 1, present: 2, correct: 3 }; const state = new Map(); 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; };