All questions

Snake Game

Premium

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.

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.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium