197 lines
5.5 KiB
JavaScript
197 lines
5.5 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule GameBoard
|
|
* @flow
|
|
*/
|
|
'use strict';
|
|
|
|
// NB: Taken straight from: https://github.com/IvanVergiliev/2048-react/blob/master/src/board.js
|
|
// with no modificiation except to format it for CommonJS and fix lint/flow errors
|
|
|
|
var rotateLeft = function (matrix) {
|
|
var rows = matrix.length;
|
|
var columns = matrix[0].length;
|
|
var res = [];
|
|
for (var row = 0; row < rows; ++row) {
|
|
res.push([]);
|
|
for (var column = 0; column < columns; ++column) {
|
|
res[row][column] = matrix[column][columns - row - 1];
|
|
}
|
|
}
|
|
return res;
|
|
};
|
|
|
|
var Tile = function (value?: number, row?: number, column?: number) {
|
|
this.value = value || 0;
|
|
this.row = row || -1;
|
|
|
|
this.column = column || -1;
|
|
this.oldRow = -1;
|
|
this.oldColumn = -1;
|
|
this.markForDeletion = false;
|
|
this.mergedInto = null;
|
|
this.id = Tile.id++;
|
|
};
|
|
|
|
Tile.id = 0;
|
|
|
|
Tile.prototype.moveTo = function (row, column) {
|
|
this.oldRow = this.row;
|
|
this.oldColumn = this.column;
|
|
this.row = row;
|
|
this.column = column;
|
|
};
|
|
|
|
Tile.prototype.isNew = function () {
|
|
return this.oldRow === -1 && !this.mergedInto;
|
|
};
|
|
|
|
Tile.prototype.hasMoved = function () {
|
|
return (this.fromRow() !== -1 && (this.fromRow() !== this.toRow() || this.fromColumn() !== this.toColumn())) ||
|
|
this.mergedInto;
|
|
};
|
|
|
|
Tile.prototype.fromRow = function () {
|
|
return this.mergedInto ? this.row : this.oldRow;
|
|
};
|
|
|
|
Tile.prototype.fromColumn = function () {
|
|
return this.mergedInto ? this.column : this.oldColumn;
|
|
};
|
|
|
|
Tile.prototype.toRow = function () {
|
|
return this.mergedInto ? this.mergedInto.row : this.row;
|
|
};
|
|
|
|
Tile.prototype.toColumn = function () {
|
|
return this.mergedInto ? this.mergedInto.column : this.column;
|
|
};
|
|
|
|
var Board = function () {
|
|
this.tiles = [];
|
|
this.cells = [];
|
|
for (var i = 0; i < Board.size; ++i) {
|
|
this.cells[i] = [this.addTile(), this.addTile(), this.addTile(), this.addTile()];
|
|
}
|
|
this.addRandomTile();
|
|
this.setPositions();
|
|
this.won = false;
|
|
};
|
|
|
|
Board.prototype.addTile = function () {
|
|
var res = new Tile();
|
|
Tile.apply(res, arguments);
|
|
this.tiles.push(res);
|
|
return res;
|
|
};
|
|
|
|
Board.size = 4;
|
|
|
|
Board.prototype.moveLeft = function () {
|
|
var hasChanged = false;
|
|
for (var row = 0; row < Board.size; ++row) {
|
|
var currentRow = this.cells[row].filter(function (tile) { return tile.value !== 0; });
|
|
var resultRow = [];
|
|
for (var target = 0; target < Board.size; ++target) {
|
|
var targetTile = currentRow.length ? currentRow.shift() : this.addTile();
|
|
if (currentRow.length > 0 && currentRow[0].value === targetTile.value) {
|
|
var tile1 = targetTile;
|
|
targetTile = this.addTile(targetTile.value);
|
|
tile1.mergedInto = targetTile;
|
|
var tile2 = currentRow.shift();
|
|
tile2.mergedInto = targetTile;
|
|
targetTile.value += tile2.value;
|
|
}
|
|
resultRow[target] = targetTile;
|
|
this.won = this.won || (targetTile.value === 2048);
|
|
hasChanged = hasChanged || (targetTile.value !== this.cells[row][target].value);
|
|
}
|
|
this.cells[row] = resultRow;
|
|
}
|
|
return hasChanged;
|
|
};
|
|
|
|
Board.prototype.setPositions = function () {
|
|
this.cells.forEach(function (row, rowIndex) {
|
|
row.forEach(function (tile, columnIndex) {
|
|
tile.oldRow = tile.row;
|
|
tile.oldColumn = tile.column;
|
|
tile.row = rowIndex;
|
|
tile.column = columnIndex;
|
|
tile.markForDeletion = false;
|
|
});
|
|
});
|
|
};
|
|
|
|
Board.fourProbability = 0.1;
|
|
|
|
Board.prototype.addRandomTile = function () {
|
|
var emptyCells = [];
|
|
for (var r = 0; r < Board.size; ++r) {
|
|
for (var c = 0; c < Board.size; ++c) {
|
|
if (this.cells[r][c].value === 0) {
|
|
emptyCells.push({r: r, c: c});
|
|
}
|
|
}
|
|
}
|
|
var index = Math.floor(Math.random() * emptyCells.length);
|
|
var cell = emptyCells[index];
|
|
var newValue = Math.random() < Board.fourProbability ? 4 : 2;
|
|
this.cells[cell.r][cell.c] = this.addTile(newValue);
|
|
};
|
|
|
|
Board.prototype.move = function (direction) {
|
|
// 0 -> left, 1 -> up, 2 -> right, 3 -> down
|
|
this.clearOldTiles();
|
|
for (var i = 0; i < direction; ++i) {
|
|
this.cells = rotateLeft(this.cells);
|
|
}
|
|
var hasChanged = this.moveLeft();
|
|
for (var i = direction; i < 4; ++i) {
|
|
this.cells = rotateLeft(this.cells);
|
|
}
|
|
if (hasChanged) {
|
|
this.addRandomTile();
|
|
}
|
|
this.setPositions();
|
|
return this;
|
|
};
|
|
|
|
Board.prototype.clearOldTiles = function () {
|
|
this.tiles = this.tiles.filter(function (tile) { return tile.markForDeletion === false; });
|
|
this.tiles.forEach(function (tile) { tile.markForDeletion = true; });
|
|
};
|
|
|
|
Board.prototype.hasWon = function () {
|
|
return this.won;
|
|
};
|
|
|
|
Board.deltaX = [-1, 0, 1, 0];
|
|
Board.deltaY = [0, -1, 0, 1];
|
|
|
|
Board.prototype.hasLost = function () {
|
|
var canMove = false;
|
|
for (var row = 0; row < Board.size; ++row) {
|
|
for (var column = 0; column < Board.size; ++column) {
|
|
canMove = canMove || (this.cells[row][column].value === 0);
|
|
for (var dir = 0; dir < 4; ++dir) {
|
|
var newRow = row + Board.deltaX[dir];
|
|
var newColumn = column + Board.deltaY[dir];
|
|
if (newRow < 0 || newRow >= Board.size || newColumn < 0 || newColumn >= Board.size) {
|
|
continue;
|
|
}
|
|
canMove = canMove || (this.cells[row][column].value === this.cells[newRow][newColumn].value);
|
|
}
|
|
}
|
|
}
|
|
return !canMove;
|
|
};
|
|
|
|
module.exports = Board;
|