A Warning for a Word Already Gone

The already-used hint flashed on a word the player could no longer edit, because the check that decided whether to show it never looked at what phase the game was actually in.

Colliding with the snake’s own body leaves the crashed word on screen through the collision animation on purpose, its collision letter still highlighted, so a player can see exactly what they were mid-word on when they hit the wall. The already-used hint was not supposed to have an opinion about that lingering word at all, but it did: the moment the collision animation finished, the word got pushed onto history, and the hint flashed a warning about a word the player could no longer edit or resubmit.

The check behind the hint compared the word currently on screen against everything already played, with no regard for whether the player could act on that comparison at all. During the wave animation that follows a crash, the word is still visibly there, so the comparison ran, found a match against the entry that had just been recorded, and lit up a warning with nothing left for the player to do about it.

The fix gated the check on the game actually being in a state where typing means anything:

const isPlaying = $derived(game.state.phase === 'playing');
const isAlreadyUsed = $derived(
  isPlaying &&
  currentWord.length >= 2 &&
  game.state.wordHistory.includes(currentWord.toLowerCase())
);

A second derived value already existed for the same phase check elsewhere in the component, controlling whether the clear button showed, so the fix reused it rather than adding a second copy of the same condition. New tests cover the hint appearing for a genuine duplicate typed mid-game, and staying quiet through both the animating phase right after a crash and the game-over phase that follows it.