Image 15-Puzzle mechanics

Updating and current state

Every time the player moves a tile, we'll check to see if the puzzle is completed. This is very simple - we'll use a function called checkState, and we'll loop over all of the imageTiles and check if the imagePos x, y matches the the canvasPos x, y.

If this is not the case for any image tiles, the puzzle is not finished so we just return out of the function:

function checkState()
{
	for(var i in imageTiles)
	{
		if(imageTiles[i].imgPos[0]!=imageTiles[i].canvasPos[0] ||
			imageTiles[i].imgPos[1]!=imageTiles[i].canvasPos[1])
		{
			return;
		}
	}

If all tiles have been checked, the level is complete. We check and see if the movesTaken is better than the current imageBest entry for the current imageIndex, or if there is not currently a recorded total (the imageBest entry is 0). If so, we update the imageBest entry for this imageIndex:

	if(movesTaken<imageBest[imageIndex] || imageBest[imageIndex]==0)
	{
		imageBest[imageIndex] = movesTaken;
	}

Lastly, this function will start the next level; this will be imageIndex + 1, or 0 if this was the last image in the list. Afterwards, we can close the function.

	startLevel((imageIndex + 1)>=images.length ? 0 : (imageIndex + 1));
}

Game Update method

Our update method, updateGame, which is run each frame, only needs to do anything if there's a new click event the process, so we check that first:

function updateGame()
{
	if(mouseState.click!=null)
	{

We then calculate the tile on which the click event fell by rounding down the event x, y values divided by the tileW, tileH values respectively:

		var tile = [
			Math.floor(mouseState.click[0]/tileW),
			Math.floor(mouseState.click[1]/tileH)
		];

We'll then create a tileIdx variable to store the index of the tile on which the event fell in the imageTiles array, and loop through the array to see if our tile matches the canvasPos property of any of these tiles. If it does, we update the tileIdx to the corresponding index and leave the loop.

		var tileIdx = -1;
		for(var i in imageTiles)
		{
			if(imageTiles[i].canvasPos[0]==tile[0] &&
				imageTiles[i].canvasPos[1]==tile[1])
			{
				tileIdx = i;
				break;
			}
		}

If no index has been found (a valid tile was not clicked on), we clear the click event and leave the function.

		if(tileIdx==-1)
		{
			mouseState.click = null;
			return;
		}

If a valid Tile was clicked on, we'll look above, below, left and right of this tile to see if the freePos is neighbouring it. If so, we'll swap the Tile canvasPos values with the freePos values and increase the movesTaken counter.

		if(tile[0]==freePos[0] && tile[1]==(freePos[1]-1))
		{
			freePos[1]-= 1;
			imageTiles[i].canvasPos[1]+= 1;
			movesTaken++;
		}
		else if(tile[0]==freePos[0] && tile[1]==(freePos[1]+1))
		{
			freePos[1]+= 1;
			imageTiles[i].canvasPos[1]-= 1;
			movesTaken++;
		}
		else if(tile[1]==freePos[1] && tile[0]==(freePos[0]-1))
		{
			freePos[0]-= 1;
			imageTiles[i].canvasPos[0]+= 1;
			movesTaken++;
		}
		else if(tile[1]==freePos[1] && tile[0]==(freePos[0]+1))
		{
			freePos[0]+= 1;
			imageTiles[i].canvasPos[0]-= 1;
			movesTaken++;
		}

After, we'll clear the click event, call the checkState method to see if the puzzle is complete, and close the if block and function.

		mouseState.click = null;
		checkState();
	}
}
Page loaded in 0.01 second(s).