More articles

Coding The Snake Game In JavaScript

Written by
Filed under
Published on
Modified on

We've all seen the infamous snake game. It was a staple of non-smartphones back in the day. In this post, we'll be building something similar in JavaScript. This won't be the fully functional version, but it should get you pretty close to a fully functional version.

Snake Game

Name: Snake Game
Language: JavaScript
Estimated Completion: 3 hours

The Game Rules

The game sounds pretty simple overall from a high-level overview. But the low-level logic is somewhat involved.

  • You start with a single block in the center of a grid.
  • When the game starts it moves in a single random direction, and it is your job to keep it within the grid boundaries by moving it up, down, left or right.
  • If it hits any of the edges, it's game over for you.
  • At any one time, there will also be another block somewhere inside the grid stationary by itself.
  • When the moving block makes contact with that block, it gets added to your block, and it will follow along right behind it.
  • At this point another block will appear on the grid and you will repeat the process.
  • I'm not exactly sure how you win the game. But I'm assuming there comes a point in the game where you can no longer successfully maneuver your block snake. Or maybe when you reach a certain number of blocks for that level you win. I'll get to that roadblock when I get there.
  • Let's Start

    Once again, I'll be using JavaScript for this because I want to get it done as quickly as possible and using JS saves the trouble of setting up a new project and configuring it. And also so you can play it in the browser. I find JavaScript to be an excellent language for quick game development.

    The Game Grid

    So first thing is first, let's make a grid where our block friend will reside in. For this grid, I'll just use a basic table and pick an arbitrary n x n size. I'm going to make the size generic so that it can be as large or small as I want without it affecting the game.

    The following function should build the grid for you.

    
    var size = 5;
    
    function drawGrid()
    {
    	for(var i = 0; i < size; i++)
    	{
    		var row = document.createElement("tr");
    
    		for(var x = 0; x < size; x++)
    		{
    			var cell = document.createElement("td");
    			row.appendChild(cell);
    		}
    
    		document.getElementById("grid").appendChild(row);
    	}
    }
    
    

    Here are a few variables that we will add for now. Some of these might be gone later, but better to add them now lest we forget.

    
    var size = 20;
    var timer;
    var direction = "up";
    var theblock;
    
    

    The size as I mentioned above will determine the size of the grid. The timer will set an interval for how fast our block is moving and the direction will tell which direction our block is currently moving in. It will be updated based on the user's arrow key presses. So if you press the up key, it will get set to "up", down key "down" etc. These should be enough variables to get the ball rolling.

    Next up I'm going to initialize my main character, the Block. To save time, I'm just going to make his position the halfway point both in the x and y-axis. So on a 5 x 5 grid, he'll be located at [2, 2]. And I can set that with the following function.

    
    // gets called when the page loads
    function load()
    {
        drawGrid();
        theblock = {x: size / 2, y: size / 2};
        drawBlock();
    }
    
    function drawBlock()
    {
        var parent = document.getElementById("grid");
        parent.rows[theblock.y].cells[theblock.x].style.backgroundColor = "black";
    }
    
    window.onload = load;
    
    

    To draw the block, I'm just going to give the table's td a background color. So, for now, we should have something like this.

    It Moves!

    I'm going to set the timer to trigger every half second, and it will take whatever the current direction is and update the x and y values accordingly. By default, it will be set to "up", which means I'm going to decrease the y value. Before that though, I'll clear the current location that the block is occupying so that he doesn't leave a trail behind.

    
    function start()
    {
        timer = setInterval(function(){move();}, 500);
    }
    
    function move()
    {
        // let's clear the current block
        var parent = document.getElementById("grid");
        parent.rows[theblock.y].cells[theblock.x].style.backgroundColor = "white";
    
        // determines the new x and y values when changing direction
        switch(direction)
        {
            case "up":
            theblock.y--;
            break;
    
            case "down":
            theblock.y++;
            break;
    
            case "left":
            theblock.x--;
            break;
    
            case "right":
            theblock.x++;
            break;
        }
    
        // edge detection. game ends if this happens
        if (theblock.x < 0 || theblock.y < 0 || theblock.x >= size || theblock.y >= size)
        {
            document.getElementById("message").innerHTML = "Lost";
            clearInterval(timer);
        }
    
        else
        {
            drawBlock();
        }
    }
    
    

    A few things going on here. First, I'm clearing the block, followed by an update to the x and y coordinates depending on the value of direction. After the x and y values are updated, the drawBlock() function will be called again to set its new location. And I've added a lost condition to stop the game from running when a user hits any of the outer edges.

    So at this point, I just have a block on a grid that moves upwards every half second until it hits the edge. And I've set up the code that handles the coordinates when the direction changes. Next up I'll have it move around the board based on keypress events.

    Directional movement: Up, Down, Left, Right

    Now that we have a block in our grid, let's make it move around a bit. This should be as simple as updating the "direction" variable that I declared on top. The move() function that I just wrote above should then handle the rest.

    
    function start()
    {
        document.onkeydown = checkKey;
        timer = setInterval(function(){move();}, 500);
    }
    
    function checkKey(e) {
        e = e || window.event;
    
        if (e.keyCode == '38') {
            // up arrow
            direction = "up";
        }
        else if (e.keyCode == '40') {
            // down arrow
            direction = "down";
        }
        else if (e.keyCode == '37') {
           // left arrow
           direction = "left";
        }
        else if (e.keyCode == '39') {
           // right arrow
           direction = "right";
        }
    }
    
    

    So now we have a moving block on the board. It still feels like a working version is miles away, but not bad for an hour. So what's next? Something easy. I'm going to add a random red block to the board when the game starts.

    Let's Add A Snake Segment

    This "segment" sounds like another variable to me. It's going to need to go in a random location on the board, so I'm going to calculate an x and y position that's within the boundaries of the grid. I'll store that x and y into the segment variable and then set that table cell to red. And I'm going to call this function when our game starts and we set our interval.

    
    var segment;
    
    function start()
    {
        createSegment();
        document.onkeydown = checkKey;
        timer = setInterval(function(){move();}, 500);
    }
    
    function createSegment()
    {
        var x2 = Math.floor((Math.random() * size));
        var y2 = Math.floor((Math.random() * size));
    
        segment = {x:x2, y:y2};
        var parent = document.getElementById("grid");
        parent.rows[y2].cells[x2].style.backgroundColor = "red";
    }
    
    

    So now we have a random red block somewhere on the grid that my block needs to make contact with. Once it makes contact, it will create a new segment and add the current one to a new list of blocks. This list of blocks will be the body of the snake.

    This shouldn't be too difficult. Because only the first block can make contact with segments, I'll just be comparing the blocks x and y coordinates with the current segments x and y. Once they both match, it means that we have a collision. Once I have a collision two things will happen. First I'll have to create another red block somewhere randomly on my grid, and second I'll need to add this new block to my list of blocks.

    
    function checkSegment()
    {
        if (theblock.x == segment.x && theblock.y == segment.y)
        {
            createSegment();
            addChild();
        }
    }
    
    

    Fear not, I haven't implemented the addChild() function just yet. It's just a placeholder for now so that I don't forget later on. This checkSegment function will need to be called after my block makes a move, so at the end of the move() function makes sense.

    
    function move()
    {
        // let's clear the current block
        var parent = document.getElementById("grid");
        parent.rows[theblock.y].cells[theblock.x].style.backgroundColor = "white";
    
        // determines the new x and y values when changing direction
        switch(direction)
        {
            case "up":
            theblock.y--;
            break;
    
            case "down":
            theblock.y++;
            break;
    
            case "left":
            theblock.x--;
            break;
    
            case "right":
            theblock.x++;
            break;
        }
    
        // edge detection. game ends if this happens
        if (theblock.x < 0 || theblock.y < 0 || theblock.x >= size || theblock.y >= size)
        {
            document.getElementById("message").innerHTML = "Lost";
            clearInterval(timer);
        }
    
        else
        {
            drawBlock();
            checkSegment(); // !!!!! right here
        }
    }
    
    

    Next comes the tough part. Adding new segments to the main block. This would be the addChild() from above.

    My First Child Segment

    So first off, what does adding a new segment consist of?

    • 1. theBlock will make contact with a randomly generated block somewhere on the grid
    • 2. This second block will then be "added" to the first. This is the tricky part.
    • 2b. This new block will need to be added to the tail of the last added element, and it will need to have the same direction as the last element added. (huh)
    • 3. A new segment will be added somewhere in the grid
    
    var blocks = new Array();
    
    function addChild()
    {
        // the location of this block needs to be calculated
        // based on the current location of the last child added
        // then I can add it to the blocks array
    }
    
    

    I'm Out Of Time!

    It may not seem like it, but I'm on hour 3 now, and things just got complex. I have several failed attempts at the addChild implementation. This was definitely as tough as I had originally anticipated, but I think I got pretty far into the project. So far I have the game grid setup, and I have the main protagonist block created. I also have random segments getting generated at a random location on the grid. And I have our block moving in all directions every half second and being controlled by the keyboard.

    When I say challenge, I mean it apparently. But I will be back to finish this up either on the next challenge, or in a future one! It's also a good example that sometimes a project that sounds simple can grow to be a much more complex thing. Here's a quick working sample of what I managed to finish so far.

    view full source
Land your next big coding job. Search through 1000's of job listings.
X

Discussion / Comments / Questions

Walter G.
12/26/2018 12:48:10 PM
List any improvements or suggestions in the comments!

Add a comment

Send me your weekly newsletter filled with awesome ideas
Post comment