0

I’m working on a snake game. When my snake “eats” a piece of blue food, I add another piece to its tail. However, when I console.log the snake array, I don’t see the individual data attributes for each snake piece. I only see the position of the snake’s head duplicated based on the snake’s current length.

Is this happening because the setInterval that makes the snake move is too short of an interval? Is the snake moving so quickly that when console.log happens every piece is currently passing through where the head moves? Or is there some delay with what I’m seeing on the screen and where the snake array is? How can I fix my code so that I get the accurate values of each piece in the snake array? I want the array to reflect what I see on the screen.

If you press the shift key the snake will pause, but console.log still doesn’t display the correct values. The arrow keys control the snake’s movements. Click on the grid first to start playing.

//In the moveSnake function I had to use code from the below link in order to ignore multiple keydown events.
//https://stackoverflow.com/questions/9098901/how-to-disable-repetitive-keydown-in-jquery

$(document).ready(function() {
  makebox();
  addSnake();
  moveSnake();
  addFood();
  killSnake();
  addToSnake();
  });

function makebox() {

  var size = 24;  //24
  var boxSize = 12; //12
  for (i=1;i<=size*size;i++) {
    $("#container").append("<div class='box'></div>");
  };
  $("#container").width(size*boxSize + "px");
  $(".box").width(boxSize + "");
  $(".box").height(boxSize + "px");
  $(".box").each( function(i) {
    $(this).attr('data', (i+1));
  });
  };

function addSnake () {

var rightTime, leftTime, downTime, upTime, right, left, up, lildown;

moveRight = function() {
console.log(snake);
  right = true;
  left= false;
  up = false;
  lildown = false;
  down = {}
  rightTime = setInterval(function(){
  for (var i=0;i<snake.length;i++) {
    snake[i]++
$('*[data="' + snake[i] + '"]').addClass("hover")
$('*[data="' + (snake[snake.length-1]-snake.length) + '"]').removeClass("hover");

} }, 150)

};

moveLeft = function() {
  console.log(snake);
  right = false;
  left= true;
  up = false;
  lildown = false;
  down = {}
  leftTime = setInterval(function(){ //snake -= 1
    for (var i = 0; i <snake.length;i++){
        snake[i] -= 1
  $('*[data="' + snake[i] + '"]').addClass("hover");
  $('*[data="' + (snake[snake.length-1]+snake.length ) + '"]').removeClass("hover");
} }, 150)

};

moveDown = function() {
console.log(snake);
  right = false;
  left= false;
  up = false;
  lildown = true;
  down = {}
  downTime = setInterval(function(){ //snake += 25
    for (var i = 0; i <snake.length;i++){
        snake[i] += 18
  $('*[data="' + snake[i] + '"]').addClass("hover");
  $('*[data="' + (snake[snake.length-1] - 18 * snake.length) + '"]').removeClass("hover");


} }, 150)

};

moveUp = function() {
console.log(snake);
  right = false;
  left= false;
  up = true;
  lildown = false;
  down = {}
  upTime = setInterval(function(){ //snake -= 25
for (var i = 0; i <snake.length;i++){
        snake[i] -= 18
  $('*[data="' + snake[i] + '"]').addClass("hover");
  $('*[data="' + (snake[snake.length-1] + 18 * snake.length) + '"]').removeClass("hover");
} }, 150)

};

addTail = function() {
    snake.push(snake[snake.length - 1])
console.log(snake)
  }

var snake = [42]

$('*[data="' + snake[0] + '"]').addClass("hover");

var down = {};

removeExtra = function(){

var array = [];

  $(".hover").each(function() {
      array.push($(this).attr("data"));
  });

var len = array.length
var len2 = snake.length - 1
var combo = len-len2

  //for (var i=0;i<len2;i++){
    //array.splice(0,i)
    //$('*[data="' + (array[i]) + '"]').removeClass("hover");}

 }

  moveSnake = function() {

    $(document).keydown(function(event){

   var keycode = (event.keyCode ? event.keyCode : event.which);


   if(keycode == '39'){
        if (down['39'] == null) {
          window.clearInterval(leftTime);
          window.clearInterval(downTime);
          window.clearInterval(upTime);
          moveRight();
          removeExtra();
          down['39'] = true;

                             }
   }

  else if(keycode == '37'){
        if (down['37'] == null) {
          window.clearInterval(rightTime);
          window.clearInterval(downTime);
          window.clearInterval(upTime);
          moveLeft();
        removeExtra();
          down['37'] = true;
                    }
   }

   else if(keycode == '40'){
         if (down['40'] == null) {
           window.clearInterval(leftTime);
           window.clearInterval(rightTime);
           window.clearInterval(upTime);
           moveDown();
        removeExtra();
           down['40'] = true;
                     }
    }

    else if(keycode == '38'){
          if (down['38'] == null) {
            window.clearInterval(leftTime);
            window.clearInterval(rightTime);
            window.clearInterval(downTime);
            moveUp();
        removeExtra();
            down['38'] = true;
                      }
     }
      
          else if(keycode == '16'){
                                
            window.clearInterval(upTime);
            window.clearInterval(leftTime);
            window.clearInterval(rightTime);
            window.clearInterval(downTime);
            console.log(snake)
          }


 });

 addToSnake = function(){
   var count = 0;

     var config = { attributes: true, childList: true, characterData: true };

     $(".box, .food").each(function () {
       var target = this;
       var observer = new MutationObserver(function(mutations) {
         mutations.forEach(function(mutation) {
          if ($(".food").hasClass("hover") == true){
            $(".box").removeClass("food")
            addTail();
            addFood();
                      }
                    });
                  });

      observer.observe(target, config);

   });

           }

  killSnake = function() {
    var config = { attributes: true, childList: true, characterData: true, subtree:true };

    $(".right-border, .left-border, .top-border, .bottom-border").each(function () {
      var target = this;
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
          console.log("Game over!")
        });
      });

    observer.observe(target, config);
  });
          }
}

addFood = function(){
  var random = Math.floor(Math.random() * (500 - 1 + 1)) + 1;
  $('*[data="' + random + '"]').addClass("food")

};

};
.box {
  display: inline-block;
  border: 2px grey solid;

}


#container {
  display: block;
  border: 2px black solid;
  border-radius: 5px;
  font-size: 0;
  margin: 10px auto;
}
.hover {
  background-color: black;
}

.food {
  background-color: blue;
}

.white {
  background-color: white;
}

.right-border, .left-border, .top-border, .bottom-border {
  background: red;
  border: 2px red solid;

}
<!DOCTYPE html>

<html lang="en">

<head>

  <title>Snake</title>

  <meta charset="utf-8">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

  <link rel="stylesheet" href="style.css">

  <script type="text/javascript" src="script.js"></script>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

</head>

<body>

  <div class="container">

    <center>
      <h1>Snake</h1>
      
      <div id="container"></div>
    </center>

  </div>

</body>

</html>
Pat Mellon
  • 360
  • 2
  • 5
  • 12

1 Answers1

0

Wellll.. I've managed to fix this game somehow, mainly by doing 2 things:

  • Splitting the removal of the last tile & adding the first tile from updating snake indexes(values in the array). You got bugs in your code other that array values, and they are fixed this way.
  • adding the variable that remembers the last field where the snake has been and using that variable to add tile when a snake eats something. this is the main fix to your issue, as previously, you were basically copying values in an array, and it worked only by mistake (bad loop).

So, now it should work as expected, buuuut..

  1. It's blasphemy against God to use jquery in a game, it has terrible performance comparing to vanilla, and won't help you in any possible way.
  2. Movement & updating should occur inside 1 interval/frame loop, not inside 4 loops, it's serious crime against DRY rule.
  3. It'll be probably easier to use 2-dimensional array.
  4. And several others

//In the moveSnake function I had to use code from the below link in order to ignore multiple keydown events.
//https://stackoverflow.com/questions/9098901/how-to-disable-repetitive-keydown-in-jquery

$(document).ready(function() {
  makebox();
  addSnake();
  moveSnake();
  addFood();
  killSnake();
  addToSnake();
  });

  var previousSnake = null;

function makebox() {

  var size = 24;  //24
  var boxSize = 12; //12
  for (i=1;i<=size*size;i++) {
    $("#container").append("<div class='box'></div>");
  };
  $("#container").width(size*boxSize + "px");
  $(".box").width(boxSize + "");
  $(".box").height(boxSize + "px");
  $(".box").each( function(i) {
    $(this).attr('data', (i+1));
  });
  };



function addSnake () {



var rightTime, leftTime, downTime, upTime, right, left, up, lildown;

moveRight = function() {
console.log(snake);
  right = true;
  left= false;
  up = false;
  lildown = false;
  down = {}
  rightTime = setInterval(function(){

$('*[data="' + (snake[0]+1) + '"]').addClass("hover")
$('*[data="' + snake[snake.length-1] + '"]').removeClass("hover");
previousSnake = snake[snake.length-1]
updateSnakeNumbers(snake[0]+1)
 }, 150)
 

};

moveLeft = function() {
  console.log(snake);
  right = false;
  left= true;
  up = false;
  lildown = false;
  down = {}
  leftTime = setInterval(function(){ //snake -= 1
  console.log('llll',JSON.stringify(snake))
  $('*[data="' + (snake[0]-1) + '"]').addClass("hover");
  $('*[data="' + snake[snake.length-1]  + '"]').removeClass("hover");
  previousSnake = snake[snake.length-1]
  console.log('llll',JSON.stringify(snake))
  updateSnakeNumbers(snake[0]-1)
  console.log('llll',JSON.stringify(snake))
 }, 150)

};

moveDown = function() {
console.log(snake);
  right = false;
  left= false;
  up = false;
  lildown = true;
  down = {}
  downTime = setInterval(function(){ //snake += 25
  $('*[data="' + (snake[0]+18) + '"]').addClass("hover");
  $('*[data="' + snake[snake.length-1] + '"]').removeClass("hover");
  previousSnake = snake[snake.length-1]
updateSnakeNumbers(snake[0]+18)

}, 150)

};

moveUp = function() {
console.log(JSON.stringify(snake));
  right = false;
  left= false;
  up = true;
  lildown = false;
  down = {}
  upTime = setInterval(function(){ //snake -= 25
  $('*[data="' + (snake[0]-18) + '"]').addClass("hover");
  $('*[data="' + snake[snake.length-1]  + '"]').removeClass("hover");
  previousSnake = snake[snake.length-1]
  updateSnakeNumbers(snake[0]-18)
 }, 150)

};

addTail = function() {
  console.log(JSON.stringify(snake),previousSnake)
    snake.push(previousSnake)
console.log(JSON.stringify(snake),previousSnake)
  }

var snake = [42]

function updateSnakeNumbers(head){
  var prevNum = head
  snake = snake.map(n=>{
    var tmpPrev = prevNum;
    prevNum = n;
    return tmpPrev
  })
}

$('*[data="' + snake[0] + '"]').addClass("hover");

var down = {};

removeExtra = function(){

var array = [];

  $(".hover").each(function() {
      array.push($(this).attr("data"));
  });

var len = array.length
var len2 = snake.length - 1
var combo = len-len2

  //for (var i=0;i<len2;i++){
    //array.splice(0,i)
    //$('*[data="' + (array[i]) + '"]').removeClass("hover");}

 }

  moveSnake = function() {

    $(document).keydown(function(event){

   var keycode = (event.keyCode ? event.keyCode : event.which);


   if(keycode == '39'){
        if (down['39'] == null) {
          window.clearInterval(leftTime);
          window.clearInterval(downTime);
          window.clearInterval(upTime);
          moveRight();
          removeExtra();
          down['39'] = true;

                             }
   }

  else if(keycode == '37'){
        if (down['37'] == null) {
          window.clearInterval(rightTime);
          window.clearInterval(downTime);
          window.clearInterval(upTime);
          moveLeft();
        removeExtra();
          down['37'] = true;
                    }
   }

   else if(keycode == '40'){
         if (down['40'] == null) {
           window.clearInterval(leftTime);
           window.clearInterval(rightTime);
           window.clearInterval(upTime);
           moveDown();
        removeExtra();
           down['40'] = true;
                     }
    }

    else if(keycode == '38'){
          if (down['38'] == null) {
            window.clearInterval(leftTime);
            window.clearInterval(rightTime);
            window.clearInterval(downTime);
            moveUp();
        removeExtra();
            down['38'] = true;
                      }
     }
      
          else if(keycode == '16'){
                                
            window.clearInterval(upTime);
            window.clearInterval(leftTime);
            window.clearInterval(rightTime);
            window.clearInterval(downTime);
            console.log(snake)
          }


 });

 addToSnake = function(){
   var count = 0;

     var config = { attributes: true, childList: true, characterData: true };

     $(".box, .food").each(function () {
       var target = this;
       var observer = new MutationObserver(function(mutations) {
         mutations.forEach(function(mutation) {
          if ($(".food").hasClass("hover") == true){
            $(".box").removeClass("food")
            addTail();
            addFood();
                      }
                    });
                  });

      observer.observe(target, config);

   });

           }

  killSnake = function() {
    var config = { attributes: true, childList: true, characterData: true, subtree:true };

    $(".right-border, .left-border, .top-border, .bottom-border").each(function () {
      var target = this;
      var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
          console.log("Game over!")
        });
      });

    observer.observe(target, config);
  });
          }
}

addFood = function(){
  var random = Math.floor(Math.random() * (500 - 1 + 1)) + 1;
  $('*[data="' + random + '"]').addClass("food")

};

};
.box {
  display: inline-block;
  border: 2px grey solid;

}


#container {
  display: block;
  border: 2px black solid;
  border-radius: 5px;
  font-size: 0;
  margin: 10px auto;
}
.hover {
  background-color: black;
}

.food {
  background-color: blue;
}

.white {
  background-color: white;
}

.right-border, .left-border, .top-border, .bottom-border {
  background: red;
  border: 2px red solid;

}
<!DOCTYPE html>

<html lang="en">

<head>

  <title>Snake</title>

  <meta charset="utf-8">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

  <link rel="stylesheet" href="style.css">

  <script type="text/javascript" src="script.js"></script>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

</head>

<body>

  <div class="container">

    <center>
      <h1>Snake</h1>
      
      <div id="container"></div>
    </center>

  </div>

</body>

</html>
Michał Sałaciński
  • 2,256
  • 1
  • 11
  • 10
  • Thank you so much for your help! I know there's a lot wrong with my code. I thought if I could figure out this array issue then I could start to tackle some of the other issues. I really appreciate your help and suggestions! – Pat Mellon Jun 01 '17 at 18:02
  • If you get a chance can you walk me through what's going on in your updateSnakeNumbers function? I've been looking at it for 2 days, and I still don't think I can explain what it's doing exactly. Here's a [link](https://github.com/Pat878/JS_Snake/blob/master/script.js) to the code with some changes based on your answer. – Pat Mellon Jun 04 '17 at 02:40
  • Ok: so we're passing pre-calculated number as 0-index for head. then snake = snake.map( ) allows overwriting "snake" with new array, created from each of items from array "snake". The idea is to move numbers from head to back of snake. First item (head) is given from param, last one won't be used, as last part of snake is disappearing each time. So, inside .map loop we're getting current value, remembering it for later (prevNum) and inserting previous "prevNum" in it's place. – Michał Sałaciński Jun 04 '17 at 11:52
  • Awesome. Thanks again! – Pat Mellon Jun 04 '17 at 13:37