Path: blob/main/projects/doge2048/js/game_manager.js
1836 views
function GameManager(size, InputManager, Actuator, ScoreManager) {1this.size = size; // Size of the grid2this.inputManager = new InputManager;3this.scoreManager = new ScoreManager;4this.actuator = new Actuator;56this.startTiles = 2;78this.inputManager.on("move", this.move.bind(this));9this.inputManager.on("restart", this.restart.bind(this));10this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));11this.inputManager.on("showInfo", this.showInfo.bind(this));12this.inputManager.on("hideInfo", this.hideInfo.bind(this));1314this.setup();15}1617// Restart the game18GameManager.prototype.restart = function () {19this.actuator.continue();20this.setup();21};2223// Keep playing after winning24GameManager.prototype.keepPlaying = function () {25this.keepPlaying = true;26this.actuator.continue();27};2829GameManager.prototype.showInfo = function () {30this.actuator.showInfo();31};323334GameManager.prototype.hideInfo = function () {35this.actuator.hideInfo();36};3738GameManager.prototype.isGameTerminated = function () {39if (this.over || (this.won && !this.keepPlaying)) {40return true;41} else {42return false;43}44};4546// Set up the game47GameManager.prototype.setup = function () {48this.grid = new Grid(this.size);4950this.score = 0;51this.over = false;52this.won = false;53this.keepPlaying = false;5455// Add the initial tiles56this.addStartTiles();5758// Update the actuator59this.actuate();60};6162// Set up the initial tiles to start the game with63GameManager.prototype.addStartTiles = function () {64for (var i = 0; i < this.startTiles; i++) {65this.addRandomTile();66}67};6869// Adds a tile in a random position70GameManager.prototype.addRandomTile = function () {71if (this.grid.cellsAvailable()) {72var value = Math.random() < 0.9 ? 2 : 4;73var tile = new Tile(this.grid.randomAvailableCell(), value);7475this.grid.insertTile(tile);76}77};7879// Sends the updated grid to the actuator80GameManager.prototype.actuate = function () {81if (this.scoreManager.get() < this.score) {82this.scoreManager.set(this.score);83}8485this.actuator.actuate(this.grid, {86score: this.score,87over: this.over,88won: this.won,89bestScore: this.scoreManager.get(),90terminated: this.isGameTerminated()91});9293};9495// Save all tile positions and remove merger info96GameManager.prototype.prepareTiles = function () {97this.grid.eachCell(function (x, y, tile) {98if (tile) {99tile.mergedFrom = null;100tile.savePosition();101}102});103};104105// Move a tile and its representation106GameManager.prototype.moveTile = function (tile, cell) {107this.grid.cells[tile.x][tile.y] = null;108this.grid.cells[cell.x][cell.y] = tile;109tile.updatePosition(cell);110};111112// Move tiles on the grid in the specified direction113GameManager.prototype.move = function (direction) {114// 0: up, 1: right, 2:down, 3: left115var self = this;116117if (this.isGameTerminated()) return; // Don't do anything if the game's over118119var cell, tile;120121var vector = this.getVector(direction);122var traversals = this.buildTraversals(vector);123var moved = false;124125// Save the current tile positions and remove merger information126this.prepareTiles();127128// Traverse the grid in the right direction and move tiles129traversals.x.forEach(function (x) {130traversals.y.forEach(function (y) {131cell = { x: x, y: y };132tile = self.grid.cellContent(cell);133134if (tile) {135var positions = self.findFarthestPosition(cell, vector);136var next = self.grid.cellContent(positions.next);137138// Only one merger per row traversal?139if (next && next.value === tile.value && !next.mergedFrom) {140var merged = new Tile(positions.next, tile.value * 2);141merged.mergedFrom = [tile, next];142143self.grid.insertTile(merged);144self.grid.removeTile(tile);145146// Converge the two tiles' positions147tile.updatePosition(positions.next);148149// Update the score150self.score += merged.value;151152// The mighty 2048 tile153if (merged.value === 2048) self.won = true;154} else {155self.moveTile(tile, positions.farthest);156}157158if (!self.positionsEqual(cell, tile)) {159moved = true; // The tile moved from its original cell!160}161}162});163});164165if (moved) {166this.addRandomTile();167168if (!this.movesAvailable()) {169this.over = true; // Game over!170}171172this.actuate();173}174};175176// Get the vector representing the chosen direction177GameManager.prototype.getVector = function (direction) {178// Vectors representing tile movement179var map = {1800: { x: 0, y: -1 }, // up1811: { x: 1, y: 0 }, // right1822: { x: 0, y: 1 }, // down1833: { x: -1, y: 0 } // left184};185186return map[direction];187};188189// Build a list of positions to traverse in the right order190GameManager.prototype.buildTraversals = function (vector) {191var traversals = { x: [], y: [] };192193for (var pos = 0; pos < this.size; pos++) {194traversals.x.push(pos);195traversals.y.push(pos);196}197198// Always traverse from the farthest cell in the chosen direction199if (vector.x === 1) traversals.x = traversals.x.reverse();200if (vector.y === 1) traversals.y = traversals.y.reverse();201202return traversals;203};204205GameManager.prototype.findFarthestPosition = function (cell, vector) {206var previous;207208// Progress towards the vector direction until an obstacle is found209do {210previous = cell;211cell = { x: previous.x + vector.x, y: previous.y + vector.y };212} while (this.grid.withinBounds(cell) &&213this.grid.cellAvailable(cell));214215return {216farthest: previous,217next: cell // Used to check if a merge is required218};219};220221GameManager.prototype.movesAvailable = function () {222return this.grid.cellsAvailable() || this.tileMatchesAvailable();223};224225// Check for available matches between tiles (more expensive check)226GameManager.prototype.tileMatchesAvailable = function () {227var self = this;228229var tile;230231for (var x = 0; x < this.size; x++) {232for (var y = 0; y < this.size; y++) {233tile = this.grid.cellContent({ x: x, y: y });234235if (tile) {236for (var direction = 0; direction < 4; direction++) {237var vector = self.getVector(direction);238var cell = { x: x + vector.x, y: y + vector.y };239240var other = self.grid.cellContent(cell);241242if (other && other.value === tile.value) {243return true; // These two tiles can be merged244}245}246}247}248}249250return false;251};252253GameManager.prototype.positionsEqual = function (first, second) {254return first.x === second.x && first.y === second.y;255};256257258