How To Code Snake 🐍
Tags: javascript, gamedev, programming
Published: 2021-05-15 13:20:50

I wouldn't usually post a blog like this to dev as it's something I usually do for myself, game dev is fun and I get a lot of enjoyment out of it. If you enjoy this blog please let me know and maybe I'll post more here!

[Nokia’s snake](https://theprint.in/features/nokias-snake-the-mobile-game-that-became-an-entire-generations-obsession/462873/#:~:text=Launched%20in%201997%20with%20Nokia,that%20became%20a%20household%20name.)  was launched in 1997 with the Nokia 6110, the catalyst for mobile gaming! The concept of the game snake, however, existed long before the 1997 version. In another blog, I talk about  [How to code Blockade (1976)](https://codeheir.com/2019/03/03/how-to-code-blockade-1976-5/)  which is essentially player vs player snake a good 21 years before!

In this blog, I’m going to go through the step-by-step process of coding the game Snake, as it is commonly prescribed to beginner programmers as  _the_  game to code. However, I wouldn’t personally recommend this game to beginner programmers as Snake certainly has some tough quirks you have to figure out. If you are a new programmer I’d recommend taking a look at  [Top 5 BEST games to code as a beginner!](https://codeheir.com/2019/08/17/top-5-best-games-to-code-as-a-beginner/)  or, if you’ve never coded before at all then take a look at  [How To Code A Game As A Beginner](https://codeheir.com/2021/02/06/how-to-code-a-game-as-a-beginner/).

## Let’s get started coding snake!

So for this blog, I’m going to be using  [https://editor.p5js.org/](https://editor.p5js.org/)  which uses the  [p5.js](https://p5js.org/get-started/)  library which makes using the Canvas a more pleasant experience. All the code that I’m writing for this blog [can be found here]([https://editor.p5js.org/codeheir/sketches/yJPfyIuPy](https://editor.p5js.org/codeheir/sketches/yJPfyIuPy)) so feel free to use that as a reference if you get stuck.

### Making a grid for the snake to live

If you study the game of snake you can kind of see that everything is placed within a hidden grid, let’s create our own grid – which will be removed at the end – so we can keep everything in place and make our lives easier.

```javascript
const GRID_SIZE = 20;

function setup() {
   createCanvas(700, 700);
}

function draw() {
  background(155, 204, 153);
  
  for (let x = 0; x < width; x += width / GRID_SIZE) {
    for (let y = 0; y < height; y += height / GRID_SIZE) {
      	stroke(255);
        strokeWeight(1);
        line(x, 0, x, height);
        line(0, y, width, y);
    }
  }
}
```
Which should give you something like this:

![Green background white grid
](https://i0.wp.com/codeheir.com/wp-content/uploads/2021/05/image.png?resize=630%2C631&ssl=1)

Now we can tinker with  `GRID_SIZE`  to fit our game to our liking at any stage. This is an important variable, the snake's position and the food's position will be based on this.

### Creating the Snake

Let's create a new file called  `snake.js`  which'll contain a class for our snake. Don't forget to add the reference to your  `index.html`  so we can use it:

```
  <body>
    <script src="sketch.js"></script>
    <script src="snake.js"></script> // add this
  </body>
```

#### SNAKE.JS

```javascript
class Snake {
  constructor() {
    this.body = [];
    this.body.push({x: width/2, y: height/2}); // the head of the snake
    this.dir = 1; // 1 = right, 2 = down, 3 = left, 4 = right
  }
  
  draw() {
    fill(0);
    for (let b of this.body) {
      rect(b.x, b.y, width / GRID_SIZE, height / GRID_SIZE)
    }
  }
  
  update() {
    if (this.dir == 1) {
      this.body[0].x += width / GRID_SIZE;  
    } else if (this.dir == 2) {
      this.body[0].y += height / GRID_SIZE;
    } else if (this.dir == 3) {
      this.body[0].x -= width / GRID_SIZE;
    } else if (this.dir == 4) {
      this.body[0].y -= height / GRID_SIZE;
    }
  }
}
```
So we've created a  `body`  for our snake which is an array that'll contain the  `x`  and  `y` locations of the part of the body. We're also adding the head of the snake to the body in the constructor, so when we new up the Snake object.

I'm pre-empting the movement of the snake, I know it's going to be able to move either up down left or right, so if the  `dir`  value is set to 1 then we're going right, if it's set to 2 then we're going down, 3 then we're going left and 4 is up.

We've also got a  `draw`  method which simply draws the rectangles representing the body of the snake.

And finally, the `update`  method which just moves the head of the snake in the direction we're moving. Note, I only move the head of the snake because if I were to move the whole snake in a given direction then it wouldn't look like a snake at all. We're going to need to come back to this method as we will need to update the rest of the body - when we have the rest of the body and not just the head.

#### SKETCH.JS

Back in  `sketch.js`  we need to create the snake object and call our update/draw methods. Also, we'll limit the framerate to 4 to give us that retro feel!

```javascript
const GRID_SIZE = 20;

let snake;
function setup() {
  createCanvas(700, 700);
  snake = new Snake();
  frameRate(4);
}

function draw() {
  background(155, 204, 153);
  for (let x = 0; x < width; x += width / GRID_SIZE) {
    for (let y = 0; y < height; y += height / GRID_SIZE) {
      	stroke(255);
        strokeWeight(1);
        line(x, 0, x, height);
        line(0, y, width, y);
    }
  }
  
  snake.update();
  snake.draw();
}
```
Then when you hit play you should have something like this:

![snake moving to the right](https://i2.wp.com/codeheir.com/wp-content/uploads/2021/05/1.gif?resize=625%2C626&ssl=1)

### Adding key pressed handlers

When we hit the arrow keys we want to change direction, add this function to your  `sketch.js`

```javascript
function keyPressed() {
  if (keyCode === 39 && snake.dir !== 3) {
    snake.dir = 1;
  } else if (keyCode === 40 && snake.dir !== 4) {
    snake.dir = 2;
  } else if (keyCode === 37 && snake.dir !== 1) {
    snake.dir = 3;
  } else if (keyCode === 38 && snake.dir !== 2) {
    snake.dir = 4;
  } 
}
```
So this is saying when we press right and we're not going left change direction to the right, when we press down and we're not going up, go down, etc.

Have a little play:

![snake moving](https://i0.wp.com/codeheir.com/wp-content/uploads/2021/05/2.gif?resize=625%2C626&ssl=1)

### Adding food for the snake

When the snake eats food, it grows, let's feed it.

#### FOOD.JS

Create a new file  `food.js`  and remember to add the reference to this in your  `index.html`.

```javascript
class Food {
  constructor() {
    this.spawn();
  }
  
  spawn() {
    let randX = random(width);
    let randY = random(height);
    this.x = randX - randX % (width / GRID_SIZE);
    this.y = randY - randY % (height / GRID_SIZE)
  }
  
  draw() {
    fill(255, 100, 100);
    rect(this.x, this.y, width / GRID_SIZE, height / GRID_SIZE);
  }
}
```
So we're giving the food a random location in the grid, the code  `randX - randX % (width / GRID_SIZE);`  is just allowing us to align the food within a grid square.

![snake not eating](https://i0.wp.com/codeheir.com/wp-content/uploads/2021/05/3.gif?resize=625%2C626&ssl=1)

We've added the food but not given our snake the means to eat it 😢 Let's give him a mouth.

In our main draw function in  `sketch.js`

```javascript
...
function draw() {
  background(155, 204, 153);
  for (let x = 0; x < width; x += width / GRID_SIZE) {
    for (let y = 0; y < height; y += height / GRID_SIZE) {
      	stroke(255);
        strokeWeight(1);
        line(x, 0, x, height);
        line(0, y, width, y);
    }
  }
  
  snake.update();
    
  if (snake.hasEatenFood()) {   // add this code
    food.spawn();
  }
  
  snake.draw();
  food.draw();

}
```
We've not yet written the  `hasEatenFood`  method so let's add that in  `snake.js`

```javascript
...
hasEatenFood() {
    if (this.body[0].x == food.x && this.body[0].y == food.y) {
      return true;     
    }
  }
```
![snake eating but not growing](https://i2.wp.com/codeheir.com/wp-content/uploads/2021/05/4.gif?resize=625%2C626&ssl=1)

Awesome! Now our snake can eat and not get bigger, jealous? Let's add some code to make our snake grow.

#### SNAKE.JS

So there's a few things here we need to add, here's the whole finished file, I'll explain what's added below:

```javascript
class Snake {
  constructor() {
    this.body = [];
    this.body.push({x: width/2, y: height/2}); // the head of the snake
    this.dir = 1; // 1 = right, 2 = down, 3 = left, 4 = right
    
    this.lastX = width/2;
    this.lastY = height/2;
  }
  
  draw() {
    fill(0);
    for (let b of this.body) {
      rect(b.x, b.y, width / GRID_SIZE, height / GRID_SIZE)
    }
  }
  
  update() {
    this.lastX = this.body[this.body.length-1].x;     // track the last X and Y  
    this.lastY = this.body[this.body.length-1].y;     // so we can put the new body there
    for (let i = this.body.length-1; i >= 1; i--) {
      this.body[i].x = this.body[i-1].x;
      this.body[i].y = this.body[i-1].y;
    }
    
    if (this.dir == 1) {
      this.body[0].x += width / GRID_SIZE;  
    } else if (this.dir == 2) {
      this.body[0].y += height / GRID_SIZE;
    } else if (this.dir == 3) {
      this.body[0].x -= width / GRID_SIZE;
    } else if (this.dir == 4) {
      this.body[0].y -= height / GRID_SIZE;
    }
  }
  
  grow() {
    this.body.push({x: this.lastX, y: this.lastY});
  }
  
  hasEatenFood() {
    if (this.body[0].x == food.x && this.body[0].y == food.y) {
      return true;     
    }
  }
  
}
```
We now track the  `lastX`  and the  `lastY`  of the snake so that when the snake eats that position is basically where the food goes, it effectively goes straight to its butt. I've also added code to update the whole body of the snake, so in the update method you'll see the code:

```javascript
    for (let i = this.body.length-1; i >= 1; i--) {
      this.body[i].x = this.body[i-1].x;
      this.body[i].y = this.body[i-1].y;
    }
```
This is updating each body to be the  `x`  and  `y` of the element before it, remember the head is at index 0.

And we've also added the  `grow()`  method which simply appends to the array with the new body.

![snake now eating and growing
](https://i1.wp.com/codeheir.com/wp-content/uploads/2021/05/5.gif?resize=625%2C626&ssl=1)

### Adding hit detection

We want to make sure that the snake doesn't bump into itself, and if it does then we need to restart the game.

I've added a new method  `hitDetection()`  that gets called in the  `update()`  of the  `snake`  class:

```javascript
  hitDetection() {
    for (let i = 1; i < this.body.length; i++) {
      if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) {
        this.spawn();
      }
    }
  }
```

So this simply checks to see if the head of the snake hits any other part of its body. I've also moved the constructor code into a  `spawn()`  method the same as the  `food`  class, here's all the code:

```javascript
class Snake {
  constructor() {
    this.spawn();
  }
  
  // added this
  spawn() {
    this.body = [];
    this.body.push({x: width/2, y: height/2}); 
    this.dir = 1; 
    this.lastX = width/2;
    this.lastY = height/2;
  }
  
  draw() {
    fill(0);
    for (let b of this.body) {
      rect(b.x, b.y, width / GRID_SIZE, height / GRID_SIZE)
    }
  }
  
  update() {
    this.hitDetection();
    
    this.lastX = this.body[this.body.length-1].x;   
    this.lastY = this.body[this.body.length-1].y;     
    for (let i = this.body.length-1; i >= 1; i--) {
      this.body[i].x = this.body[i-1].x;
      this.body[i].y = this.body[i-1].y;
    }
    
    if (this.dir == 1) {
      this.body[0].x += width / GRID_SIZE;  
    } else if (this.dir == 2) {
      this.body[0].y += height / GRID_SIZE;
    } else if (this.dir == 3) {
      this.body[0].x -= width / GRID_SIZE;
    } else if (this.dir == 4) {
      this.body[0].y -= height / GRID_SIZE;
    }
  }
  
  // added this
  hitDetection() {
    for (let i = 1; i < this.body.length; i++) {
      if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) {
        this.spawn();
      }
    }
  }
  
  grow() {
    this.body.push({x: this.lastX, y: this.lastY});
  }
  
  hasEatenFood() {
    if (this.body[0].x == food.x && this.body[0].y == food.y) {
      return true;     
    }
  }
  
}
```
![snake now eating and growing
](https://i2.wp.com/codeheir.com/wp-content/uploads/2021/05/6.gif?resize=625%2C626&ssl=1)

### Losing the grid

Next, let's remove the code we added for our grid, it was simply there to make sure everything was kept in line!

So your draw function in  `sketch.js`  should look like this:

```javascript
function draw() {
  background(155, 204, 153);
  /**
  let x = 0; x < width; x += width / GRID_SIZE) {
  for (let y = 0; y < height; y += height / GRID_SIZE) {
      stroke(255);
      strokeWeight(1);
      line(x, 0, x, height);
      line(0, y, width, y);
    }
  }
  */
  snake.update();
    
  if (snake.hasEatenFood()) {
    food.spawn();
    snake.grow();
  }
  stroke(155, 204, 153); // add this
  snake.draw();
  food.draw();

}
```

![the final result of the snake game](https://i0.wp.com/codeheir.com/wp-content/uploads/2021/05/7.gif?resize=625%2C626&ssl=1)

## What's next?

The most complicated parts of Snake are done, but there are still some things we need to tweak. The best way to learn is by doing, so I challenge you to continue on with the coding and see if you can complete the following tasks. Please let me know if you manage any/all of them, I'd really appreciate that!

1.  When the snake hits the edge of the map the game should restart or the snake should come out the other side of the wall
2.  Add a scoring system and display the number (Could just be the snakes  `body`  length)
3.  Make sure the food doesn't spawn on the snake's body.
4.  Start the snake off already with a body size of 5


I hope you've enjoyed this blog, if you do by some miracle enjoy my blabbering then head over to my blogging site at [codeheir.com](codeheir.com) where I write weekly blogs about whatever in the world of programming has my attention!