A simple Snake game built with React. Move the snake to eat bait, grow longer, avoid obstacles and your own tail.
- Classic snake gameplay
- Keyboard controls (arrow keys)
- Bait and obstacles with random positioning
- Score tracking and game over screen
Prerequisites: Node.js (14+) and npm or yarn.
Install dependencies:
npm install
# or
yarnRun the development server:
npm start
# or
yarn startBuild for production:
npm run build
# or
yarn buildRun tests:
npm test- Arrow keys or
W/A/S/Dto move Pto pause (if implemented)- Refresh or restart button to play again
- Guide the snake to eat the bait to score points and grow longer.
- Avoid hitting walls, obstacles, or the snake's own body.
- The game ends on collision; try to beat your high score!
src/– React source filescomponents/– UI components:snake.js,bait.js,obstacle.js,score.js,gameover.js,instructions.jsutils/– helper utilities for random positions and debouncingApp.js– main app component
- Architecture: Single-page React application.
App.jsholds the main game state and loop; presentational components insrc/components/render UI elements. - Data flow:
Appmaintains core state:snake(array of positions),direction,bait,obstacles,score, andstatus(playing,paused,gameover). Child components receive props and emit events (callbacks) for user actions. - Game loop: A timed tick (e.g.,
setInterval) inAppadvances game state each frame: move snake, detect collisions, spawn bait/obstacles, and trigger re-render. - Utilities:
src/utils/provides deterministic helpers: random position generators and debouncing for input handling.
- Grid & Coordinates: Fixed grid with a
GRID_SIZEconstant. Positions are{ x, y }pairs where0 <= x,y < GRID_SIZE. - Snake representation: An array of
Positionobjects (head at index 0). On move: compute the new head by adding a direction delta, unshift it, and pop the tail unless bait was eaten. - Bait & Obstacles: Bait is a single
Position; obstacles is an array ofPosition. UsegetRandomPositionForBait()andgetRandomPositionForObstacle()to generate positions that do not collide with the snake or existing obstacles. - Collision detection: On each tick check in order: wall collisions (if walls are fatal), obstacle collisions, self-collision (head equals any body segment). If collision, set
status = 'gameover'. - Movement & Input: Keyboard handlers update a queued
nextDirectionto avoid 180° reversals. Input may be debounced to prevent overly-fast direction changes. - Key functions / modules:
moveSnake()— advances the snake and handles growthchangeDirection()— validates and sets new directionspawnBait()/spawnObstacles()— pick non-colliding positionscheckCollision()— returns collision type (none / wall / obstacle / self)resetGame()— reinitializes game state
- Timing & performance: Tick rate is configurable (e.g.,
TICK_MS); keep render work minimal and use React state updates sparingly for smooth gameplay. - Testing: Unit tests for movement and collision are in
src/components/snake.test.jsandsrc/components/bait.test.js.
Contributions are welcome. Open issues or submit pull requests for bug fixes and enhancements.