Snake GameLoading saved progress…

Snake Game

Build the classic Snake game as a single React component. A snake crawls a 12x12 grid one cell per tick; arrow keys steer it; eating food grows it and scores a point; running into a wall or your own body ends the game. It's a small game, but it forces two things most timers get wrong: a loop that reads the latest state, and a clean teardown.

What you'll build

The starter App.tsx renders the board in its resting state — a 3-cell snake, one piece of food, score 0, and a Start button — with no logic. Make it playable:

  1. Hold the game in state. snake (array of {x, y}, head first), food, score, running, and over, all in useState.
  2. Run a loop. On Start, setInterval(step, 150). Each step computes newHead = head + dir and either ends the game (wall or self), grows (on food), or moves (add head, drop tail).
  3. Steer with the keyboard. A window keydown listener maps arrow keys to a direction — but never one that reverses straight into the neck.
  4. Read the latest direction. The tick and the guard must read the current direction, so keep it in a ref, not a captured variable.
  5. Clean up. Clear the interval on game over and in the effect cleanup.

Examples

  • Load: the snake sits mid-board facing right, food to its upper-right, score 0. Nothing moves until you press Start.
  • Press Start: the snake advances one cell every 150ms. Steer with the arrow keys.
  • Eat the food: the snake grows one cell, the score ticks up, and new food appears on a random empty cell.
  • Hit a wall or yourself: the loop stops and a Game Over overlay covers the board. Restart resets everything.

Behavioral contract

  • Use a 12x12 board, a 150ms tick, the fixed starting snake [(6,6), (5,6), (4,6)], starting food (9,4), and a rightward starting heading.
  • The initial game is paused. Start and Restart both reset the game state, heading, and queued turn before starting exactly one interval.
  • On an empty cell, add the new head and remove the tail. On food, keep the tail, add exactly 1 point, and choose new food from unoccupied cells only.
  • A wall collision or overlap with any current snake cell stops the interval and shows Game Over. Opposite-direction input is ignored; other keys do nothing.
  • Arrow keys steer only while the game is running and must not scroll the page. Clear the interval on game over, restart, and teardown.

Accessibility contract

  • Keep Start/Restart as a native button with a truthful visible label.
  • Expose the visual cell matrix as one named 12-by-12 board graphic; mark its 144 decorative cells hidden from assistive technology.
  • Announce score changes politely and maintain a separate polite status message for ready, running, and game-over states, including keyboard instructions and the final score.
  • Do not rely on green/red cells alone: the accessible board name, score, status, and Game Over text carry the same essential state.

Notes

  • The tick must read the latest direction. A keydown sets the direction; the interval, created once, must see that change — a value captured in the interval closure goes stale. Hold the direction in a ref.
  • No reversing. A left turn while moving right would run the head into the neck instantly — reject any direction opposite the current heading.
  • Grow vs move is one line apart. Both add the new head; growing keeps the tail, moving drops it with pop.
  • Always clear the interval. On game over and on unmount, or a stray loop keeps ticking after the component is gone.
WE’RE LISTENING

Help make UIReady better

Found a bug or have an idea? Tell us about it.

Up to 3 files · 5 MiB each · JPG, PNG, WebP, MP4, WebM, MP3, M4A or WAV