Showing the snake where it's about to go

A ghost-path overlay that simulates each keystroke before it happens, a collision rule that took two attempts to get right, and the interactive demo it made redundant.

The interactive how-to-play demo explains snake movement next to the board, on a fixed scripted game, before the player ever types a real word. The word preview overlay explains it on the board, live, for the word the player is actually typing: a ghost path traced cell by cell as each letter lands, so the consequence of a word is visible before Submit is pressed rather than after.

Walking the snake before it moves#

computePreviewPath takes a candidate word and the player’s current snake and simulates the whole thing character by character: the head advances, the tail drops unless growth is pending, and only then is the new head checked against the updated body. The alternative, checking each projected step against a static snapshot of the snake’s current body, was rejected because it produces false warnings on paths that pass near the tail: by the time the projected head would arrive at a cell the tail currently occupies, the tail will already have moved on. ADR-0003 settled on the full simulation instead, matching SnakeManager’s own movement logic step for step.

Showing a red collision cell in this case would mislead the player into avoiding a safe word, which is worse than showing no preview at all.

Where the collision mark actually goes#

The first version placed a collision cell at the position the head would have entered next. For a body collision that’s a real, in-bounds cell, and it looks fine. For a wall collision it isn’t: the position sits outside the grid, and the browser places an invalid CSS grid coordinate wherever it likes, so the warning could appear anywhere on the board instead of at the wall the player actually hit.

ADR-0004 moved the collision cell back one step, to the last position the head actually reached, for both kinds of collision:

export type PreviewCell =
	| { position: Position; kind: 'step' | 'turn' | 'reversal'; letter: string }
	| { position: Position; kind: 'collision'; letter: string; collisionSide: Direction };

collisionSide carries the direction the snake was attempting when it died, so the renderer can draw a thick border on that side of the cell instead of guessing at an off-grid position. Once a path ends in a collision, the whole projected path switches to the same amber-dashed warning style rather than leaving only the last cell marked, so the player reads the entire word as dangerous, not just its final letter.

Teaching less as players learn more#

A preview that never turns off eventually becomes noise for a player who no longer needs it. ADR-0005 fades the overlay out by a completed-games count kept in localStorage, daily challenge completions only, with a matching board hint along the top or bottom edge of the board carrying the same message in words: an intro prompt before the first word, a warning that preview is about to end, then a countdown with a dismiss button that jumps straight to seasoned status.

The demo it replaces#

With the board itself now explaining consequence as it happens, the scripted how-to-play demo had nothing left to teach that the live game didn’t already show, so it came out: the overlay component, its state store, the auto-show wiring on page load, and the settings keys tracking whether a player had seen it, cleaned out of returning players’ localStorage on their next visit rather than left behind as dead state. Between the ghost path and the board hint, a new player now learns the rules by watching their own words land rather than by watching someone else’s scripted ones first.