Lesson 10

To make the tail follow the head, we're going to assign the direction of the segment further forward in snake to the current segment when it moves. This way all segments follow the same path!

Let's define a new function called segmentFurtherForwardThan which takes a segment index and a snake and returns the segment closer to the head. Let's start with:

var segmentFurtherForwardThan = function(index, snake) {
  return snake[index - 1]
}

While this seems OK, in reality it has a nefarious bug! Open your console and type:

segmentFurtherForwardThan(1, snake);

This returns a segment object, which looks great! But what happens if we try a index of 0?

segmentFurtherForwardThan(0, snake);

Instead of a segment, we get undefined! This means we can't get a direction! Let's use an if statement to return the segment at the index if there isn't a segment further forward. Change segmentFurtherForwardThan to:

var segmentFurtherForwardThan = function(index, snake) {
  if (snake[index - 1] === undefined) {
    return snake[index];
  } else {
    return snake[index - 1];
  }
}

Now when you run segmentFurtherForwardThan(0, snake) it will return the snakes head instead of undefined. Excellent! Now we get to change moveSnake to use segmentFurtherForwardThan when it assigns the segments direction. Change moveSnake to:

var moveSnake = function(snake) {
  return snake.map(function(oldSegment, segmentIndex) {
    var newSegment = moveSegment(oldSegment);
    newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction;
    return newSegment;
  });
}

Great! Now each segment will follow the segment ahead of it!

Expected Results

What your snake.js file should look like:

var drawSnake = function(snakeToDraw) {
  var drawableSnake = { color: "green", pixels: snakeToDraw };
  var drawableObjects = [drawableSnake];
  CHUNK.draw(drawableObjects);
}

var moveSegment = function(segment) {
  switch(segment.direction) {
    case "down":
      return { top: segment.top + 1, left: segment.left };
    case "up":
      return { top: segment.top - 1, left: segment.left };
    case "right":
      return { top: segment.top, left: segment.left + 1 }
    case "left":
      return { top: segment.top, left: segment.left - 1 }
    default:
      return segment;
  }
}

var segmentFurtherForwardThan = function(index, snake) {
  if (snake[index - 1] === undefined) {
    return snake[index];
  } else {
    return snake[index - 1];
  }
}

var moveSnake = function(snake) {
  return snake.map(function(oldSegment, segmentIndex) {
    var newSegment = moveSegment(oldSegment);
    newSegment.direction = segmentFurtherForwardThan(segmentIndex, snake).direction;
    return newSegment;
  });
}

var advanceGame = function() {
  snake = moveSnake(snake);
  if (CHUNK.detectCollisionBetween(snake, CHUNK.gameBoundaries())) {
    CHUNK.endGame();
    CHUNK.flashMessage("Whoops! you hit a wall!");
  }
  drawSnake(snake);
}

var changeDirection = function(direction) {
  snake[0].direction = direction;
}

var snake = [{ top: 1, left: 0, direction: "down" }, { top: 0, left: 0, direction: "down" }];

CHUNK.executeNTimesPerSecond(advanceGame, 1);
CHUNK.onArrowKey(changeDirection);

How the game should work so far:

Syntax Breakdown

array.map and array.forEach pass 3 arguments to the function it is given: the first is the element in the collection, the second is its index (or location in the collection) and the third is the collection itself.

Next Step: