function GameManager(size, InputManager, Actuator, StorageManager) {
this.size = size;
this.inputManager = new InputManager;
this.storageManager = new StorageManager;
this.actuator = new Actuator;
this.startTiles = 2;
this.inputManager.on("move", this.move.bind(this));
this.inputManager.on("restart", this.restart.bind(this));
this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
this.setup();
}
function kcal(exp) {
var kcal = [];
kcal[2]=200;
kcal[4]=250;
kcal[8]=320;
kcal[16]=400;
kcal[32]=500;
kcal[64]=650;
kcal[128]=820;
kcal[256]=1000;
kcal[512]=1200;
kcal[1024]=1500;
kcal[2048]=2000;
kcal[4096]=3000;
kcal[8192]=5000;
return kcal[exp];
}
GameManager.prototype.restart = function () {
this.storageManager.clearGameState();
this.actuator.continueGame();
this.setup();
};
GameManager.prototype.crowd = function () {
this.storageManager.clearGameState();
this.actuator.continueGame();
this.grid = new Grid(this.size);
this.score = 0;
this.points = 0;
this.over = false;
this.won = false;
this.keepPlaying = false;
this.actuate();
var counter = 0;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 4; j++) {
counter++;
var value = Math.pow(2, counter);
var tile = new Tile({ x: j, y: i }, value);
if (value <= 8192) this.grid.insertTile(tile);
}
}
};
GameManager.prototype.keepPlaying = function () {
this.keepPlaying = true;
this.actuator.continueGame();
};
GameManager.prototype.isGameTerminated = function () {
if (this.over || (this.won && !this.keepPlaying)) {
return true;
} else {
return false;
}
};
GameManager.prototype.setup = function () {
var previousState = this.storageManager.getGameState();
if (previousState) {
this.grid = new Grid(previousState.grid.size,
previousState.grid.cells);
this.score = previousState.score;
this.points = previousState.points;
this.over = previousState.over;
this.won = previousState.won;
this.keepPlaying = previousState.keepPlaying;
} else {
this.grid = new Grid(this.size);
this.score = 0;
this.points = 0;
this.over = false;
this.won = false;
this.keepPlaying = false;
this.addStartTiles();
}
this.localizeElements();
this.fillLegend();
this.actuate();
};
GameManager.prototype.localizeElements = function () {
var elementArray = [
'game-intro',
'restart-button',
'retry-button',
'keep-playing-button',
'game-explanation',
'disclaimer',
'tile-legend',
'credits'
];
for (var i in elementArray) {
LocalizeElement(elementArray[i]);
}
};
GameManager.prototype.fillLegend = function () {
var legend = document.getElementsByClassName("tile-legend");
for (var i = 1; i <= 13; i++) {
var exp = Math.pow(2, i);
var row = document.createElement("div");
var grid = document.createElement("div");
var cell = document.createElement("div");
var img = document.createElement("img");
var p = document.createElement("p");
row.classList.add('legend-row');
grid.classList.add('legend-grid');
cell.classList.add('legend-cell');
cell.classList.add('cell-' + exp);
img.src = "style/img/" + exp + ".jpg";
cell.appendChild(img);
grid.appendChild(cell);
row.appendChild(grid);
p.textContent = Localize(exp) + " (" + kcal(exp) + " Kcal)";
row.appendChild(p);
legend[0].appendChild(row);
}
};
GameManager.prototype.addStartTiles = function () {
for (var i = 0; i < this.startTiles; i++) {
this.addRandomTile();
}
};
GameManager.prototype.addRandomTile = function () {
if (this.grid.cellsAvailable()) {
var value = Math.random() < 0.9 ? 2 : 4;
var tile = new Tile(this.grid.randomAvailableCell(), value);
this.grid.insertTile(tile);
}
};
GameManager.prototype.actuate = function () {
if (this.storageManager.getBestScore() < this.score) {
this.storageManager.setBestScore(this.score);
}
if (this.storageManager.getBestPoints() < this.points) {
this.storageManager.setBestPoints(this.points);
}
if (this.over) {
this.storageManager.clearGameState();
} else {
this.storageManager.setGameState(this.serialize());
}
this.actuator.actuate(this.grid, {
score: this.score,
points: this.points,
over: this.over,
won: this.won,
bestScore: this.storageManager.getBestScore(),
bestPoints: this.storageManager.getBestPoints(),
terminated: this.isGameTerminated()
});
};
GameManager.prototype.serialize = function () {
return {
grid: this.grid.serialize(),
score: this.score,
points: this.points,
over: this.over,
won: this.won,
keepPlaying: this.keepPlaying
};
};
GameManager.prototype.prepareTiles = function () {
this.grid.eachCell(function (x, y, tile) {
if (tile) {
tile.mergedFrom = null;
tile.savePosition();
}
});
};
GameManager.prototype.moveTile = function (tile, cell) {
this.grid.cells[tile.x][tile.y] = null;
this.grid.cells[cell.x][cell.y] = tile;
tile.updatePosition(cell);
};
GameManager.prototype.move = function (direction) {
var self = this;
if (this.isGameTerminated()) return;
var cell, tile;
var vector = this.getVector(direction);
var traversals = this.buildTraversals(vector);
var moved = false;
this.prepareTiles();
traversals.x.forEach(function (x) {
traversals.y.forEach(function (y) {
cell = { x: x, y: y };
tile = self.grid.cellContent(cell);
if (tile) {
var positions = self.findFarthestPosition(cell, vector);
var next = self.grid.cellContent(positions.next);
if (next && next.value === tile.value && !next.mergedFrom) {
var merged = new Tile(positions.next, tile.value * 2);
merged.mergedFrom = [tile, next];
self.grid.insertTile(merged);
self.grid.removeTile(tile);
tile.updatePosition(positions.next);
self.points += kcal(tile.value) * 2;
if (merged.value > self.score) self.score = merged.value;
if (merged.value === 2048 || merged.value === 2048) self.won = true;
} else {
self.moveTile(tile, positions.farthest);
}
if (!self.positionsEqual(cell, tile)) {
moved = true;
}
}
});
});
if (moved) {
this.addRandomTile();
if (!this.movesAvailable()) {
this.over = true;
}
this.actuate();
}
};
GameManager.prototype.getVector = function (direction) {
var map = {
0: { x: 0, y: -1 },
1: { x: 1, y: 0 },
2: { x: 0, y: 1 },
3: { x: -1, y: 0 }
};
return map[direction];
};
GameManager.prototype.buildTraversals = function (vector) {
var traversals = { x: [], y: [] };
for (var pos = 0; pos < this.size; pos++) {
traversals.x.push(pos);
traversals.y.push(pos);
}
if (vector.x === 1) traversals.x = traversals.x.reverse();
if (vector.y === 1) traversals.y = traversals.y.reverse();
return traversals;
};
GameManager.prototype.findFarthestPosition = function (cell, vector) {
var previous;
do {
previous = cell;
cell = { x: previous.x + vector.x, y: previous.y + vector.y };
} while (this.grid.withinBounds(cell) &&
this.grid.cellAvailable(cell));
return {
farthest: previous,
next: cell
};
};
GameManager.prototype.movesAvailable = function () {
return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};
GameManager.prototype.tileMatchesAvailable = function () {
var self = this;
var tile;
for (var x = 0; x < this.size; x++) {
for (var y = 0; y < this.size; y++) {
tile = this.grid.cellContent({ x: x, y: y });
if (tile) {
for (var direction = 0; direction < 4; direction++) {
var vector = self.getVector(direction);
var cell = { x: x + vector.x, y: y + vector.y };
var other = self.grid.cellContent(cell);
if (other && other.value === tile.value) {
return true;
}
}
}
}
}
return false;
};
GameManager.prototype.positionsEqual = function (first, second) {
return first.x === second.x && first.y === second.y;
};