|
(function() { |
|
angular.module("Game", []) |
|
.service("GameService", GameService); |
|
|
|
// Service |
|
function GameService(GridService) { |
|
var grid = GridService.grid; |
|
// Service variables |
|
this.game = { |
|
directions: { // define direction keymap for merge function |
|
37: { |
|
tileIncrement: 1, |
|
lineIncrement: grid.dim, |
|
reverse: false |
|
}, // left |
|
38: { |
|
tileIncrement: grid.dim, |
|
lineIncrement: 1, |
|
reverse: false |
|
}, // up |
|
39: { |
|
tileIncrement: 1, |
|
lineIncrement: grid.dim, |
|
reverse: true |
|
}, // right |
|
40: { |
|
tileIncrement: grid.dim, |
|
lineIncrement: 1, |
|
reverse: true |
|
} // down |
|
}, |
|
state: { |
|
win: false, |
|
gameover: false, |
|
continue: false |
|
}, |
|
moves: 0, |
|
score: { |
|
merge: 0, |
|
current: 0, |
|
best: 0, |
|
spm: 0 |
|
}, |
|
grid: grid, |
|
newGame: newGame, |
|
continueGame: continueGame, |
|
update: update, |
|
merge: merge, |
|
updateScore: updateScore, |
|
isGameOver: isGameOver |
|
}; |
|
|
|
// Service functions |
|
function newGame() { |
|
/* Start a new game */ |
|
this.state = { |
|
win: false, |
|
gameover: false, |
|
continue: false |
|
}; |
|
this.moves = 0; |
|
this.score.merge = 0; |
|
this.score.current = 0; |
|
this.score.spm = 0; |
|
this.grid.newTiles(); |
|
} |
|
|
|
function continueGame() { |
|
this.state. |
|
continue = true; |
|
} |
|
|
|
function update(event) { |
|
/* Update event based on direction key pressed */ |
|
event.preventDefault(); |
|
if (!this.state.gameover && (!this.state.win || this.state. |
|
continue)) { |
|
var direction = this.directions[event.which]; |
|
if (direction) { // if valid direction is in directions |
|
var tilesCopy = this.grid.copyTiles(); // make a deep copy of grid |
|
this.moves++; // update moves |
|
this.score.merge = this.merge(direction, updateTiles = true); // merge grid based on direction pressed |
|
this.updateScore(); // update score |
|
if (!this.grid.identicalTiles(tilesCopy)) { // if old gridCopy is not identical to the new grid, generate a new tile |
|
this.grid.setEmptyTile(); |
|
} |
|
this.isGameOver(); // check if game is over |
|
} |
|
} |
|
} |
|
|
|
function merge(direction, updateTiles) { |
|
/* Merge grid tiles based on direction using the directions matrix |
|
The merge function will perform three main phases: |
|
- Obtain the position of tiles for each line in the grid (number of lines = grid.dim) based on the direction matrix provided above. |
|
- For each line, take only the non-zero tiles, which allows for a simpler merge step on non-zero tiles. |
|
- Append the zeros to the front or back of the line depending on the direction selected (i.e. based on the value of direction.reverse). |
|
- Update the old grid values to the new merged values. |
|
- Update score and check win condition |
|
- Return mergeScore |
|
*/ |
|
|
|
// For each line in a grid, get positional tiles depending on direction pressed. |
|
var i = 0; |
|
var mergeScore = 0; |
|
while (i < this.grid.dim) { |
|
var k = 0; |
|
var positionTiles = []; |
|
var positionTile = i * direction.lineIncrement; // set up line increment |
|
while (k < this.grid.dim) { |
|
positionTiles.push(positionTile); |
|
positionTile += direction.tileIncrement; // set up tile increment |
|
k++; |
|
} |
|
|
|
// Get non-zero tiles from positional tiles |
|
var mergedTiles = []; |
|
for (var m = 0; m < positionTiles.length; m++) { |
|
var tile = this.grid.getTile(positionTiles[m]); |
|
if (tile) { |
|
mergedTiles.push(tile); |
|
} |
|
} |
|
|
|
// Merge non-zero tiles |
|
var position = 0; |
|
reverseTiles(mergedTiles, direction); // reverse tile for merge function if direction.reverse = true |
|
while (position < mergedTiles.length - 1) { |
|
if (mergedTiles[position] === mergedTiles[position + 1]) { |
|
mergeScore = mergedTiles[position] * 2; |
|
mergedTiles[position] = mergeScore; // update value of mergedTile position |
|
mergedTiles[position + 1] = 0; // update value of mergedTile position + 1 |
|
} |
|
position++; |
|
} |
|
mergedTiles = mergedTiles.filter(function(value) { |
|
return value !== 0; |
|
}); // strip mergedTiles of empty tiles |
|
reverseTiles(mergedTiles, direction); // reverse tile for merge function if direction.reverse = true |
|
|
|
if (mergedTiles.indexOf(2048) > -1) { // if 2048 tile is in mergedTiles, you win! |
|
this.state.win = true; |
|
} |
|
|
|
// Append zero tiles to the front/back of mergedTiles depending on direction pressed. |
|
var zeroLength = this.grid.dim - mergedTiles.length; |
|
for (var z = 0; z < zeroLength; z++) { |
|
if (direction.reverse) { |
|
mergedTiles.unshift(0); |
|
} else { |
|
mergedTiles.push(0); |
|
} |
|
} |
|
|
|
// Update actual grid tiles to new merged values if updateTiles == true |
|
if (updateTiles) { |
|
for (var tile = 0; tile < mergedTiles.length; tile++) { |
|
this.grid.setTile(positionTiles[tile], mergedTiles[tile]); |
|
} |
|
} |
|
i++; |
|
} |
|
return mergeScore; // return mergeScore |
|
} |
|
|
|
function isGameOver() { |
|
/* Check if game is over */ |
|
var mergeScore = 0; |
|
if (!this.grid.isEmpty()) { // if grid is completely full |
|
for (var d in this.directions) { |
|
var direction = this.directions[d]; |
|
mergeScore += this.merge(direction, updateTiles = false); |
|
} |
|
if (!mergeScore) { // if mergeScore = 0, then there are no merges left on a full board, hence game is over. |
|
this.state.gameover = true; |
|
} |
|
} |
|
} |
|
|
|
function updateScore() { |
|
/* Update the current and best score */ |
|
this.score.current += this.score.merge; // update score |
|
this.score.spm = this.score.current / this.moves; |
|
if (this.score.best < this.score.current) { // update best score |
|
this.score.best = this.score.current; |
|
} |
|
} |
|
|
|
function reverseTiles(tiles, direction) { |
|
/* Reverse tiles if direction.reverse is true */ |
|
if (direction.reverse) { |
|
tiles.reverse(); |
|
} |
|
} |
|
} |
|
})(); |