The objective of the puzzle is to place the tiles in order by making sliding moves to
the empty space.
The example defines a single object named SlidingP uzzle, wh ic h con ta ins the fol-
lowing properties:
tiles: a two-dimensional array of tiles.
empty: a plain object that holds the position of th e empty space.
tileSize: holds the display size (in pixels) of one tile.
numbers: a two-dimensiona l 4 × 4 array of numbers from one to 16. This array
is used to mark the starting positions of tiles. Because not all starting positions
are solvable (in fact, half of the starting positions are impossible to solve), the
progr am starts with an ordered arr ay of 16 numbers and shues them before
initializing th e graphical interface of the puzzle. The position of number 16 is
initialized as an empty sp ace.
The SlidingPuzzle object also has some methods:
setTileSize(): sets the tileSize property.
shuffle(): shues the two-dimensional numbe rs array.
start(): initializes the graphical interface of the puzzle.
translateMove(): a static me thod that translates an integer from zero to three
into an ac tual m ove towards one of the f our directions (left, r ight, up, or down).
If a move is not possible (because it would fall o the frame), the method returns
false.
The example allows the player to interact with the puzzle using either a mouse or a
keyboard. A mouse click on a tile that can possibly move (i.e., has empty space at
one of its side s) moves that tile. Alternatively, pressing one of the arrow keys moves
whichever tile is f ree to move in that direction. The following are the two event-
handler functions to handle mouse and keyboard events:
moveTile(): handles mouse clicks. If the clicked tile can move, th en the han-
dler moves it.
findAndMoveTile(): handles key presses. It searches for the tile that c an
move and if such a tile exists, it fires a click event on that tile to actually move
it.
296 Appendix B. Ways to Continue
Here’s the complete code for th e 15-puzzle. The code is w ell co mmented so you
should have no diculties understanding it:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>15 Sliding Puzzle</title>
<style>
/*
* Tiles must be absolutely positioned so you have better control
* of their position. Note that all size-related properties are
* computed and set dynamically using the jQuery css() method.
*/
.tile {
border-style: outset;
background-color: #bbb;
color: #777;
text-align: center;
font-family: fantasy;
position: absolute;
}
/*
* This is the puzzle frame. It should have position other than
* static if its absolutely positioned children (tiles) are to
* respect its position.
*/
#puzzle {
position: relative;
}
</style>
<script src = "http://code.jquery.com/jquery-2.1.1.min.js">
</script>
<script>
/*
* The constructor
*/
var SlidingPuzzle = function() {
this.tiles = [];
this.empty = {};
this.numbers = [[1, 2, 3, 4], [ 5, 6, 7, 8],
[9, 10, 11, 12], [13, 14, 15, 16]];
this.tileSize = null;
};
/*
* Sets the display size of a tile. All size related CSS properties
* will be set according to this value.
B.4. jQu ery 297
* Parameters:
* s: size of a tile in pixels.
*/
SlidingPuzzle.prototype.setTileSize = function(s) {
this.tileSize = s;
};
/*
* Shuffles the numbers. Call this method before initializing the
* graphical interface with start(). The method tries to move the
* number 16 (which is a placeholder for the empty space) in random
* directions in the two-dimensional numbers array 100000 times.
* If a call to translateMove() returns true, then it is possible
* to move the empty space from its current position (x, y) in the
* direction stored in the move object.
*/
SlidingPuzzle.prototype.shuffle = function() {
var n;
var x = 3, y = 3;
var move = {x: 0, y: 0};
for (n = 1; n < 100000; n++) {
if (SlidingPuzzle.translateMove(Math.floor(Math.random() * 4),
x, y, move)) {
this.numbers[x][y] = this.numbers[x + move.x][y + move.y];
x += move.x;
y += move.y;
this.numbers[x][y] = 16;
}
}
};
/*
* Initializes the graphical interface of the puzzle.
* Parameters:
* id: the ID of an HTML element to hold the puzzle.
*/
SlidingPuzzle.prototype.start = function(id) {
var x, y;
for (x = 0; x < 4; x++) {
this.tiles[x] = [];
for (y = 0; y < 4; y++)
{
if (this.numbers[x][y] < 16) {
//If the number does not represent the empty space, then create a
//tile and append it to the puzzle frame. Next, attach a click
//event handler to the created tile. Then set the position of the
//tile. Using data(), associate the jQuery object representing
//the tile with the position of the tile and the reference to the
//puzzle frame. All these data will be used by event handlers
//because it is not possible to pass arguments to them. The
//mouseover event handler is defined so that the mouse cursor will
298 Appendix B. Ways to Continue
//not change to the text select form when over text, but rather
//keep its normal arrow appearance. Note that ’this’ inside the
//event-handler definition refers to the tile over which the mouse
//cursor is placed and not to the SlidingPuzzle object instance.
this.tiles[x][y] = $("<div class=’tile’>" +
this.numbers[x][y] + "</div>").appendTo("#" + id);
this.tiles[x][y].click(this.moveTile);
this.tiles[x][y].css("top", x * 1.2 * this.tileSize);
this.tiles[x][y].css("left", y * 1.2 * this.tileSize);
this.tiles[x][y].data("x", x);
this.tiles[x][y].data("y", y);
this.tiles[x][y].data("puzzle", this);
this.tiles[x][y].mouseover(function(){
$(this).css("cursor", "default");
});
}
else {
//If the number represents the empty space, then store its position.
this.empty.x = x;
this.empty.y = y;
}
}
}
//Set the dimensions of the puzzle frame and tiles:
$("#" + id).css("width", 4 * 1.2 * this.tileSize);
$("#" + id).css("height", 4 * 1.2 * this.tileSize);
$(".tile").css("width", this.tileSize);
$(".tile").css("height", this.tileSize);
$(".tile").css("border-radius", 0.2 * this.tileSize);
$(".tile").css("border-width", 0.1 * this.tileSize);
$(".tile").css("fontSize", 0.8 * this.tileSize);
//Attach the keydown event handler to the document. Associate also
//a reference to the puzzle with the document. This is necessary
//because we need to refer to the puzzle from within the
//findAndMoveTile() event-handler function but it is not possible
//to pass arguments to it.
$(document).keydown(this.findAndMoveTile);
$(document).data("puzzle", this);
};
/*
* A keyboard event handler, which is fired on the document. ’this’
* therefore refers to the document object. If a move in the
* desired direction is possible, then it triggers a click event
* on the tile that can move. Since arrow keys have codes from 37
* to 40 (stored in the which property of the Event object), we
* need to subtract 37 before passing the desired move to
* translateMove().
* Parameters:
* event: an Event object holding the additional information
* about the actual event that triggered findAndMoveTile().
*/
B.4. jQu ery 299
SlidingPuzzle.prototype.findAndMoveTile = function(event) {
var move = {x: 0, y: 0};
//Retrieve a reference to the puzzle stored in the document object:
var pzzl = $(this).data("puzzle");
if (SlidingPuzzle.translateMove(event.which - 37, pzzl.empty.x,
pzzl.empty.y, move)) {
pzzl.tiles[pzzl.empty.x + move.x][pzzl.empty.y +
move.y].trigger("click");
}
};
/*
* A mouse click event handler. It is triggered on the tile that
* should move. ’this’ therefore refers to that specific tile.
*/
SlidingPuzzle.prototype.moveTile = function() {
var i;
//Fetch the tile’s current position and a reference to the puzzle:
var x = $(this).data("x");
var y = $(this).data("y");
var pzzl = $(this).data("puzzle");
if (Math.abs(x - pzzl.empty.x) + Math.abs(y - pzzl.empty.y) == 1){
//Move only if the tile is currently beside the empty position.
//First, insert the tile to the place of the empty position in the
//"tiles" array. Next, use "data()" to update the position
//information of the tile. Then animate the tile to its new
//position. Finally, update the "empty" object to hold the new
//position of the empty space.
pzzl.tiles[pzzl.empty.x][pzzl.empty.y] = $(this);
pzzl.tiles[pzzl.empty.x][pzzl.empty.y].data("x", pzzl.empty.x);
pzzl.tiles[pzzl.empty.x][pzzl.empty.y].data("y", pzzl.empty.y);
pzzl.tiles[pzzl.empty.x][pzzl.empty.y].animate({
"top": pzzl.empty.x * 1.2 * pzzl.tileSize,
"left": pzzl.empty.y * 1.2 * pzzl.tileSize
}, 200);
pzzl.empty.x = x;
pzzl.empty.y = y;
}
};
/*
* A static method that translates an integer from zero to three to
* an actual move.
* Parameters:
* n [in]: an integer between zero and three specifying the
* desired move.
* x, y [in]: the coordinates specifying the current position of
* the tile to be moved.
* mov [out]: a reference to the object that will hold the x and
* y directions of the move. The values are only valid
* if the method returns true at the same time.
300 Appendix B. Ways to Continue
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.182.250