Another JavaScript Game: Snake

One of my favorite projects to assign ADDA students is to create a game from scratch using JavaScript. I'm always surprised by the ambition of what they want to build. This semester, two of my students decided to take on snake. So I got to thinking how I would approach the same problem. Once I start down this path its impossible for me to stop. I stuck closely to the original rather than build a game more like slither.io, which is the direction that one of my students went.

No Canvas?

I made one decision that seems odd to me in retrospect. I eschewed the <canvas> and made the game board out of html div elements instead. I did this as a model to my students on what could be done with the tools they already know. When I started I was unsure if it would even work, but I'm pleasantly surprised by the outcome.

Snake Movement

I created an object constructor for the snake.

function Snake() {
  //this is the starting location for the snake
  //first number is x location, second is y
  this.locations = [[25, 25], [24, 25], [23, 25], [22, 25], [21, 25], [20, 25], [19, 25], [18, 25], [17, 25]];
  //hold the direction the snake is moving
  this.dir = {
    x: 1,
    y: 0
  };
  this.food = [];
}

So when I wanted to move the snake, I just added the direction to the current 'head,' and then added that value to the beginning of the array.

Snake.prototype.draw = function() {
  //get current 'head' location
  var x = this.locations[0][0]
  var y = this.locations[0][1];
  //increment by the direction
  var head = [x + this.dir.x, y + this.dir.y];
  
  //add new head to the locations array
  this.locations.unshift(head);
}

To change the tail, I simply just pop out the last element in the locations array.

Snake.prototype.cutTail = function() {
  return this.locations.pop();
}

To make the animation happen, I just add or remove a class to the head and tail div.

Speeding It Up

Originally I just had the snake move at a constant speed, but wanting to keep true the original, I had to have the snake speed up as the score increased.

I had been using a setInterval to call my render function repeatedly, but once the program is started, there is no way to dynamically alter the duration of that function, or at least I couldn't find a way. Instead I created a recursive setTimeout function to achieve this.

function draw(speedFactor, cb){
  var speed = setSpeed(speedFactor);
  console.log(speed);
  cb(function(){
    render();
    draw(score, setTimeout)
  }, speed);
}

speedFactor is the current score. Another function takes the score and uses it to set the speed.

function setSpeed(sFactor){
  var speed = 130 - (sFactor * 10)

  if(speed <= 0){
    speed = 10;
  }

  return speed;
}

Together these two functions allow me to vary the speed, so as the score increases, the interval between firings will decrease.

Conclusion

Even though this project was a distraction others things that I should have been working on, it was a fun to bang out.

If you are interested the project repository is posted here.