Skip to the solution
HardMachine Coding14 min read

Build the Snake Game

290 views

The problem

Question presented to candidate: "Build the classic Snake game in the browser. The snake moves one cell per tick on a grid, arrow keys steer it, eating food makes it longer and scores a point, and hitting a wall or itself ends the game. Add start, pause and restart."

Requirements:

  • A 12 by 12 grid, a snake of length 3 moving right, one piece of food on a free cell.
  • A fixed tick (about 150 ms): the snake moves exactly one cell per tick, independent of how often the UI re-renders.
  • Arrow keys (and WASD) steer; pressing the direct opposite direction is ignored, and two quick presses within one tick must never reverse the snake into itself.
  • Eating grows the snake by one and places new food on a free cell; walls and the body end the game.
  • Space or a button pauses and resumes; Restart starts a new game; on-screen buttons for touch.

What a strong answer should cover:

  • Rules as a pure step(game) function, separate from rendering and from the timer, with the food RNG injected (seeded) so the game is reproducible.
  • A game loop with one interval that exists only while running, cleaned up on pause, game over and unmount.
  • A direction queue validated against the last queued direction, consumed one per tick, which is what blocks the fast double-press 180-degree bug.
  • Collision rules, including that the tail cell is free on a non-eating tick.
  • Keyboard handling on window with preventDefault for arrows, and a note on setInterval versus a requestAnimationFrame accumulator.

Clarifying questions expected:

  • "Do walls kill the snake, or does it wrap around to the other side?"
  • "Should the speed increase as the score grows?"
  • "Do you need touch controls or just the keyboard?"

Code / implementation expected: Yes. A playable game with the rules in a plain module and the loop and input wired in the framework of your choice.

  • React
  • Game
  • Keyboard

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

14 min read · 9 sections

React solution

Every file of the working solution. Open it in the playground to run and change it.

ReactComplete solution
import { useEffect, useState } from "react";
import Board from "./src/components/Board";
import {
  KEY_TO_DIR,
  TICK_MS,
  createGame,
  step,
  togglePause,
  turn,
} from "./src/utils/snake";

const LABEL = {
  ready: "Press an arrow key or Start",
  running: "Running",
  paused: "Paused",
  over: "Game over",
  won: "You filled the board!",
};

export default function App() {
  const [game, setGame] = useState(() => createGame(1));
  const running = game.status === "running";

  // The game loop: one fixed-rate interval, alive only while running.
  // A functional update means the interval never reads a stale game.
  useEffect(() => {
    if (!running) return;
    const id = setInterval(() => setGame(step), TICK_MS);
    return () => clearInterval(id);
  }, [running]);

  // One window listener for the whole lifetime of the component.
  useEffect(() => {
    const onKey = (e) => {
      const dir = KEY_TO_DIR[e.key];
      if (dir) {
        e.preventDefault(); // stop arrow keys from scrolling the page
        setGame((g) => turn(g, dir));
      } else if (e.key === " ") {
        e.preventDefault();
        setGame(togglePause);
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  const over = game.status === "over" || game.status === "won";
  return (
    <main className="snake">
      <h1>Snake</h1>
      <div className="bar">
        <span data-testid="score">
          Score: {game.score} · Length: {game.snake.length}
        </span>
        {over ? (
          <button
            type="button"
            onClick={() => setGame((g) => createGame(g.seed + 1))}
          >
            Restart
          </button>
        ) : (
          <button type="button" onClick={() => setGame(togglePause)}>
            {running ? "Pause" : "Start"}
          </button>
        )}
      </div>
      <Board snake={game.snake} food={game.food} />
      <p className="status" role="status" data-testid="status">
        {LABEL[game.status]}
        {over ? " · Final score " + game.score : ""}
      </p>
      <div className="pad">
        {["up", "left", "down", "right"].map((d) => (
          <button
            key={d}
            type="button"
            className={d}
            aria-label={d}
            onClick={() => setGame((g) => turn(g, d))}
          >
            {{ up: "▲", left: "◀", down: "▼", right: "▶" }[d]}
          </button>
        ))}
      </div>
    </main>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 100 of 107 in Machine coding

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextHard

Build Nested Comments with Collapsible Threads & Voting

Continue