Sunday, 7 February 2016

Web Tutorial: Valentine's Maze Game (Part 2/4)

The board's been set, the tiles have been placed. Now we have to ensure that there is a way to win the game.

You see, all the tiles have random tile types and placements. There's no guarantee that there is a path from start to end.

And speaking of Start and End...

Make this change to your HTML.
<body onload="reset();">


Then add the reset() function. The resetboard() function will be part of your reset() function. The variables start_id and end_id are the ids of your start and end tiles. We use a Do-while loop here because this operation will be carried out at least once, until you get a path between the start and end tiles.
        <script>
            var objtiletype=[];
            objtiletype[0] = {pos:"0px 0px", n:0, e:1, s:1, w:0};
            objtiletype[1] = {pos:"-200px 0px", n:0, e:0, s:1, w:1};
            objtiletype[2] = {pos:"-200px -200px", n:1, e:0, s:0, w:1};
            objtiletype[3] = {pos:"0px -200px", n:1, e:1, s:0, w:0};
            objtiletype[4] = {pos:"-100px 0px", n:0, e:1, s:0, w:1};
            objtiletype[5] = {pos:"0px -100px", n:1, e:0, s:1, w:0};
            objtiletype[6] = {pos:"-100px -200px", n:0, e:1, s:0, w:1};
            objtiletype[7] = {pos:"-200px -100px", n:1, e:0, s:1, w:0};
            objtiletype[8] = {pos:"-52px -100px", n:0, e:1, s:0, w:0};
            objtiletype[9] = {pos:"-148px -100px", n:0, e:0, s:0, w:1}; 

            var objtiles=[];

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                }
                while ();
            }

            function resetboard()
            {


Now, some rules. The start tile can be any tile on the extreme left. Thus tile ids 0, 6, 12 and 18 qualify. The end tile can be any tile on the extreme right. Thus tile ids 5, 11, 17 and 23 qualify. The generaterandomno() function has been deployed to randomly pick any tile on the extreme left to be the start tile, and any tile on the extreme right to be the end tile. Once the id for start and end tiles have been determined, they will derive their n, s, e and w from objtiletype[7], which is the tiletype for the start tile; and objtiletype[8], which is the tiletype for the end tile. And of course, the background-position is set according to their respective tiletypes.

After specifying the start and end tiles, we fire off the createpath() function.

Modify your JavaScript. We'll add a global array named path. And the condition of the Do-while loop is - as long as the value of the last element in the path array is "-1" (which means the operation was not successful), the script will keep trying.

            var objtiletype=[];
            objtiletype[0] = {pos:"0px 0px", n:0, e:1, s:1, w:0};
            objtiletype[1] = {pos:"-200px 0px", n:0, e:0, s:1, w:1};
            objtiletype[2] = {pos:"-200px -200px", n:1, e:0, s:0, w:1};
            objtiletype[3] = {pos:"0px -200px", n:1, e:1, s:0, w:0};
            objtiletype[4] = {pos:"-100px 0px", n:0, e:1, s:0, w:1};
            objtiletype[5] = {pos:"0px -100px", n:1, e:0, s:1, w:0};
            objtiletype[6] = {pos:"-100px -200px", n:0, e:1, s:0, w:1};
            objtiletype[7] = {pos:"-200px -100px", n:1, e:0, s:1, w:0};
            objtiletype[8] = {pos:"-52px -100px", n:0, e:1, s:0, w:0};
            objtiletype[9] = {pos:"-148px -100px", n:0, e:0, s:0, w:1}; 

            var objtiles=[];
            var path=[];

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);
            }


Creating a Path

Now that you have a start and end tile, the path needs to be created. This will be a trial and error process, and the script may take quite a few tries before succeeding.

Create the createpath() function in your JavaScript.
            function createpath(varstart,varend)
            {   
                path=[];
                var next_id=varstart;
                path[0]=varstart;
                var i=1;
            }

            function generaterandomno(varmin,varmax)
            {
                return Math.floor((Math.random() * (varmax-varmin+1)) + varmin);
            }


createpath() takes in the start tile's id and the end tile's id as parameters. At the start of the function, the path array is cleared. A variable next_id is declared, and set to the value of start_id. Which makes sense because the first value is always the start tile's id, right? Another variable, i, is declared, and this will keep track of how many tiles there are on the current path. The value of i is initialized to 1 because there is already one tile - the start tile.

Add a sendmessage() function and fire it off in your createpath() function. This basically displays a message to the user that the game is running some operation. It's a good practice - it keeps the user informed in the event that he has to wait.
            function createpath(varstart,varend)
            {   
                path=[];
                var next_id=varstart;
                path[0]=varstart;
                var i=1;

                sendmessage("Please wait. Evaluating paths...");
            }

            function generaterandomno(varmin,varmax)
            {
                return Math.floor((Math.random() * (varmax-varmin+1)) + varmin);
            }

            function sendmessage(varmessage)
            {
                document.getElementById("pnlMessage").innerHTML=varmessage;
            }


After the Do-while loop in the reset() function, fire off the sendmessage() function again to invite the user to begin playing.
            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);

                sendmessage("Click <span style='cursor:pointer' onclick='reset();'><b>here</b></span> to begin");
            }


Now add a Do-while loop in your createpath() function. This operation will be performed at least one time, to an undefined number of times until there's a definite success, ie. the value of next_id is the same as varend.
            function createpath(varstart,varend)
            {   
                path=[];
                var next_id=varstart;
                path[0]=varstart;
                var i=1;

                sendmessage("Please wait. Evaluating paths...");

                do
                {

                }
                while (next_id!=varend);
            }


In the Do-while loop, set the value of next_id by running the function nexttileid() and passing in the value of next_id. Don't have a nexttileid() function? You guessed it, we're going to create one! And then the value of next_id will be the next element in the paths array. i is then incremented, to reflect that there is another tile id in the array.
            function createpath(varstart,varend)
            {   
                path=[];
                var next_id=varstart;
                path[0]=varstart;
                var i=1;

                sendmessage("Please wait. Evaluating paths...");

                do
                {
                    next_id=nexttileid(next_id);
                    path[i]=next_id;
                    i++;
                }
                while (next_id!=varend);
            }

            function nexttileid(varid)
            {

            }

            function generaterandomno(varmin,varmax)
            {
                return Math.floor((Math.random() * (varmax-varmin+1)) + varmin);
            }


More code! If next_id is "-1", which means the path generator reached a dead end, path is cleared again and the first element reset to start_id. Needless to say, i is reset to 1.
            function createpath(varstart,varend)
            {   
                path=[];
                var next_id=varstart;
                path[0]=varstart;
                var i=1;

                sendmessage("Please wait. Evaluating paths...");

                do
                {
                    next_id=nexttileid(next_id);
                    path[i]=next_id;
                    i++;

                    if (next_id==-1)
                    {
                        path=[];
                        next_id=varstart;
                        path[0]=varstart;
                        i=1;
                    }
                }
                while (next_id!=varend);
            }


Hopefully the logic is clear at this point! If there's anything you don't quite grasp, slow down, study the code again.

We'll work on the nexttileid() function next. This function takes in the current tile id, varid, and determines the available tiles to move to next, then decides at random which one it's going to be. Tile ids that are already in the path array are not eligible. This prevents the script from going in an infinite loop.

Here, we start by declaring variables row and col. Then we set row and col to the x and y value of the tile determined by varid.
            function nexttileid(varid)
            {
                var row,col;
                row=objtiles[varid].y;
                col=objtiles[varid].x;
            }


From here, there are four directions to proceed - north, south, east and west. These are represented by the variables n, s, e and w. All of them are set to "1" by default. Now there are some conditions that would disqualify these directions. They're represented by the following If statements.
            function nexttileid(varid)
            {
                var row,col;
                row=objtiles[varid].y;
                col=objtiles[varid].x;

                var n=1,s=1,e=1,w=1;

                if (row==0) n=-1;
                if (row==3) s=-1;
                if (col==0) w=-1;
                if (col==5) e=-1;
            }


For example, Tile 2 is on row 0, meaning it's on the top row, then the next direction can't be north. And so on for the rest.


Tile 0


Tile 1
(w)


Tile 2
(current)


Tile 3
(e)


Tile 4


Tile 5


Tile 6


Tile 7


Tile 8
(s)


Tile 9


Tile 10


Tile 11


Tile 12


Tile 13


Tile 14


Tile 15


Tile 16


Tile 17


Tile 18


Tile 19


Tile 20


Tile 21


Tile 22


Tile 23


The next series of If statements. If a direction is viable (ie, the value is still "1"), then that variable is set to the id of the tile that lies in that direction.
            function nexttileid(varid)
            {
                var row,col;
                row=objtiles[varid].y;
                col=objtiles[varid].x;

                var n=1,s=1,e=1,w=1;

                if (row==0) n=-1;
                if (row==3) s=-1;
                if (col==0) w=-1;
                if (col==5) e=-1;

                if (n==1) n=varid-6;
                if (s==1) s=varid+6;
                if (w==1) w=varid-1;
                if (e==1) e=varid+1;
            }

For example, if the current tile's id is 4, then directions west, south and east are available. So w is set to 3, s is set to 10 and e is set to 5.



Tile 0


Tile 1


Tile 2


Tile 3
(w = 3)


Tile 4
(current = 4)


Tile 5
(e = 5)


Tile 6


Tile 7


Tile 8


Tile 9


Tile 10
(s = 10)


Tile 11


Tile 12


Tile 13


Tile 14


Tile 15


Tile 16


Tile 17


Tile 18


Tile 19


Tile 20


Tile 21


Tile 22


Tile 23


Here we declare the directions array and another series of If statements. If a direction is viable (ie, the value is not "-1"), then that variable is added to the directions array. The variable currentdirectionindex is meant to help add those viable directions nto the directions array.

            function nexttileid(varid)
            {
                var row,col;
                row=objtiles[varid].y;
                col=objtiles[varid].x;

                var n=1,s=1,e=1,w=1;

                if (row==0) n=-1;
                if (row==3) s=-1;
                if (col==0) w=-1;
                if (col==5) e=-1;

                if (n==1) n=varid-6;
                if (s==1) s=varid+6;
                if (w==1) w=varid-1;
                if (e==1) e=varid+1;

                var directions=[];
                var currentdirectionindex=-1;

                if (path.indexOf(n)==-1&&n!=-1) {currentdirectionindex++;directions[currentdirectionindex]=n;}
                if (path.indexOf(s)==-1&&s!=-1) {currentdirectionindex++;directions[currentdirectionindex]=s;}
                if (path.indexOf(w)==-1&&w!=-1) {currentdirectionindex++;directions[currentdirectionindex]=w;}
                if (path.indexOf(e)==-1&&e!=-1) {currentdirectionindex++;directions[currentdirectionindex]=e;}
            }


Then there's an If-else statement. First, we check if there are any elements in the directions array. If not, the nexttileid() function returns a value of "-1".

If there's only one value, then of course, the nexttileid() function returns that value. If there's more than one value, the nexttileid() function fires off generaterandomno() function and returns the result. And of course, if at any time the returned result is the same as end_id, the function createpath() registers it as a success. Otherwise, the entire process begins all over again.

In the event of a success, of course, you now have a path array that holds all the tile ids, in sequence!

            function nexttileid(varid)
            {
                var row,col;
                row=objtiles[varid].y;
                col=objtiles[varid].x;

                var n=1,s=1,e=1,w=1;

                if (row==0) n=-1;
                if (row==3) s=-1;
                if (col==0) w=-1;
                if (col==5) e=-1;

                if (n==1) n=varid-6;
                if (s==1) s=varid+6;
                if (w==1) w=varid-1;
                if (e==1) e=varid+1;

                var directions=[];
                var currentdirectionindex=-1;

                if (path.indexOf(n)==-1&&n!=-1) {currentdirectionindex++;directions[currentdirectionindex]=n;}
                if (path.indexOf(s)==-1&&s!=-1) {currentdirectionindex++;directions[currentdirectionindex]=s;}
                if (path.indexOf(w)==-1&&w!=-1) {currentdirectionindex++;directions[currentdirectionindex]=w;}
                if (path.indexOf(e)==-1&&e!=-1) {currentdirectionindex++;directions[currentdirectionindex]=e;}

                if (directions.length==0)
                {
                    return -1;
                }
                else
                {  
                    if (directions.length==1) return directions[0];
                    if (directions.length>1) return directions[generaterandomno(0,directions.length-1)];
                }
            }


So now you have a path!

But it's time to fiddle with the tiles on the board so as to give the player that one possible path to success. Back to your reset() function! Here we iterate through the path array. To be exact, we iterate from the second element to the second last element of the path array - because the first and last elements are the start and end tiles, and they've already been rendered.

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);

                for (var i=1;i<path.length-1;i++)
                {

                }

                sendmessage("Click <span style='cursor:pointer' onclick='reset();'><b>here</b></span> to begin");
            }


OK, now we need to determine what tiletypes are needed. If you look at the board, other than the start and end tiles, there are basically two kinds of tiles - those with straight lines, and those with right angles. You need a tile with a straight line if your previous and next tile are have the same x or y values. Otherwise, you need a right angle. Make perfect sense so far?

Straight line



Tile 0


Tile 1


Tile 2
(next)


Tile 3


Tile 4


Tile 5


Tile 6


Tile 7


Tile 8
(current)


Tile 9


Tile 10


Tile 11


Tile 12


Tile 13


Tile 14
(previous)


Tile 15


Tile 16


Tile 17


Tile 18


Tile 19


Tile 20


Tile 21


Tile 22


Tile 23


Right angle



Tile 0


Tile 1


Tile 2


Tile 3


Tile 4


Tile 5


Tile 6


Tile 7


Tile 8


Tile 9


Tile 10


Tile 11


Tile 12


Tile 13


Tile 14
(current)


Tile 15
(next)


Tile 16


Tile 17


Tile 18


Tile 19


Tile 20
(prev)


Tile 21


Tile 22


Tile 23


The right angle tiletypes are from 0 to 3 in the obtiletypes array, and the straight line tiletypes are elements 4 to 7. So over here, by default we assign the tile's tiletype to a right angle by firing off generaterandomno() and returning a value from 0 to 3. Next, we check if the previous and next tiles lie on the same row. If so, we assign the tile's tiletype to a random straight line tiletype instead. Same if the previous and next tiles lie on the same column. Then we adjust the CSS background-position property of that tile to correspond to its tiletype; and the tile's n, s, e and w values to its tiletype's values.

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);

                for (var i=1;i<path.length-1;i++)
                {
                    tiletypeindex=generaterandomno(0,3);

                    if (objtiles[path[i-1]].x==objtiles[path[i]].x&&objtiles[path[i+1]].x==objtiles[path[i]].x)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    if (objtiles[path[i-1]].y==objtiles[path[i]].y&&objtiles[path[i+1]].y==objtiles[path[i]].y)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    document.getElementById("tile"+path[i]).style.backgroundPosition=objtiletype[tiletypeindex].pos;

                    objtiles[path[i]].n=objtiletype[tiletypeindex].n;
                    objtiles[path[i]].e=objtiletype[tiletypeindex].e;
                    objtiles[path[i]].s=objtiletype[tiletypeindex].s;
                    objtiles[path[i]].w=objtiletype[tiletypeindex].w;
                }

                sendmessage("Click <span style='cursor:pointer' onclick='reset();'><b>here</b></span> to begin");
            }


Now run your code!

Your board should have a start tile (the arrow flight) and an end tile (the heart)



There should be a viable path between them. But you won't see it unless you do this...

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);

                for (var i=1;i<path.length-1;i++)
                {
                    tiletypeindex=generaterandomno(0,3);

                    if (objtiles[path[i-1]].x==objtiles[path[i]].x&&objtiles[path[i+1]].x==objtiles[path[i]].x)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    if (objtiles[path[i-1]].y==objtiles[path[i]].y&&objtiles[path[i+1]].y==objtiles[path[i]].y)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    document.getElementById("tile"+path[i]).style.backgroundPosition=objtiletype[tiletypeindex].pos;

                    objtiles[path[i]].n=objtiletype[tiletypeindex].n;
                    objtiles[path[i]].e=objtiletype[tiletypeindex].e;
                    objtiles[path[i]].s=objtiletype[tiletypeindex].s;
                    objtiles[path[i]].w=objtiletype[tiletypeindex].w;

                    document.getElementById("tile"+path[i]).style.backgroundColor="#FF4400";
                }

                sendmessage("Click <span style='cursor:pointer' onclick='reset();'><b>here</b></span> to begin");
            }


Refresh! That line changed the background color of all the tiles in the paths array, less the first and last to orange.



Stil don't see the path? Well, if the tiles were properly rotated, like so...



There it is - a way for the player to win this game. There might be more than one solution, of course, but the important thing was to ensure that there is at least one viable solution. Keep the background change for now. It may come in useful in the next part.

Next

We cause the tiles to rotate upon being clicked, and determine when the player wins the game.

Friday, 5 February 2016

Web Tutorial: Valentine's Maze Game (Part 1/4)

Valentine's Day approaches in a week! Romance is in the air. The earth shakes with passion.

OK, enough with the corny cliches. This Valentine's Day special web tutorial will be a long one.

From my review of the mobile game Agent Alice, there was a mini-game in it I was very fond of. And I decided I simply had to make my own.



After that, I gave it a Valentine's Day theme, and voila!

It's a game where all the pieces of a maze-like puzzle are rotated. You have to rotate the correct pieces to connect from Point A to Point B. This tutorial will be JavaScript-heavy. Hold on to your hats!

I'm going to start off with very minimal HTML.

<html>
    <head>
        <title>Find</title>

        <style>
            #board_wrapper
            {
                width:602px;
                height:402px;
                margin:50px auto 0 auto;
                border:2px solid #000000;
            }

            .text_wrapper
            {
                width:100%;
                height:auto;
                min-height:30px;
                text-align:center;
                color:#FF4400;
                font-size:14px;
            }
        </style>
    </head>
    <body>
        <div class="text_wrapper">
            Difficulty:
            <select id="ddlDifficulty">
                <option value="" selected>Easy</option>
            </select>
       

        <div id="pnlMessage" class="text_wrapper">
        <div id="pnlTimer" class="text_wrapper">Time left: 0 seconds
        <div id="board_wrapper">

        </div>    </body>
</html>

The CSS class text_wrapper is purely cosmetic - positioning, look and feel, do what you want with it. Go crazy. But the CSS class board_wrapper is fairly important. Here's what it does.

- border:2px solid #000000 sets the border
- width:602px, height:402px is like this for a very specific reason. The board is supposed to be made up of 4 rows, each containing 6 tiles. Each tile is 100x100 pixels. The extra 2 pixels leaves allowance for the border.
- margin:50px auto 0 auto puts the board smack in the middle of the screen.
               

This is what you should have right now.




You notice that the drop down list ddlDifficulty has only one option in it. This is deliberate and temporary.

The Game Tiles

OK, we've created the board. It's time to set up game tiles.

Each tile is a div that will be styled using this class. As earlier advertised, the height is 100 pixels and so is the width. The position property is set to absolute because each of these tiles needs to rotate on its own without affecting (visually, at least) the others. Lastly, the cursor property is set to pointer, so users know that these tiles should be clicked on.

I've created the following image to serve as the background image for these tiles.
easy.png

Modify your HTML, like so. This adds a value incorporating the URL of this image. By default, this option is selected.

            <select id="ddlDifficulty">
                <option value="url(easy.png)" selected>Easy</option>
            </select>


Now, we're going to dive right into the JavaScript. This could get a little hairy.

Add this code into your head tag.
    <head>
        <title>Find</title>

        <style>
            #board_wrapper
            {
                width:602px;
                height:402px;
                margin:50px auto 0 auto;
                border:2px solid #000000;
            }

            .text_wrapper
            {
                width:100%;
                height:auto;
                min-height:30px;
                text-align:center;
                color:#FF4400;
                font-size:14px;
            }
        </style>

        <script>
            var objtiletype=[];
            objtiletype[0] = {pos:"0px 0px", n:0, e:1, s:1, w:0};
            objtiletype[1] = {pos:"-200px 0px", n:0, e:0, s:1, w:1};
            objtiletype[2] = {pos:"-200px -200px", n:1, e:0, s:0, w:1};
            objtiletype[3] = {pos:"0px -200px", n:1, e:1, s:0, w:0};
            objtiletype[4] = {pos:"-100px 0px", n:0, e:1, s:0, w:1};
            objtiletype[5] = {pos:"0px -100px", n:1, e:0, s:1, w:0};
            objtiletype[6] = {pos:"-100px -200px", n:0, e:1, s:0, w:1};
            objtiletype[7] = {pos:"-200px -100px", n:1, e:0, s:1, w:0};
            objtiletype[8] = {pos:"-52px -100px", n:0, e:1, s:0, w:0};
            objtiletype[9] = {pos:"-148px -100px", n:0, e:0, s:0, w:1}; 

            var objtiles=[];
        </script>
    </head>

objtile is a global array meant to represent your game tiles.

objtiletype is a global array of objects. Each objtiletype has a x and y pixel offset defined by its property pos. n, s, e and w correspond to the cardinal points north, south, east and west. There are 10 types of tiles - the last 2 are start and end tiles, while the first 8 are normal game tile types. And if each objtiletype had the image I created as a background, that's what they would look like.

Normal game tiles.
Start and end game tiles.

Example - objtiletype[1]'s n and e values are 0, while s and w are 1. Do you see that in the tile (top left corner), the only exit/entry points are at the south and west? That's what the cardinal points are for.

Don't worry too much about this part; I'll get back to this soon.

Modify your HTML.

    <body onload="resetboard();">
        <div class="text_wrapper">
            Difficulty:
            <select id="ddlDifficulty">
                <option value="url(easy.png)" selected>Easy</option>
            </select>
       

        <div id="pnlMessage" class="text_wrapper">
        <div id="pnlTimer" class="text_wrapper">Time left: 0 seconds
        <div id="board_wrapper">

        </div>    </body>

Create the resetboard() function.

        <script>
            var objtiletype=[];
            objtiletype[0] = {pos:"0px 0px", n:0, e:1, s:1, w:0};
            objtiletype[1] = {pos:"-200px 0px", n:0, e:0, s:1, w:1};
            objtiletype[2] = {pos:"-200px -200px", n:1, e:0, s:0, w:1};
            objtiletype[3] = {pos:"0px -200px", n:1, e:1, s:0, w:0};
            objtiletype[4] = {pos:"-100px 0px", n:0, e:1, s:0, w:1};
            objtiletype[5] = {pos:"0px -100px", n:1, e:0, s:1, w:0};
            objtiletype[6] = {pos:"-100px -200px", n:0, e:1, s:0, w:1};
            objtiletype[7] = {pos:"-200px -100px", n:1, e:0, s:1, w:0};
            objtiletype[8] = {pos:"-52px -100px", n:0, e:1, s:0, w:0};
            objtiletype[9] = {pos:"-148px -100px", n:0, e:0, s:0, w:1}; 

            var objtiles=[];

            function resetboard()
            {
                var board=document.getElementById("board_wrapper");
                board.innerHTML="";
               
                var rowcounter=0,colcounter=0;
                var tile,tiletypeindex;

                for (var i=0;i<24;i++)
                {

                }
            }
        </script>


The first two lines clears the game board. Next, the variables rowcounter and colcounter are declared, along with tile and tiletypeindex.

Next is the For loop. There are a total of (4x6=24) tiles. So tiles is an array of elements indexed from 0 to 24. This For loop initializes the values of each objtile and places them on the board according to their number.

Add this to your resetboard() function.

            function resetboard()
            {
                var board=document.getElementById("board_wrapper");
                board.innerHTML="";
               
                var rowcounter=0,colcounter=0;
                var tile,tiletypeindex;

                for (var i=0;i<24;i++)
                {
                    tiletypeindex=generaterandomno(0,7);
                    objtiles[i] = {id:i, x:colcounter, y:rowcounter, n:objtiletype[tiletypeindex].n,s:objtiletype[tiletypeindex].s,e:objtiletype[tiletypeindex].e,w:objtiletype[tiletypeindex].w};
                }
            }

            function generaterandomno(varmin,varmax)
            {
                return Math.floor((Math.random() * (varmax-varmin+1)) + varmin);
            }


tiletypeindex is determined by generating a number from 0 to 7. Sure, there are 10 tile types as shown by the array objtiletype, but remember only the first 8, ie elements 0 to 7, are normal game tiles. So each tile type for each objtile, is determined randomly. Its n, s, e and w values will then correspond to those values in the objtiletype. This will be important later!

Add this to your resetboard() function.

                for (var i=0;i<24;i++)
                {
                    tiletypeindex=generaterandomno(0,7);
                    objtiles[i] = {id:i, x:colcounter, y:rowcounter, n:objtiletype[tiletypeindex].n,s:objtiletype[tiletypeindex].s,e:objtiletype[tiletypeindex].e,w:objtiletype[tiletypeindex].w};

                    tile=document.createElement("div");
                    tile.id="tile"+i;

                    tile.className="tile_wrapper";
                    tile.style.marginLeft=objtiles[i].x*100+"px";
                    tile.style.marginTop=objtiles[i].y*100+"px";

                    if (colcounter % 5==0&&colcounter>0)
                    {
                        rowcounter++;
                        colcounter=0;
                    }
                    else
                    {
                        colcounter++;
                    }
                }


The role of the rowcounter and colcounter variables becomes clearer now. Each row is only supposed to have 6 tiles. colcounter is incremented with each iteration of the For loop. But once colcounter reaches a number divisible by 5, rowcounter is incremented and colcounter is reset to 0.  

rowcounter multiplied by 100 is the number of pixels each objtile has its margin-top property offset by.  

colcounter multiplied by 100 is the number of pixels each objtile has its margin-top property offset by.

Also, the class of the tile is set to tile_wrapper. So we'll need to create that class in your CSS.

        <style>
            #board_wrapper
            {
                width:602px;
                height:402px;
                margin:50px auto 0 auto;
                border:2px solid #000000;
            }

            .tile_wrapper
            {
                width:100px;
                height:100px;
                position:absolute;
                cursor:pointer;
            }

            .text_wrapper
            {
                width:100%;
                height:auto;
                min-height:30px;
                text-align:center;
                color:#FF4400;
                font-size:14px;
            }
        </style>


I'm numbering the tiles to show you where each tile belongs, along with its rowcounter and colcounter values.


Tile 0
rowcounter = 0
colcounter = 0


Tile 1
rowcounter = 0
colcounter = 1


Tile 2
rowcounter = 0
colcounter = 2


Tile 3
rowcounter = 0
colcounter = 3


Tile 4
rowcounter = 0
colcounter = 4


Tile 5
rowcounter = 0
colcounter = 5


Tile 6
rowcounter = 1
colcounter = 0


Tile 7
rowcounter = 1
colcounter = 1


Tile 8
rowcounter = 1
colcounter = 2


Tile 9
rowcounter = 1
colcounter = 3


Tile 10
rowcounter = 1
colcounter = 4


Tile 11
rowcounter = 1
colcounter = 5


Tile 12
rowcounter = 2
colcounter = 0


Tile 13
rowcounter = 2
colcounter = 1


Tile 14
rowcounter = 2
colcounter = 2


Tile 15
rowcounter = 2
colcounter = 3


Tile 16
rowcounter = 2
colcounter = 4


Tile 17
rowcounter = 2
colcounter = 5


Tile 18
rowcounter = 3
colcounter = 0


Tile 19
rowcounter = 3
colcounter = 1


Tile 20
rowcounter = 3
colcounter = 2


Tile 21
rowcounter = 3
colcounter = 3


Tile 22
rowcounter = 3
colcounter = 4


Tile 23
rowcounter = 3
colcounter = 5

Add this to your resetboard() function.

                for (var i=0;i<24;i++)
                {
                    tiletypeindex=generaterandomno(0,7);
                    objtiles[i] = {id:i, x:colcounter, y:rowcounter, n:objtiletype[tiletypeindex].n,s:objtiletype[tiletypeindex].s,e:objtiletype[tiletypeindex].e,w:objtiletype[tiletypeindex].w};

                    tile=document.createElement("div");
                    tile.id="tile"+i;

                    tile.className="tile_wrapper";
                    tile.style.marginLeft=objtiles[i].x*100+"px";
                    tile.style.marginTop=objtiles[i].y*100+"px";
                    tile.style.backgroundImage=document.getElementById("ddlDifficulty").value;
                    tile.style.backgroundPosition=objtiletype[tiletypeindex].pos;
                    tile.style.backgroundRepeat="no-repeat";
                    tile.style.transform="rotate(0deg)";
                    tile.style.webkitTransform="rotate(0deg)";

                    if (colcounter % 5==0&&colcounter>0)
                    {
                        rowcounter++;
                        colcounter=0;
                    }
                    else
                    {
                        colcounter++;
                    }
                }


Now each tile uses the value of the ddlDifficulty drop-down list as its background image. The image is then offset by the x and y values determined by its corresponding objtiletype.pos property. Now we set a few more properties, such as backgroundRepeat and rotate, and we're good.

Add this to your resetboard() function.

                for (var i=0;i<24;i++)
                {
                    tiletypeindex=generaterandomno(0,7);
                    objtiles[i] = {id:i, x:colcounter, y:rowcounter, n:objtiletype[tiletypeindex].n,s:objtiletype[tiletypeindex].s,e:objtiletype[tiletypeindex].e,w:objtiletype[tiletypeindex].w};

                    tile=document.createElement("div");
                    tile.id="tile"+i;

                    tile.className="tile_wrapper";
                    tile.style.marginLeft=objtiles[i].x*100+"px";
                    tile.style.marginTop=objtiles[i].y*100+"px";
                    tile.style.backgroundImage=document.getElementById("ddlDifficulty").value;
                    tile.style.backgroundPosition=objtiletype[tiletypeindex].pos;
                    tile.style.backgroundRepeat="no-repeat";
                    tile.style.transform="rotate(0deg)";
                    tile.style.webkitTransform="rotate(0deg)";
                    board.appendChild(tile);

                    if (colcounter % 5==0&&colcounter>0)
                    {
                        rowcounter++;
                        colcounter=0;
                    }
                    else
                    {
                        colcounter++;
                    }
                }


This now appends the tile object you created, into the game board, at the exact placement.

Refresh your code, and the page should look like this now.



Next

There's more of course. We're just getting started. Game logic is next on the menu!