Paste Search Dynamic
Recent pastes
snake game
  1. // Directions
  2. var LEFT = 37;
  3. var UP = 38;
  4. var RIGHT = 39;
  5. var DOWN = 40;
  6.  
  7. // Board
  8. var $board;
  9. var columns = 40;
  10. var rows = 30;
  11. var grid; // A bidimensional array to map [i, j] coordinates to the cells of the grid
  12. var goodSnake;
  13. var badSnakes = [];
  14.  
  15. // Identifier returned by setInterval, used to stop the game
  16. var intervalId;
  17.  
  18. // The document ready function
  19. $(document).ready(function() {
  20.         init();
  21.  
  22.         // Update the board every 80 ms
  23.         intervalId = setInterval(updateGrid, 80);
  24.  
  25.         // Make a bad snake appear every 500 ms
  26.         setInterval(addBadSnake, 500);
  27. });
  28.  
  29. /**
  30.  * Build the initial grid and create the good snake
  31.  */
  32. function init(){
  33.         buildGrid();
  34.         goodSnake = new Snake(20, 15);
  35. }
  36.  
  37. /**
  38.  * Build the grid of the game
  39.  */
  40. function buildGrid(){
  41.         // Init the grid array
  42.         grid = [];
  43.         for (var i = 0; i < columns; i++){
  44.                 grid[i] = [];
  45.         }
  46.  
  47.         $board = $("#board");
  48.  
  49.         for (var j = 0; j < rows; j++){
  50.                 for (var i = 0; i < columns; i++){
  51.                         // Create a div for the current cell
  52.                         var $cell = $("<div>", {class: "cells"});
  53.                         $board.append($cell);
  54.  
  55.                         // Link the cell to the grid array
  56.                         grid[i][j] = $cell;
  57.                 }
  58.         }
  59. }
  60.  
  61. /**
  62.  * Draw the current state of the game.
  63.  */
  64. function draw(){
  65.         // Empty everything
  66.         for (var i = 0; i < columns; i++){
  67.                 for (var j = 0; j < rows; j++){
  68.                         grid[i][j].css("background-color", "black");
  69.                 }
  70.         }
  71.  
  72.         // Draw the good snake
  73.         drawSnake(goodSnake, "red", "blue");
  74.  
  75.         // Draw all the bad snakes
  76.         for (var k = 0; k < badSnakes.length; k++){
  77.                 drawSnake(badSnakes[k], "orange", "green");
  78.         }
  79. }
  80.  
  81. /**
  82.  * Draw a snake
  83.  * @param {Snake} snake - The snake to draw
  84.  * @param {string} headColor - The color of the head of the snake
  85.  * @param {string} tailColor - The color of the tail of the snake
  86.  */
  87. function drawSnake(snake, headColor, tailColor){
  88.         // Head
  89.         grid[snake.head[0]][snake.head[1]].css("background-color", headColor);
  90.  
  91.         // Tail
  92.         for (var k = 0; k < snake.tail.length; k++){
  93.                 grid[snake.tail[k][0]][snake.tail[k][1]].css("background-color", tailColor);
  94.         }
  95. }
  96.  
  97. /**
  98.  * Update the state of the game (snake position and drawing)
  99.  */
  100. function updateGrid(){
  101.         // Move good snake
  102.         move(goodSnake);
  103.  
  104.         // Move bad snakes
  105.         for (var k = 0; k < badSnakes.length; k++){
  106.                 var snake = badSnakes[k];
  107.                 changeDirection(snake);
  108.                 move(snake);
  109.         }
  110.  
  111.         // Game over?
  112.         if (isDeadGoodSnake()){
  113.                 // Game over
  114.                 clearInterval(intervalId);
  115.         }
  116.         else{
  117.                 // Were some snake killed?
  118.                 updateDeadBadSnakes();
  119.                 draw();
  120.         }
  121. }
  122.  
  123. /**
  124.  * The prototype constructor of Snake objects
  125.  * @constructor
  126.  * @param {number} x - The initial x coordinate of the snake.
  127.  * @param {number} y - The initial y coordinate of the snake.
  128.  */
  129. function Snake(x, y){
  130.         this.head = [x, y];
  131.         this.tail = [
  132.                 [x, y],
  133.                 [x, y],
  134.                 [x, y],
  135.                 [x, y],
  136.                 [x, y]
  137.         ];
  138.         var direction = randomDirection(); // private attribute
  139.  
  140.         /**
  141.          * Get the direction of the snake
  142.          * @return {number} the direction
  143.          */
  144.         this.getDirection = function(){
  145.                 return direction;
  146.         }
  147.  
  148.         /**
  149.          * Set the direction of the snake
  150.          * @param {number} d - the direction
  151.          */
  152.         this.setDirection = function(d){
  153.                 if (!oppositeDirections(direction, d)){
  154.                         direction = d;
  155.                 }
  156.         }
  157. }
  158.  
  159. /**
  160.  * Test if two directions are opposite.
  161.  * @param {number} d1 - the first direction
  162.  * @param {number} d2 - the second direction
  163.  * @return {boolean} true if the directions are opposite, false else
  164.  */
  165. function oppositeDirections(d1, d2){
  166.         if (d1 == LEFT){
  167.                 return d2 == RIGHT;
  168.         }
  169.         if (d1 == UP){
  170.                 return d2 == DOWN;
  171.         }
  172.         if (d1 == RIGHT){
  173.                 return d2 == LEFT;
  174.         }
  175.         if (d1 == DOWN){
  176.                 return d2 == UP;
  177.         }
  178. }
  179.  
  180. /**
  181.  * A function to make a snake moves
  182.  * @param {Snake} snake - the snake
  183.  */
  184. function move(snake){   
  185.         // Move the snake
  186.  
  187.         // shift the n - 1 first elements of the tail
  188.         for (var k = snake.tail.length - 1; k > 0; k--){
  189.                 snake.tail[k][0] = snake.tail[k - 1][0];
  190.                 snake.tail[k][1] = snake.tail[k - 1][1];
  191.         }
  192.  
  193.         // Put the head in the first element of the tail
  194.         snake.tail[0][0] = snake.head[0];
  195.         snake.tail[0][1] = snake.head[1];
  196.  
  197.         // Move the head
  198.         if (snake.getDirection() == LEFT){
  199.                 snake.head[0]--;
  200.         }
  201.         else if (snake.getDirection() == UP){
  202.                 snake.head[1]--;
  203.         }
  204.         else if (snake.getDirection() == RIGHT){
  205.                 snake.head[0]++;
  206.         }
  207.         else if (snake.getDirection() == DOWN){
  208.                 snake.head[1]++;
  209.         }
  210. }
  211.  
  212. // Change the direction of the snake of the user using the keybord
  213. $(document).keydown(function(e){
  214.         var code = e.keyCode;
  215.         if (code == LEFT || code == UP || code == RIGHT || code == DOWN){
  216.                 goodSnake.setDirection(code);
  217.         }
  218. });
  219.  
  220. /**
  221.  * Change the direction of a bad snake at random (only for bad snakes)
  222.  * @param {Snake} snake - the snake
  223.  */
  224. function changeDirection(snake){
  225.         if (parseInt(Math.random() * 10) == 0){
  226.                 snake.setDirection(randomDirection());
  227.         }
  228. }
  229.  
  230. /**
  231.  * Select a random direction. Exploits the fact that directions have value 37 to 40.
  232.  * @return {number} the random direction
  233.  */
  234. function randomDirection(){
  235.         var r = parseInt(Math.random() * 4);
  236.         return 37 + r;
  237. }
  238.  
  239. /**
  240.  * Add a bad snake
  241.  */
  242. function addBadSnake(){
  243.         var x = parseInt(Math.random() * columns);
  244.         var y = parseInt(Math.random() * rows);
  245.         badSnakes[badSnakes.length] = new Snake(x, y);
  246. }
  247.  
  248. /**
  249.  * Test if we are dead
  250.  * @return {boolean} true if we are dead, false else
  251.  */
  252. function isDeadGoodSnake(){
  253.  
  254.         // Case 1: we are out of the board
  255.         if (outOfBoard(goodSnake)){
  256.                 return true;
  257.         }
  258.        
  259.         // Case 2: one of the head of one of the bad snakes is on one of our tail cells
  260.         for (var k = 0; k < badSnakes.length; k++){
  261.                 var badSnake = badSnakes[k];
  262.                 if (bites(badSnake, goodSnake)){
  263.                         return true;
  264.                 }
  265.         }
  266.  
  267.         // OK we are good
  268.         return false;
  269. }
  270.  
  271. /**
  272.  * Check if a snake is out of the board
  273.  * @param {Snake} snake - the snake
  274.  * @return {boolean} true if we the snake is out of the board, false else
  275.  */
  276. function outOfBoard(snake){
  277.         return snake.head[0] < 0 || snake.head[0] >= columns || snake.head[1] < 0 || snake.head[1] >= rows;
  278. }
  279.  
  280. /**
  281.  * Check if a snake bites another snake (if the head of the first is on the tail of the second)
  282.  * @param {Snake} s1 - the first snake
  283.  * @param {Snake} s2 - the second snake
  284.  * @return {boolean} true if the head of the first is on the tail of the second, false else.
  285.  */
  286. function bites(s1, s2){
  287.         for (var k = 0; k < s2.tail.length; k++){
  288.                 if (s1.head[0] == s2.tail[k][0] && s1.head[1] == s2.tail[k][1]){
  289.                         return true;
  290.                 }
  291.         }
  292.         return false;
  293. }
  294.  
  295. /**
  296.  * Detect the bad snakes we killed, remove them from the board and add points.
  297.  */
  298. function updateDeadBadSnakes(){
  299.         // Snakes out of the board (we did not kill them)
  300.         for (var k = badSnakes.length - 1; k >= 0; k--){
  301.                 if (outOfBoard(badSnakes[k])){
  302.                         badSnakes.splice(k, 1);
  303.                 }
  304.         }
  305.  
  306.         // Did we bit a snake?
  307.         for (var k = badSnakes.length - 1; k >= 0; k--){
  308.                 var badSnake = badSnakes[k];
  309.                 if (bites(goodSnake, badSnake)){
  310.                         explodeAt(goodSnake.head);
  311.                         badSnakes.splice(k, 1);
  312.                         var score = parseInt($("#score").text()) + 1;
  313.                         $("#score").text(score);
  314.                 }
  315.         }
  316. }
  317.  
  318. /**
  319.  * Trigger a simple red explosion effect at a given position in the board
  320.  * @param {number[]} pos - the position at which to trigger the effect
  321.  */
  322. function explodeAt(pos){
  323.         var $div = $("<div>");
  324.         $board.append($div);
  325.         $div.css({"position": "absolute", "left": pos[0] * 20, "top": pos[1] * 20, "background-color": "red", "width": 20, "height": 20});
  326.         $div.animate({
  327.                 left: pos[0] * 20 - 20,
  328.                 top: pos[1] * 20 - 20,
  329.         opacity: '0',
  330.         height: '60px',
  331.         width: '60px'
  332.     }, 200);
  333. }
Parsed in 0.033 seconds