AT Mot — a bilingual (EN/FR) daily word game native to the AT Protocol.
0

Configure Feed

Select the types of activity you want to include in your feed.

at trunk 1.5 kB View raw
1import { WORD_LENGTH } from '../config.js'; 2import type { TileState } from './types.js'; 3 4/** 5 * Score a guess against the answer using the standard two-pass rules: 6 * 1. Mark exact matches (correct / green). 7 * 2. For the rest, mark present (yellow) only while unused answer letters of 8 * that value remain — so duplicate letters are handled correctly 9 * (a second guessed 'E' goes grey if the answer has only one 'E' and it 10 * was already matched). 11 * 12 * Both inputs MUST already be normalized (uppercase A–Z, length WORD_LENGTH). 13 */ 14export function scoreGuess(guess: string, answer: string): TileState[] { 15 if (guess.length !== WORD_LENGTH || answer.length !== WORD_LENGTH) { 16 throw new Error(`scoreGuess expects ${WORD_LENGTH}-letter words`); 17 } 18 19 const states: TileState[] = new Array(WORD_LENGTH).fill('absent'); 20 // Count answer letters not yet consumed by a green match. 21 const remaining: Record<string, number> = {}; 22 23 for (let i = 0; i < WORD_LENGTH; i++) { 24 const a = answer[i]!; 25 if (guess[i] === a) { 26 states[i] = 'correct'; 27 } else { 28 remaining[a] = (remaining[a] ?? 0) + 1; 29 } 30 } 31 32 for (let i = 0; i < WORD_LENGTH; i++) { 33 if (states[i] === 'correct') continue; 34 const g = guess[i]!; 35 if ((remaining[g] ?? 0) > 0) { 36 states[i] = 'present'; 37 remaining[g]!--; 38 } 39 } 40 41 return states; 42} 43 44/** True when every tile is correct. */ 45export function isWin(states: TileState[]): boolean { 46 return states.every((s) => s === 'correct'); 47}