Link to home
Start Free TrialLog in
Avatar of curiouswebster
curiouswebsterFlag for United States of America

asked on

I need help getting this JavaScript snake game working

I was given this game by an expert on EE to help me learn JavaScript. I am learning it but this game would be more useful if it worked. For me, it seems the keyboard event is not being captured. So the game is not as useful as a learning tool.

If someone could please try and get it running, so I could really try and understand every line, I'd really appreciate it.

Thanks,
newbieweb
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<title>Snake Game Demo</title>
		<style type="text/css">
			html, body {
				background-color: #444444;
			}
			/* Adjust position of play area. */
			#canvas {
				/* Make sure that apple and snake parts are relative to the play area. */
				position: relative;
				/* 20 (cell width) * 21 (columns) + 2 (border) = 422 */
				width: 422px;
				height: 422px;
				/* Center play area horizontally. */
				margin: 0 auto;
				background-color: #ffffff;
				outline: solid 1px #ffffff;
				border: solid 1px #000000;
			}
			/* All entities (apple, snake parts) are absolute and one pixel smaller in
			   size than a grid cell to avoid nasty thick edges where snake parts meet. */
			.entity {
				position: absolute;
				width: 19px;
				height: 19px;
			}
			#apple {
				border: solid 1px #800000;
				background-color: #ff0000;
			}
			.tail {
				border: solid 1px #008000;
				background-color: #00ff00;
			}
			/* Change colour of snake upon gameover. */
			.gameover .tail {
				border-color: #808000;
				background-color: #ffff00;
			}
			#status {
				width: 402px;
				margin: 100px auto 0;
				padding: 0 10px;
				outline: solid 1px #ffffff;
				border: solid 1px #000000;
				background-color: #000000;
				color: #ffffff;
				font-size: x-large;
			}
			#btn-new-game {
				float: right;
			}
		</style>
		<script type="text/javascript" src="Scripts/jquery.min.js"></script>
		<script type="text/javascript">
		    //==> 1		// The special dollar function $ is provided by jQuery and
		    // allows elements to be selected using CSS1, CSS2 and CSS3
		    // selectors.
		    //		See: http://api.jquery.com/jQuery/

		    // One of the few objects that cannot be selected with any CSS
		    // selector is the document object. For this reason the document
		    // is passed by value instead (as shown below). This adds the
		    // special jQuery functionality:

		    // Binds 'onready' event to the HTML document object.
		    //		See: http://api.jquery.com/ready/
		    //
		    // Note: jQuery allows us to use anonymous functions (without name).
		    $(document).ready(function () {
		        // Call a function to initialise the game.
		        init_game();

		        // Bind the 'click' event to our 'New Game' button.
		        // When 'New Game' is clicked the 'init_game' function
		        // will be invoked.
		        //		See: http://api.jquery.com/click/
		        $('#btn-new-game').click(init_game);
		    });

		    // A global variable which will be used to store values in without
		    // any risks of conflicting with other global variables (either default
		    // or from other scripts that may be included).

		    // Think of this as a kind of namespace.
		    $global = {};

		    // LOOK AT '==> 2' further down the script next.

		    // Returns a random number between 0 and 21. The game board is 21 squares
		    // wide by 21 squares high.
		    function random_slot() {
		        // 'Math.floor' rounds a floating-point number down. So 0.8 will become 0.
		        //		See: http://www.w3schools.com/jsref/jsref_floor.asp
		        // 'Math.random' generates a random number between 0.0 and 1.0 (inclusive).
		        //		See: http://www.w3schools.com/jsref/jsref_random.asp
		        return Math.floor(Math.random() * 21);
		    }
		    // This function will simply move the apple to another square on the grid.
		    function move_apple() {
		        // Set variables in $global to a random grid position using our
		        // 'random_slot' function from above.
		        $global.appleX = random_slot();
		        $global.appleY = random_slot();
		        // Use CSS to select the apple element by id.
		        // Notice how in jQuery it is possible to chain certain calls
		        // as .css has been chained below:
		        $('#apple')
		        // Adjust left position of apple. To do this we simply multiply the
		        // horizontal grid index by 20 (the width of a grid cell in pixels).
		        // This gives us the pixel position of the apple.
		        //		See: http://api.jquery.com/css/
					.css('left', $global.appleX * 20)
		        // Multiply the top position of the apple in a similar way.
		        //		See: http://api.jquery.com/css/
					.css('top', $global.appleY * 20);
		    }
		    // This function extends the tail of the snake by the specified count.
		    function extend_tail(count) {
		        // While there are still pieces to add...
		        // Note: 'count' variable is post-fix decremented (after being checked)
		        //		 by adding -- after the variable name.
		        while (count--)
		        // Push an element into the global tail array.
		        // The array allows later code to easily access each part of the tail.

		        // The $ function is used here to create a brand new element.
		        //		See: http://www.w3schools.com/jsref/jsref_obj_array.asp
		            $global.tail.push($('<div class="entity tail"></div>')
		        // Adds new tail piece into the physical DOM structure (page).
		        // All tail pieces are contained within the 'canvas' element which
		        // is defined within the <body>...</body> tags at the bottom.
		        //		See: http://api.jquery.com/appendTo/
						.appendTo('#canvas')
		        // Locate tail at snakes head current position.
		        // Note: Again this grid position needs to be converted into pixels.
		        //		See: http://api.jquery.com/css/
						.css('left', $global.x * 20)
		        //		See: http://api.jquery.com/css/
						.css('top', $global.y * 20));
		    }
		    // Function is called at intervals to update the snake.
		    function update_snake() {
		        // Snake movement is an illusion as actually only one piece is moved
		        // each time. The end of the tail is moved one square in front of the
		        // head. The tail is then promoted to being the head.

		        // Fetch last piece of tail. In JavaScript each array has a 'length'
		        // member which provides the current length of an array. The length
		        // can be used to access the last element in the array, but 1 must be
		        // subtracted for a zero-based index. For example, an array with length=3
		        // has elements 0, 1 and 2.

		        // Put value of last piece into a new variable called 'last'.
		        //		See: http://www.w3schools.com/jsref/jsref_obj_array.asp
		        var last = $global.tail[$global.tail.length - 1];
		        // jQuery has already been applied in this case, but you could harmlessly
		        // reapply with $(last) if it helps you to remember.
		        last
		        // Move last piece of tail to location of head of tail.
					.css('left', $global.x * 20)
					.css('top', $global.y * 20);

		        // The 'splice' function of JavaScript is used to alter arrays.
		        // http://www.w3schools.com/jsref/jsref_splice.asp

		        // It can be used to add an item to the start of an array. We insert (copy)
		        // the last piece of the array to the start of the array.
		        $global.tail.splice(0, 0, last);
		        // It can be used to remove one or more elements from an array. We remove
		        // the last piece from the end of the array. (For example, so that it is only
		        // in the array once - at the start).
		        $global.tail.splice($global.tail.length - 1, 1);
		    }
		    // Function to update score status.
		    function update_score(score) {
		        // Find 'score' element and change text content to the score.
		        // The '$global.score = score' part adjusts the global score value.
		        $('#score').text($global.score = score);
		    }
		    //==> 2		// Function to initialise the game.
		    function init_game() {
		        // Initialise score to 0 using our update score function.
		        update_score(0);
		        // Start head of snake in center of grid at cell (10, 10)
		        $global.x = $global.y = 10;
		        // Set current direction of snake to down.
		        // Set next direction of snake to down.
		        // Note: Current direction is the direction in which the snake is
		        //		 currently moving. Next direction is the direction in which
		        //		 the snake will assume the next time is physically moves. This
		        //		 value is adjusted by keyboard input.
		        $global.direction = ($global.nextDirection = 'down');
		        // Initialise an empty array object.
		        $global.tail = [];

		        // Remove CSS class name 'gameover' from the body element. This is only
		        // set if the game has been played before, and is used to change the colour
		        // of the snake.
		        $('body')
					.removeClass('gameover');

		        // Remove all tail elements from board.
		        // Strictly speaking the second two lines are not needed!!
		        // They are remenance of an earlier part of my tutorial.
		        $('.tail')
					.remove();
		        //.css('left', $global.x * 20)	- REDUNDANT
		        //.css('top', $global.y * 20);	- REDUNDANT

		        // Extend tail by 3 - Make snake 3 bits in length.
		        extend_tail(3);
		        // Update snake position.
		        update_snake();
		        // Move apple to a random position on grid.
		        move_apple();

		        // Clear previous timer using its ID.
		        // Note: This is only applicable when starting a new game.
		        clearInterval($global.timerId);
		        // Create a new timer which invokes our 'tick' function every 50 ms.
		        // Store the timer ID into a global varaible so that it can be stopped
		        // later.
		        $global.timerId = setInterval(tick, 50);

		        // Return false - This is fairly standard when working with events in JS.
		        return false;
		    }

		    // Assign 'keydown' event to the document object.
		    $(document).keydown(function (event) {
		        // Note: jQuery has sorted out browser incompatabilities on the keyCode
		        // for us so that we don't need to worry!

		        // 'switch' is similar to 'if'. It runs a block of code when a specific
		        // value is matched. 'break' is used to leave the switch construct.
		        switch (event.keyCode) {
		            case 38: // Up
		                // In snake you cannot move up if you are currently moving down. This
		                // would force the snake to eat itself!
		                if ($global.direction != 'down')
		                // Set next direction to up.
		                    $global.nextDirection = 'up';
		                // Leave the break construct as we are now done with up key! and
		                // do not really want to continue into the down key as well!!
		                break;
		            case 40: // Down
		                // In snake you cannot move down if you are currently moving up.
		                if ($global.direction != 'up')
		                    $global.nextDirection = 'down';
		                break;
		            case 37: // Left
		                // In snake you cannot move left if you are currently moving right.
		                if ($global.direction != 'right')
		                    $global.nextDirection = 'left';
		                break;
		            case 39: // Right
		                // In snake you cannot move right if you are currently moving left.
		                if ($global.direction != 'left')
		                    $global.nextDirection = 'right';
		                break;
		        }
		    });

		    // This function is called at intervals of 50 ms (as initialised earlier).
		    function tick() {
		        // Adjust current direction of snake to the next direction with
		        //		$global.direction = $global.nextDirection
		        // Switch on new current direction.
		        switch ($global.direction = $global.nextDirection) {
		            case 'up':
		                // Move head one cell up.
		                --$global.y;
		                break;
		            case 'down':
		                // Move head one cell down.
		                ++$global.y;
		                break;
		            case 'left':
		                // Move head one cell to left.
		                --$global.x;
		                break;
		            case 'right':
		                // Move head one cell to right.
		                ++$global.x;
		                break;
		        }

		        // Bounds checking, wrap snake around screen.

		        // If head is leaving left of play area, set to end cell. Remember
		        // that cell index 20 is the 21st cell in line.
		        if ($global.x < 0)
		            $global.x = 20;
		        // Otherwise, if the head is leaving the right of the play area, move
		        // it to the start (first index: 0).
		        else if ($global.x > 20)
		            $global.x = 0;

		        // If head is leaving top, set it to bottom.
		        if ($global.y < 0)
		            $global.y = 20;
		        // If head is leaving bottom, set it to top.
		        else if ($global.y > 20)
		            $global.y = 0;

		        // Check for collision with self...

		        // Calculate new pixel position of head.
		        var newX = $global.x * 20;
		        var newY = $global.y * 20;
		        // Go through each part of the snake and find out if the head occupies the
		        // same space as any piece of the tail. If it does then it is eating itself!!!
		        //		See: http://www.w3schools.com/js/js_loop_for.asp
		        for (var i = 0; i < $global.tail.length; ++i) {
		            // 'position' is a jQuery function (see: http://api.jquery.com/position/)
		            // which gets the pixel position of an element.
		            var tail = $global.tail[i].position();
		            // Does position of tail match position of head?
		            if (tail.left == newX && tail.top == newY) {
		                // Snake is trying to eat itself!!!

		                // Get body element.
		                $('body')
		                // Add class 'gameover' so that snake changes colour (by CSS at top).
							.addClass('gameover');
		                // Stop game timer as snake is to stop moving...it is dead!
		                clearInterval($global.timerId);
		                // Clear timer id so that timer cannot be stopped again.
		                $global.timerId = null;
		            }
		        }

		        // Check for collision with apple...
		        // Determine whether snake head and apple occupy the same grid cell.
		        if ($global.x === $global.appleX && $global.y === $global.appleY) {
		            // They do! Move apple to another location on grid to give
		            // illusion that it has been eaten.
		            move_apple();
		            // Extend tail of snake by 1 part so that it grows!
		            extend_tail(1);
		            // Add 1 to score.
		            update_score($global.score + 1);
		        }

		        // Apply changes to snake...

		        // Now that game has ticked (aka, the game loop), update physical position
		        // of snake parts on screen. Without this function the game would work, but
		        // the new position of the snake would not be reflected.
		        update_snake();
		    }
		</script>
	</head>
	<body>
		<!-- Status output which is updated by scripts. -->
		<div id="status">
			<!-- "New Game" button which is assigned a 'click' event in script. -->
			<input id="btn-new-game" type="button" value="New Game" />
			<!-- Score label -->
			<span>Score: </span>
			<!-- Score indicator which is updated by script. -->
			<span id="score">0</span>
		</div>
		<!-- Game play area -->
		<div id="canvas">
			<!-- Apple element!! -->
			<div id="apple" class="entity"></div>
		</div>
	</body>
</html>

Open in new window

SOLUTION
Avatar of numberkruncher
numberkruncher
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
What you did provide doesn't work for me.
Try to run it from archive: http://www.rapidshare.com/files/439700375/snake.rar
Avatar of curiouswebster

ASKER

Thanks. It works on IE 8 as well. I was using the wrong keys. Thanks for posting such a great demo! I hope to understand every line of code....

newbieweb
That is running fine on my computer as well, again under Chrome, FireFox and IE....

Try running it from my server:

http://rotorz.com/demo/snake/snake.html
Fair enough, easy mistake. I will remove the demo from my server.