var canvas = document.getElementById('game');
var context = canvas.getContext('2d');
var root = document.querySelector(':root');
// the canvas width & height, snake x & y, and the apple x & y, all need to be a multiples of the grid size in order for collision detection to work
// (e.g. 16 * 25 = 400)
var grid = 16;
var count = 0;
var score = 0;
// reading last score value
var max = 0;
var snake = {
  x: 160,
  y: 160,
  // snake velocity. moves one grid length every frame in either the x or y direction
  dx: grid,
  dy: 0,
  // keep track of all grids the snake body occupies
  cells: [],
  // length of the snake. grows when eating an apple
  maxCells: 4
};
var apple = {
  x: 320,
  y: 320
};
// get random whole numbers in a specific range
// @see https://stackoverflow.com/a/1527820/2124254
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min)) + min;
}
// game loop
function loop() {
  requestAnimationFrame(loop);
  // slow game loop to 15 fps instead of 60 (60/15 = 4)
  if (++count < 6) {
    return;
  }
  count = 0;
  context.clearRect(0,0,canvas.width,canvas.height);
  // move snake by its velocity
  snake.x += snake.dx;
  snake.y += snake.dy;
  // check if the snake hits the borders
  if (snake.x < 0 || snake.x >= canvas.width || snake.y < 0 || snake.y >= canvas.height) {
    endGame();
    return;
  }
  // keep track of where snake has been. front of the array is always the head
  snake.cells.unshift({x: snake.x, y: snake.y});
  // remove cells as we move away from them
  if (snake.cells.length > snake.maxCells) {
    snake.cells.pop();
  }
  // draw apple
  apple_color = getComputedStyle(root).getPropertyValue('--apple');
  context.fillStyle = apple_color;
  context.fillRect(apple.x, apple.y, grid-1, grid-1);
  // draw snake one cell at a time
  context.fillStyle = getComputedStyle(root).getPropertyValue('--snake');
  snake.cells.forEach(function(cell, index) {
    // drawing 1 px smaller than the grid creates a grid effect in the snake body so you can see how long it is
    context.fillRect(cell.x, cell.y, grid-1, grid-1);
    // snake ate apple
    if (cell.x === apple.x && cell.y === apple.y) {
      snake.maxCells++;
      score+=1;
      // saving score for next playing.
      // localStorage.setItem('score',score);
      // max=score;
      document.getElementById('score').innerHTML=score;
      // canvas is 400x400 which is 25x25 grids
      apple.x = getRandomInt(0, 25) * grid;
      apple.y = getRandomInt(0, 25) * grid;
    }
    // check collision with all cells after this one (modified bubble sort)
    for (var i = index + 1; i < snake.cells.length; i++) {
      // snake occupies the same space as a body part. reset game
      if (cell.x === snake.cells[i].x && cell.y === snake.cells[i].y) {
        endGame();
        return;
      }
    }
  });
}

// Function to end the game
function endGame() {
  if (score > max) {
    max = score;
  }
  snake.x = 160;
  snake.y = 160;
  snake.cells = [];
  snake.maxCells = 4;
  snake.dx = grid;
  snake.dy = 0;
  score = 0;
  document.getElementById('score').innerHTML = score;
  apple.x = getRandomInt(0, 25) * grid;
  apple.y = getRandomInt(0, 25) * grid;
  document.getElementById('high').innerHTML = max;
  document.getElementById('end_msg').innerHTML = "Game Over";
}

// listen to keyboard events to move the snake
document.addEventListener('keydown', function(e) {
  // prevent snake from backtracking on itself by checking that it's
  // not already moving on the same axis (pressing left while moving
  // left won't do anything, and pressing right while moving left
  // shouldn't let you collide with your own body)
  // left arrow key
  if (e.which === 37 && snake.dx === 0) {
    snake.dx = -grid;
    snake.dy = 0;
  }
  // up arrow key
  else if (e.which === 38 && snake.dy === 0) {
    snake.dy = -grid;
    snake.dx = 0;
  }
  // right arrow key
  else if (e.which === 39 && snake.dx === 0) {
    snake.dx = grid;
    snake.dy = 0;
  }
  // down arrow key
  else if (e.which === 40 && snake.dy === 0) {
    snake.dy = grid;
    snake.dx = 0;
  }
});

// start the game
requestAnimationFrame(loop);

function myFunction() {
  document.getElementById('end_msg').innerHTML="";
  alert('Press confirm to continue');
}

// stop playing
$(document).ready(function() {
  $('#btn_stop').click(function() {
    document.getElementById('end_msg').innerHTML="Game stopped";
    setTimeout(myFunction, 1000);
  });
});

The code you provided demonstrates a basic implementation of the classic game Snake using the HTML5 canvas element. Let's break down its functionality and discuss how a web developer could use the concepts in their own projects:

  1. Initializing and Configuring the Game:

    • The code initializes the canvas and its context, sets up variables for grid size, score tracking, and game elements such as the snake and the apple.
    • Web developers can adapt this initialization phase to suit their own game requirements. They can modify variables, add additional game elements, or customize the appearance of the game interface.
  2. Game Loop:

    • The code implements a game loop using the requestAnimationFrame() function to repeatedly update and render the game elements.
    • Developers can learn from this game loop structure to create their own game loops for real-time rendering and interaction in their projects.
  3. Snake Movement and Collision Detection:

    • The code allows the player to control the snake's movement using keyboard events. It prevents the snake from moving in the opposite direction and checks for collision with the game boundaries and the snake's own body.
    • Developers can use this logic as a starting point to implement player controls and collision detection in their own games or interactive applications.
  4. Apple Generation and Scoring:

    • The code generates an apple at random positions on the canvas and updates the score when the snake eats the apple.
    • Developers can adapt this concept to create collectibles or power-ups in their own games. They can define their own rules for generating and scoring based on their game mechanics.
  5. Game Over and Restart:

    • The code provides a function called endGame() that resets the game state and displays a "Game Over" message when the snake collides with a boundary or its own body.
    • Developers can study this function to understand how to handle game over scenarios and implement their own game over logic in their projects.
  6. Additional Features:

    • The code includes additional features like stopping the game on button click and displaying the maximum score achieved.
    • Developers can incorporate similar features into their projects to enhance the user experience and provide additional functionality.

By understanding and adapting the concepts from this code, web developers can create their own variations of the Snake game or incorporate similar mechanics and features into their own games or interactive projects. They can customize the visuals, add more complex gameplay elements, implement different control schemes, or even combine these concepts with other game mechanics to create unique experiences.

Hacks

  1. Implement a Two-Player Snake Game:

    • Challenge: Modify the code to allow for two players to control separate snakes on the same canvas. Implement multiplayer functionality and handle collision detection between the snakes.
    • This challenge can be challenging because it requires managing the state and movement of two separate snakes simultaneously. It involves handling input from multiple players and ensuring fair gameplay mechanics.
  2. Add Obstacles and Power-Ups:

    • Challenge: Extend the game by introducing obstacles (e.g., walls, barriers) and power-ups that affect the snake's behavior or score. Implement collision detection with obstacles and define unique effects for power-ups.
    • This challenge requires adding more complexity to the collision detection logic and introducing new game elements with distinct behaviors. It involves managing the placement and interaction of obstacles and power-ups.
  3. Implement Levels with Increasing Difficulty:

    • Challenge: Design multiple levels with increasing difficulty in terms of snake speed, obstacle density, or other factors. Allow the player to progress through levels and track their progress.
    • This challenge involves designing a level system and managing the progression of difficulty. It requires adjusting the game mechanics dynamically based on the level and providing a satisfying and challenging gameplay experience.
  4. Extra Credit Challenge: Create a Snake AI Opponent:

    • Challenge: Develop an AI-controlled snake opponent that competes against the player. Implement AI algorithms (e.g., pathfinding, decision-making) to control the opponent's movement and behavior.
    • This challenge can be the hardest of the four as it requires understanding and implementing AI algorithms specific to the Snake game. It involves designing the opponent's decision-making process, optimizing the AI's behavior, and providing a challenging opponent for the player.