Pages

Monday, August 1, 2011

HTML5 - Sliding Tile Puzzle Game


Presented below is the HTML5 source code for a puzzle game called Sliding Tile puzzle or Mystic Square puzzle. Numbers 1 to 15 are jumbled in a 4x4 board. You have to arrange the numbers in order by moving the blank tile or magic square.
At the bottom of the board, you can see images in shuffled position. With each move on the board, the images will get re-aligned. Once the puzzle is solved, you can see the complete picture in the Canvas Area.
HTML5's Drag And Drop, Offline Storage, Canvas features are used. It is tested on Chrome 12.0 and Firefox 5.0 and NOT compatible with mobile devices.

Screen Shot:



HTML Code
<!DOCTYPE HTML>
<html manifest="cache.manifest">
<head>

<link rel=StyleSheet href="slidetile.css" TYPE="text/css">

<script src="jquery-1.6.2.min.js"></script>
<script src="slidetile.js"></script>
</head>
<body onload="initialize();">

<div id="row1" class="row">
<div id="1" class="tile" draggable="true"></div>
<div id="2" class="tile" draggable="true"></div>
<div id="3" class="tile" draggable="true"></div>
<div id="4" class="tile" draggable="true"></div>
</div>

<div id="row2" class="row">
<div id="5" class="tile" draggable="true"></div>
<div id="6" class="tile" draggable="true"></div>
<div id="7" class="tile" draggable="true"></div>
<div id="8" class="tile" draggable="true"></div>
</div>

<div id="row3" class="row">
<div id="9" class="tile" draggable="true"></div>
<div id="10" class="tile" draggable="true"></div>
<div id="11" class="tile" draggable="true"></div>
<div id="12" class="tile" draggable="true"></div>
</div>

<div id="row4" class="row">
<div id="13" class="tile" draggable="true"></div>
<div id="14" class="tile" draggable="true"></div>
<div id="15" class="tile" draggable="true"></div>
<div id="16" class="tile" draggable="true"></div>
</div>

<div id="moveStatus" class="row">
<div id="status">Ready to start?</div>
</div>

<canvas id="myCanvas" width="200" height="200" class="canvas">
</canvas>

<div id="buttons" class="row">
<input type="button" id="newGameButton" class="button" value="New"
onclick="clearStorage();initialize();"></input> <input type="button"
id="saveGameButton" class="button" value="Save"></input>
</div>

<div id="rules" class="rules">
<div id="rulesInfo" class="infoTitle">
<strong>Rules:</strong>
</div>
<div class="row">1. Arrange the numbers on the board from 1 to
15.</div>
<div class="row">2. Move the tiles around by moving only the
blank tile.</div>
<div class="row">3. The blank tile can be swapped only with the
adjacent tiles.</div>
<div class="row">4. Drag and Drop the tiles using the mouse.
Images are not draggable.</div>

<div class="infoTitle">
<strong>About:</strong>
</div>
<div class="row" id="AboutInfo">Sliding tile puzzle is also called Gem Puzzle or Mystic Square puzzle. If you can solve within 80 moves,well, you are lucky. The algorithm is complex to solve and sometimes impossible. Wish you patience and good luck. </div>

<div class="infoTitle">
<strong>Note:</strong>
</div>
<div class="row" id="NoteInfo">This version of game is NOT compatible with mobile. Offline caching enabled for modern browsers. Tested for Chrome 12.0 and Firefox 5.0</div>

<div class="infoTitle">
<strong>Best Finish:</strong>
</div>
<div class="row" id="bestMove"></div>
</div>

<div class="flashMessage" id="flashMessage"></div>

</body>
</html>


CSS:
.button {
color: white;
padding: 5px 20px;
text-shadow: 1px 1px 0 #145982;
font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
font-weight: bold;
text-align: center;
border: 1px solid #60b4e5;
margin-right: 20px;
margin-top: 20px;
float: left;
position: relative;
right: -35%;
width: 100px;

/*
CSS3 gradients for webkit and mozilla browsers fallback color for the rest:
*/
background-color: #59aada;
background: -moz-linear-gradient(left center, rgb(200, 0, 0),
rgb(79, 79, 79), rgb(21, 21, 21) );
background: -webkit-gradient(linear, left top, right top, color-stop(0, rgb(200, 0,
0) ), color-stop(0.50, rgb(79, 79, 79) ),
color-stop(1, rgb(21, 21, 21) ) ); to (#FFA4E6) );
box-shadow: 3px 3px 3px #000000;
-moz-box-shadow: 3px 3px 3px #000000;
-webkit-box-shadow: 3px 3px 3px #000000;
border: 1px solid #F902B6;
border-radius: 6px;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
background-color: #59aada
}


.button:hover { /* Lighter gradients for the hover effect */
text-decoration: none;
background-color: #F902B6;
background-image: -moz-linear-gradient(#6bbbe9, #57a5d4);
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#6bbbe9),
to(#57a5d4) );
box-shadow: 3px 3px 3px #333;
-moz-box-shadow: 3px 3px 3px #333;
-webkit-box-shadow: 3px 3px 3px #333;
}
/* Prevent the text contents of draggable elements from being selectable. */
[draggable] {
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
}

.row {
width: 100 %;
clear: both;
}

.infoTitle {
width: 100 %;
clear: both;
margin-top: 20px;
text-shadow: 1px 0px 0 #145982;
font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
font-weight: bold;
}

.rules {
position: absolute;
top: 0;
right: 0;
width: 200px;
text-align: left;
color: #800000;
font-size: small;
font-family: Arial, Helvetica, sans-serif;
}

.flashMessage {
position: absolute;
top: 50px;
left: 50px;
width: 220px;
height: 60px;
text-align: left;
margin-right: 10px;
margin-leftt: 15px;
line-height: 1.5em;
color: #fff;
font-size: large;
font-family: Arial, Helvetica, sans-serif;
clear: both;
margin-top: 20px;
background: -moz-linear-gradient(left center, rgb(100, 0, 0),
rgb(179, 79, 79), rgb(121, 21, 21) );
background: -webkit-gradient(linear, left top, right top, color-stop(0, rgb(100, 0,
0) ), color-stop(0.50, rgb(179, 79, 79) ),
color-stop(1, rgb(121, 21, 21) ) );
}

#moveStatus {
clear: both;
float: left;
position: relative;
right: -35%;
text-align: left;
margin-top: 20px;
width: 215px;
background: -moz-linear-gradient(left center, rgb(200, 0, 0),
rgb(79, 79, 79), rgb(21, 21, 21) );
background: -webkit-gradient(linear, left top, right top, color-stop(0, rgb(200, 0,
0) ), color-stop(0.50, rgb(79, 79, 79) ),
color-stop(1, rgb(21, 21, 21) ) );
color: #fff;
text-shadow: 1px 1px 0 #145982;
font-family: Arial, Helvetica, sans-serif;
font-size: 15px;
font-weight: bold;
box-shadow: 3px 3px 3px #000000;
-moz-box-shadow: 3px 3px 3px #000000;
-webkit-box-shadow: 3px 3px 3px #000000;
}

.tile {
height: 50px;
width: 50px;
float: left;
position: relative;
right: -35%;
border: 2px solid #666666;
background-color: #ccc;
margin-right: 1px;
line-height: 2em;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: inset 0 0 3px #000;
box-shadow: inset 0 0 3px #000;
text-align: center;
cursor: move;
background: -moz-linear-gradient(left center, rgb(0, 0, 0),
rgb(79, 79, 79), rgb(21, 21, 21) );
background: -webkit-gradient(linear, left top, right top, color-stop(0, rgb(0, 0, 0)
), color-stop(0.50, rgb(79, 79, 79) ), color-stop(1, rgb(21, 21, 21) )
);
color: #fff;
font-size: x-large;
}

.canvas {
clear: both;
height: 200px;
width: 215px;
float: left;
position: relative;
right: -35%;
border: 2px solid #666666;
background-color: #ccc;
margin-right: 1px;
margin-top: 10px;
line-height: 2em;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: inset 0 0 3px #000;
box-shadow: inset 0 0 3px #000;
background: -moz-linear-gradient(left center, rgb(0, 0, 0),
rgb(79, 79, 79), rgb(21, 21, 21) );
background: -webkit-gradient(linear, left top, right top, color-stop(0, rgb(0, 0, 0)
), color-stop(0.50, rgb(79, 79, 79) ), color-stop(1, rgb(21, 21, 21) )
);
color: #fff;
font-size: x-large;
height: 200px;
}

.over {
border: 2px dashed #000;
}

Javascript Code:
// Global Variable
var myarray = [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11",
"12", "13", "14", "15", "" ];
var gameStatus = 0;
var moveCount = 0;
var positionDifference = 0;

// If you have 10 number of 200x200 images, use 9.
var imageNumber = Math.floor(Math.random()*9);
var sourceDiv = null;
var bestMove;
var bestTime;

$(document)
.ready(

function () {

Element.prototype.hasClassName = function(name) {
return new RegExp("(?:^|\\s+)" + name
+ "(?:\\s+|$)").test(this.className);
};

Element.prototype.addClassName = function(name) {
if (!this.hasClassName(name)) {
this.className = this.className ? [
this.className, name ].join(' ') : name;
}
};

Element.prototype.removeClassName = function(name) {
if (this.hasClassName(name)) {
var c = this.className;
this.className = c.replace(
new RegExp("(?:^|\\s+)" + name
+ "(?:\\s+|$)", "g"), "");
}
};



function handleDragStart(e) {
// Target (this) element is the source node.
sourceDiv = this;
this.style.opacity = '0.7';
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}

function handleDragEnter(e) {
// this / e.target is the current hover target.
this.addClassName('over');
}

function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault(); // Necessary. Allows us to
// drop.
}

e.dataTransfer.dropEffect = 'move'; // See the
// section on
// the
// DataTransfer
// object.
this.addClassName('over');
return false;
}

function handleDragLeave(e) {
this.removeClassName('over'); // this / e.target
// is previous
// target element.
}

function handleDrop(e) {
// this/e.target is current target element.
this.style.opacity = '1.0';
sourceDiv.style.opacity = '1.0';

if (e.stopPropagation) {
e.stopPropagation(); // Stops some browsers
// from redirecting.
}

// Stop Game when it is over
if (!gameStatus)
{

// Don't do anything if dropping the same column
// we're dragging.
if (sourceDiv != this) {

// Swap boxes only if one of them is BLANK
if (sourceDiv.innerHTML == ""
|| this.innerHTML == "") {

// Restrict box movements to one level (up,
// down, left, right)
positionDifference = Math.abs(sourceDiv.id
- this.id);

if (positionDifference == 1
|| positionDifference == 4) {

if (positionDifference == 4 || (!((sourceDiv.id%4==0 && this.id%4==1) || (sourceDiv.id%4==1 && this.id%4==0))))
{
// Set the source column's HTML to the
// HTML of the column we dropped on.
sourceDiv.innerHTML = this.innerHTML;
if (e.dataTransfer.getData('text/html') == null) {
this.innerHTML = "";
} else {
this.innerHTML = e.dataTransfer
.getData('text/html');
}

moveCount = moveCount + 1;

// Check Game Status
gameStatus = checkGameStatus();

if (gameStatus) {
if (bestMove == null || (bestMove > moveCount))
{
bestMove = moveCount;
localStorage.setItem('BEST_MOVE',bestMove);
document.getElementById("bestMove").innerHTML = bestMove + " moves";
}
document.getElementById("status").innerHTML = "You are Awesome!!. You took " + moveCount + " moves";
document.getElementById("flashMessage").innerHTML = "Game Over";
$('#flashMessage').slideDown('1000');
$('#flashMessage').delay('3000');
$('#flashMessage').slideUp('1000');
} else {
document.getElementById("status").innerHTML = "Number Of Moves :: "
+ moveCount;
}

drawCanvas();

}
else {
displayNotification();
}
}
else {
displayNotification();
}
} else {
displayNotification();
}

}
}
return false;
}

function handleDragEnd(e) {
// this/e.target is the source node.
var cols = document.querySelectorAll('.tile');
[].forEach.call(cols, function(col) {
col.removeClassName('over');
});

toggleFlashMessage();

}


$('#saveGameButton').click(function() {

if (!gameStatus)
{
for ( var i = 0; i < 16; i++) { myarray[i] = document.getElementById(i + 1).innerHTML; } localStorage.setItem('MY_ARRAY', myarray.toString()); localStorage.setItem('MOVE_COUNT', moveCount); localStorage.setItem('IMAGE_NUMBER',imageNumber); localStorage.setItem('GAME_STATUS',gameStatus); console.log("Move Count" +localStorage.getItem('MOVE_COUNT')); console.log("Image Number" +localStorage.getItem('IMAGE_NUMBER')); document.getElementById("flashMessage").innerHTML = "Game saved successfully"; $('#flashMessage').slideDown('1000'); $('#flashMessage').delay('1000'); $('#flashMessage').slideUp('1000'); } }); var cols = document.querySelectorAll('.tile'); [].forEach.call(cols, function(col) { col.addEventListener('dragstart', handleDragStart, false); col.addEventListener('dragenter', handleDragEnter, false); col.addEventListener('dragover', handleDragOver, false); col.addEventListener('dragleave', handleDragLeave, false); col.addEventListener('drop', handleDrop, false); col.addEventListener('dragend', handleDragEnd, false); }); }); function toggleFlashMessage() { $('#flashMessage').slideUp('1000'); } function initialize() { $('#notificationMessage').hide(); $('#flashMessage').hide(); if ((imageNumber = localStorage.getItem('IMAGE_NUMBER')) == null) { imageNumber = Math.floor(Math.random()*5); } if ((moveCount = localStorage.getItem('MOVE_COUNT')) == null) { moveCount = 0; document.getElementById("status").innerHTML = "Ready to start?"; } else { moveCount = parseInt(moveCount); if (moveCount != 0) { document.getElementById("status").innerHTML = "Number Of Moves :: " + moveCount; } else { document.getElementById("status").innerHTML = "Ready to start?"; } } if ((gameStatus = localStorage.getItem('GAME_STATUS')) == null) { gameStatus = 0; } else { gameStatus = parseInt(gameStatus); } if ((bestMove = localStorage.getItem('BEST_MOVE')) == null) { document.getElementById("bestMove").innerHTML = ""; } else { document.getElementById("bestMove").innerHTML = bestMove + " moves"; } if (localStorage.getItem('MY_ARRAY') == null) { // Shuffle Array Contents myarray.sort(function() { return 0.5 - Math.random(); }); } else { myarray = localStorage.getItem('MY_ARRAY').split(','); document.getElementById("flashMessage").innerHTML = "Game restored from previous session"; $('#flashMessage').slideDown('1000'); $('#flashMessage').delay('1000'); $('#flashMessage').slideUp('1000'); } // Populate Boxes with Numbers for ( var i = 0; i < myarray.length; i++) { document.getElementById(i + 1).innerHTML = myarray[i]; } drawCanvas(); } function clearStorage() { localStorage.removeItem('MOVE_COUNT'); localStorage.removeItem('MY_ARRAY'); localStorage.removeItem('IMAGE_NUMBER'); localStorage.removeItem('GAME_STATUS'); } function saveGame() { for ( var i = 0; i < 16; i++) { myarray[i] = document.getElementById(i + 1).innerHTML; } localStorage.setItem('MY_ARRAY', myarray.toString()); localStorage.setItem('MOVE_COUNT', moveCount); localStorage.setItem('IMAGE_NUMBER',imageNumber); localStorage.setItem('GAME_STATUS',gameStatus); console.log("Move Count" +localStorage.getItem('MOVE_COUNT')); console.log("Image Number" +localStorage.getItem('IMAGE_NUMBER')); } function checkGameStatus() { for ( var i = 1; i < myarray.length; i++) { var currentElement = document.getElementById(i); if (currentElement.innerHTML != i) { return 0; } } return 1; } function displayNotification() { document.getElementById("flashMessage").innerHTML = "Move not allowed"; $('#flashMessage').fadeIn('slow'); $('#flashMessage').delay(2000); $('#flashMessage').fadeOut('slow'); } function drawCanvas() { var canvas = document.getElementById("myCanvas"); var context = canvas.getContext("2d"); canvas.width = 200; canvas.height = 200; context.clearRect(0,0,200,200); var imageObj = new Image(); imageObj.onload = function(){ // draw cropped image var x = 0; var y = 0; var x1 = 0; var y1 = 0; var width = 50; var height = 50; var element; var elementValue; var elementId; for (var i=0;i<16;i++) { element = document.getElementById(i+1); elementValue = element.innerHTML; elementId = element.id; // Handle blank box if (elementValue == null || elementValue == "") { elementValue = "16"; } x = ((elementValue-1)%4) * 50; y = parseInt(((elementValue-1)/4)) * 50; x1 = ((elementId-1)%4) * 50; y1 = parseInt(((elementId-1)/4)) * 50; context.drawImage(imageObj, x, y, width, height, x1, y1, width, height); } }; imageObj.src = imageNumber+".jpg"; }



Instructions: Create .html, .css, .js files with the above code. Download JQuery 1.6.2 to the same folder. Add 10 (jpg) images of exact size 200x200px. Have all the 14 files in the same folder.


Reference: http://www.html5rocks.com/en/tutorials/dnd/basics/

No comments:

Post a Comment