1function GameManager(size, InputManager, Actuator, ScoreManager) { 2 this.size = size; // Size of the grid 3 this.inputManager = new InputManager; 4 this.scoreManager = new ScoreManager; 5 this.actuator = new Actuator; 6 7 this.startTiles = 2; 8 9 this.inputManager.on("move", this.move.bind(this)); 10 this.inputManager.on("restart", this.restart.bind(this)); 11 this.inputManager.on("keepPlaying", this.keepPlaying.bind(this)); 12 13 this.setup(); 14} 15 16// Restart the game 17GameManager.prototype.restart = function () { 18 this.actuator.continue(); 19 this.setup(); 20}; 21 22// Keep playing after winning 23GameManager.prototype.keepPlaying = function () { 24 this.keepPlaying = true; 25 this.actuator.continue(); 26}; 27 28GameManager.prototype.isGameTerminated = function () { 29 if (this.over || (this.won && !this.keepPlaying)) { 30 return true; 31 } else { 32 return false; 33 } 34}; 35 36// Set up the game 37GameManager.prototype.setup = function () { 38 this.grid = new Grid(this.size); 39 40 this.score = 0; 41 this.over = false; 42 this.won = false; 43 this.keepPlaying = false; 44 45 // Add the initial tiles 46 //console.log('test'); 47 this.addStartTiles(); 48 //console.log('Added start tiles'); 49 50 // Update the actuator 51 this.actuate(); 52}; 53 54// Set up the initial tiles to start the game with 55GameManager.prototype.addStartTiles = function () { 56 for (var i = 0; i < this.startTiles; i++) { 57 this.addRandomTile(); 58 } 59}; 60 61// Adds a tile in a random position 62GameManager.prototype.addRandomTile = function () { 63 if (this.grid.cellsAvailable()) { 64 var value = Math.random() < 0.9 ? 1 : 2; 65 var tile = new Tile(this.grid.randomAvailableCell(), value); 66 67 this.grid.insertTile(tile); 68 } 69}; 70 71// Sends the updated grid to the actuator 72GameManager.prototype.actuate = function () { 73 if (this.scoreManager.get() < this.score) { 74 this.scoreManager.set(this.score); 75 } 76 77 this.actuator.actuate(this.grid, { 78 score: this.score, 79 over: this.over, 80 won: this.won, 81 bestScore: this.scoreManager.get(), 82 terminated: this.isGameTerminated() 83 }); 84 85}; 86 87// Save all tile positions and remove merger info 88GameManager.prototype.prepareTiles = function () { 89 this.grid.eachCell(function (x, y, tile) { 90 if (tile) { 91 tile.mergedFrom = null; 92 tile.savePosition(); 93 } 94 }); 95}; 96 97// Move a tile and its representation 98GameManager.prototype.moveTile = function (tile, cell) { 99 this.grid.cells[tile.x][tile.y] = null; 100 this.grid.cells[cell.x][cell.y] = tile; 101 tile.updatePosition(cell); 102}; 103 104// Tests if two values sum to a Fibonacci number 105GameManager.prototype.testFib = function(val1, val2) { 106 var sum = val1 + val2; 107 var fib = [1,1] 108 109 while (sum > fib[fib.length-1]) { 110 fib.push(fib[fib.length-1] + fib[fib.length-2]) 111 } 112 113 for (var i = 0; i<fib.length && sum>=fib[i]; i++) { 114 if (sum === fib[i]) { 115 return true; 116 } 117 } 118 return false; 119}; 120 121// Move tiles on the grid in the specified direction 122GameManager.prototype.move = function (direction) { 123 // 0: up, 1: right, 2:down, 3: left 124 var self = this; 125 126 if (this.isGameTerminated()) return; // Don't do anything if the game's over 127 128 var cell, tile; 129 130 var vector = this.getVector(direction); 131 var traversals = this.buildTraversals(vector); 132 var moved = false; 133 134 // Save the current tile positions and remove merger information 135 this.prepareTiles(); 136 137 // Traverse the grid in the right direction and move tiles 138 traversals.x.forEach(function (x) { 139 traversals.y.forEach(function (y) { 140 cell = { x: x, y: y }; 141 tile = self.grid.cellContent(cell); 142 143 if (tile) { 144 var positions = self.findFarthestPosition(cell, vector); 145 var next = self.grid.cellContent(positions.next); 146 147 // Only one merger per row traversal? 148 if (next && self.testFib(next.value, tile.value) && !next.mergedFrom) { 149 var merged = new Tile(positions.next, tile.value + next.value); 150 merged.mergedFrom = [tile, next]; 151 152 self.grid.insertTile(merged); 153 self.grid.removeTile(tile); 154 155 // Converge the two tiles' positions 156 tile.updatePosition(positions.next); 157 158 // Update the score
159 self.score += merged.value; 160 161 // The mighty 2048 tile 162 if (merged.value === 1597) self.won = true; 163 } else { 164 self.moveTile(tile, positions.farthest); 165 } 166 167 if (!self.positionsEqual(cell, tile)) { 168 moved = true; // The tile moved from its original cell! 169 } 170 } 171 }); 172 }); 173 174 if (moved) { 175 this.addRandomTile(); 176 177 if (!this.movesAvailable()) { 178 this.over = true; // Game over! 179 } 180 181 this.actuate(); 182 } 183}; 184 185// Get the vector representing the chosen direction 186GameManager.prototype.getVector = function (direction) { 187 // Vectors representing tile movement 188 var map = { 189 0: { x: 0, y: -1 }, // up 190 1: { x: 1, y: 0 }, // right 191 2: { x: 0, y: 1 }, // down 192 3: { x: -1, y: 0 } // left 193 }; 194 195 return map[direction]; 196}; 197 198// Build a list of positions to traverse in the right order 199GameManager.prototype.buildTraversals = function (vector) { 200 var traversals = { x: [], y: [] }; 201 202 for (var pos = 0; pos < this.size; pos++) { 203 traversals.x.push(pos); 204 traversals.y.push(pos); 205 } 206 207 // Always traverse from the farthest cell in the chosen direction 208 if (vector.x === 1) traversals.x = traversals.x.reverse(); 209 if (vector.y === 1) traversals.y = traversals.y.reverse(); 210 211 return traversals; 212}; 213 214GameManager.prototype.findFarthestPosition = function (cell, vector) { 215 var previous; 216 217 // Progress towards the vector direction until an obstacle is found 218 do { 219 previous = cell; 220 cell = { x: previous.x + vector.x, y: previous.y + vector.y }; 221 } while (this.grid.withinBounds(cell) && 222 this.grid.cellAvailable(cell)); 223 224 return { 225 farthest: previous, 226 next: cell // Used to check if a merge is required 227 }; 228}; 229 230GameManager.prototype.movesAvailable = function () { 231 return this.grid.cellsAvailable() || this.tileMatchesAvailable(); 232}; 233 234// Check for available matches between tiles (more expensive check) 235GameManager.prototype.tileMatchesAvailable = function () { 236 var self = this; 237 238 var tile; 239 240 for (var x = 0; x < this.size; x++) { 241 for (var y = 0; y < this.size; y++) { 242 tile = this.grid.cellContent({ x: x, y: y }); 243 244 if (tile) { 245 for (var direction = 0; direction < 4; direction++) { 246 var vector = self.getVector(direction); 247 var cell = { x: x + vector.x, y: y + vector.y }; 248 249 var other = self.grid.cellContent(cell); 250 251 if (other && self.testFib(other.value, tile.value)) { 252 return true; // These two tiles can be merged 253 } 254 } 255 } 256 } 257 } 258 259 return false; 260}; 261 262GameManager.prototype.positionsEqual = function (first, second) { 263 return first.x === second.x && first.y === second.y; 264};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.