|
Part1-Basic In the tutorial, we are going to implement an n-puzzle. DesignThe design is pretty simple. We will have a Puzzle class and PuzzlePiece class. Puzzle class stores the state and the logics of the game while PuzzliePiece render the pieces of the puzzle. HTMLThe HTML is pretty simple. We only need a container to represent the puzzle board. <div style="width:400px; height:400px" id="board"> </div> PuzzleThe Puzzle has two parameters for the constructor. div is the HTML container and dim is the dimension of the puzzle. If dim is 3, it will be a 8-puzzle. If dim is 4, it will be a 15-puzzle. The constructor initializes the variables and create the pieces according to the dim. function Puzzle(div, dim) { this.div = div; this.cellSize = div.offsetWidth / dim; this.empty = dim * dim - 1; var item = {}; for (var i = 0, n = dim * dim; i < n; i++) { var newItem = new PuzzlePiece(this, i, dim, i == n - 1); item[i] = newItem; div.appendChild(newItem.div); } } appendChildThe appendChild() method adds a node after the last child node of the specified element node. So the resulting HTML is equivalent to <div style="width:400px; height:400px" id="board"> <div>1</div> <div>2</div> . . . </div> PuzzlePieceThe PuzzlePiece creates a div element as the physical presentation of a piece. function PuzzlePiece(board, i, dim, isEmpty) { this.div = document.createElement("div"); var current = i; if (!isEmpty) { this.div.innerHTML = i + 1; } this.div.style.position = 'absolute'; }; element.style.positionEach HTML element has a style property which represents the style object of the element's style attribute. Setting the position to absolute is the same as the following HTML <div style="position: absolute">1</div> absolute means the element will be positioned by the CSS properties namely left and top. InitializationFinally, we initialize the puzzle by getting the element from DOM and creating a new 15-puzzle with 4x4 dimension. var board = document.getElementById("board"); var puzzle = new Puzzle(board, 4); document.getElementByIddocument.getElementById() method returns a reference to the first object with the specified ID. Since it is used frequently, some JavaScript libraries shortcut the method to $(). Positioning the PiecesThe pieces are now clustered. update() function make sure the pieces are being in-place. function PuzzlePiece(board, i, dim, isEmpty) { this.div = document.createElement("div"); var size = board.cellSize; var current = i; if (!isEmpty) { this.div.innerHTML = i + 1; } this.div.style.position = 'absolute'; this.update = function(i) { current = i; var y = Math.floor(i / dim); var x = i % dim; var pos = findPos(board.div); this.div.style.left = (pos.left + x * size) + "px"; this.div.style.top = (pos.top + y * size) + "px"; } this.update(i); }; findPosWe have a utility functions to find the top and left value of the board (or any element when necessary). function findPos(obj) { var curleft = curtop = 0; if (obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); } return {left: curleft, top: curtop}; } ![]() Refining the UIPosition the pieces correctly is not appealing enough. formatDiv() is added to function PuzzlePiece(board, i, dim, isEmpty) { . . . function formatDiv(div) { div.style.textAlign = 'center'; div.style.fontFamily = 'Arial'; div.style.border = 'solid 1px black'; div.style.fontSize = Math.floor(size * 0.9) + 'px'; div.style.width = size + 'px'; div.style.height = size + 'px'; } this.update(i); formatDiv(this.div); }; Function DeclarationYou can define a JavaScript function in two ways: function fn(param){} or fn = function(param){} However, you can only define a public function in one way: this.fn = function(param){} The scope of this will only belongs to the public function. this in a private function means the private function is a constructor. The following code will not work for PuzzlePiece because this referring the object created by formatDiv: function formatDiv() { this.div.style.textAlign = 'center'; this.div.style.fontFamily = 'Arial'; this.div.style.border = 'solid 1px black'; this.div.style.fontSize = Math.floor(size * 0.9) + 'px'; this.div.style.width = size + 'px'; this.div.style.height = size + 'px'; } ![]() Implementing the LogicsPuzzlePieceonclick event is defined so the pieces will react to the mouse click. this.div.onclick = function() { board.move(current); } PuzzlemoveValidates the move before swapping the piece with the empty block this.move = function(a) { if (a == this.empty) return; var ay = Math.floor(a / dim); var ax = a % dim; var by = Math.floor(this.empty / dim); var bx = this.empty % dim; if ((ax == bx && Math.abs(ay - by) == 1) || (ay == by && Math.abs(ax - bx) == 1)) { this.swap(a, this.empty); } } swapSwaps two pieces this.swap = function(a, b) { var x = item[a]; item[a] = item[b]; item[b] = x; item[a].update(a); item[b].update(b); this.empty = a; } randomizeThe game is meaningless if all pieces are in-place this.randomize = function() { var n = dim * dim; for (var i = 0; i < n * 2; i++) { var a = Math.floor(Math.random() * n); this.swap(a, this.empty); } } Final Code
|