Image 15-Puzzle mechanics

New level and Shuffling

When we start a new level, such as when the game first starts or a new image is loaded after another has been completed, the startLevel method is called with the index of the image this puzzle will be formed from in the images array, idx.

This method begins by setting the imageIndex global to the idx value passed, emptying the imageTiles array, and resetting the imageIndex to 0:

``````function startLevel(idx)
{
imageIndex		= idx;
imageTiles.length	= 0;
movesTaken		= 0;``````

We then calculate the number of grid rows and columns by rounding down the current images width and height divided by the tileW, tileH values and subtracting 1 from each value (as our arrays are 0 based), and set the freePos x, y values to the bottom right of the grid, xMax, yMax:

``````	var yMax = Math.floor(images[idx].height / tileH) - 1;
var xMax = Math.floor(images[idx].width / tileW) - 1;

freePos = [xMax, yMax];``````

To create each segment or Tile of the image puzzle, we'll now loop over the rows and columns from y = 0 to yMax, and x = 0 to xMax.

``````	for(var y = 0; y <= yMax; y++)
{
for(var x = 0; x <= xMax; x++)
{``````

First, we'll check if we've reached the bottom-right of the grid; if so, we'll finish here without creating a Tile, as this will be the empty space or "hole":

``			if(x==xMax && y==yMax) { break; }``

We'll now create a Tile for the image; its position on the source image and the Canvas will be the x, y values of the current loop iterations (we'll shuffle later), and add the object to the imageTiles global array.

``````			var t		= new Tile();
t.imgPos	= [x, y];
t.canvasPos 	= [x, y];
imageTiles.push(t);``````

We'll now close the loops and call the shuffle method before closing the startLevel function completely:

``````		}
}

shuffle();
}``````

Shuffling the 15-puzzle

Our shuffle method begins by calculating the maximum x and y values on the grid:

``````function shuffle()
{
var yMax = Math.floor(images[imageIndex].height / tileH) - 1;
var xMax = Math.floor(images[imageIndex].width / tileW) - 1;``````

We'll then create a loop that we'll run as many times as we want to randomly swap pieces on the grid. In our example, we're using the value 150 for the number of swaps we want to perform. For each iteration we'll generate a random value between 0 and 1 and assign it to the r variable, and we'll copy the x, y values of the freePos global to the mov variable.

``````	for(var i = 0; i < 150; i++)
{
var r = Math.random();
var mov = [freePos[0], freePos[1]];``````

Depending on the value of r we'll try and move the "hole" up (<0.25), right (0.25-0.5), down (0.5-0.75), or left (0.75-1): we'll check that modifying the corresponding freePos x or y value would still be in grid bounds, and if so we'll update the freePos position.

``````		if(r < 0.25 && freePos[1]>0) { freePos[1]--; }
else if(r < 0.5 && freePos[0]<xMax) { freePos[0]++; }
else if(r < 0.75 && freePos[1]<yMax) { freePos[1]++; }
else if(freePos[0]>0) { freePos[0]--; }``````

Next, we'll loop over all of the imageTiles entries, and if one of them has canvasPos coordinates that match the freePos coordinates, we'll change the entry's canvasPos coordinates to the previous freePos position, stored in the mov variable and exit the loop:

``````		for(var it in imageTiles)
{
if(imageTiles[it].canvasPos[0]==freePos[0] &&
imageTiles[it].canvasPos[1]==freePos[1])
{
imageTiles[it].canvasPos = [
mov[0], mov[1]
];
break;
}
}``````

We can then close the shuffling main loop and the method itself:

``````	}
}``````