Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
mamayaya1
GitHub Repository: mamayaya1/game
Path: blob/main/projects/cupcake2048/js/game_manager.js
4626 views
1
function GameManager(size, InputManager, Actuator, StorageManager) {
2
this.size = size; // Size of the grid
3
this.inputManager = new InputManager;
4
this.storageManager = new StorageManager;
5
this.actuator = new Actuator;
6
7
this.startTiles = 2;
8
9
/* this.inputManager.on("crowd", this.crowd.bind(this)); */
10
this.inputManager.on("move", this.move.bind(this));
11
this.inputManager.on("restart", this.restart.bind(this));
12
this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));
13
14
this.setup();
15
}
16
17
18
// Set Prices
19
function kcal(exp) {
20
var kcal = [];
21
kcal[2]=200;
22
kcal[4]=250;
23
kcal[8]=320;
24
kcal[16]=400;
25
kcal[32]=500;
26
kcal[64]=650;
27
kcal[128]=820;
28
kcal[256]=1000;
29
kcal[512]=1200;
30
kcal[1024]=1500;
31
kcal[2048]=2000;
32
kcal[4096]=3000;
33
kcal[8192]=5000;
34
return kcal[exp];
35
}
36
37
// Restart the game
38
GameManager.prototype.restart = function () {
39
this.storageManager.clearGameState();
40
this.actuator.continueGame(); // Clear the game won/lost message
41
this.setup();
42
};
43
44
// Crowd board
45
GameManager.prototype.crowd = function () {
46
this.storageManager.clearGameState();
47
this.actuator.continueGame(); // Clear the game won/lost message
48
this.grid = new Grid(this.size);
49
this.score = 0;
50
this.points = 0;
51
this.over = false;
52
this.won = false;
53
this.keepPlaying = false;
54
this.actuate();
55
var counter = 0;
56
for (var i = 0; i < 3; i++) {
57
for (var j = 0; j < 4; j++) {
58
counter++;
59
var value = Math.pow(2, counter);
60
var tile = new Tile({ x: j, y: i }, value);
61
if (value <= 8192) this.grid.insertTile(tile);
62
}
63
}
64
};
65
66
// Keep playing after winning (allows going over 2048)
67
GameManager.prototype.keepPlaying = function () {
68
this.keepPlaying = true;
69
this.actuator.continueGame(); // Clear the game won/lost message
70
};
71
72
// Return true if the game is lost, or has won and the user hasn't kept playing
73
GameManager.prototype.isGameTerminated = function () {
74
if (this.over || (this.won && !this.keepPlaying)) {
75
return true;
76
} else {
77
return false;
78
}
79
};
80
81
// Set up the game
82
GameManager.prototype.setup = function () {
83
var previousState = this.storageManager.getGameState();
84
85
// Reload the game from a previous game if present
86
if (previousState) {
87
this.grid = new Grid(previousState.grid.size,
88
previousState.grid.cells); // Reload grid
89
this.score = previousState.score;
90
this.points = previousState.points;
91
this.over = previousState.over;
92
this.won = previousState.won;
93
this.keepPlaying = previousState.keepPlaying;
94
} else {
95
this.grid = new Grid(this.size);
96
this.score = 0;
97
this.points = 0;
98
this.over = false;
99
this.won = false;
100
this.keepPlaying = false;
101
102
// Add the initial tiles
103
this.addStartTiles();
104
}
105
106
// Localize page elements
107
this.localizeElements();
108
109
// Fill legend
110
this.fillLegend();
111
112
// Update the actuator
113
this.actuate();
114
};
115
116
// Passive localization of page elements
117
GameManager.prototype.localizeElements = function () {
118
var elementArray = [
119
'game-intro',
120
'restart-button',
121
'retry-button',
122
'keep-playing-button',
123
'game-explanation',
124
'disclaimer',
125
'tile-legend',
126
'credits'
127
];
128
for (var i in elementArray) {
129
LocalizeElement(elementArray[i]);
130
}
131
};
132
133
// Fill legend
134
GameManager.prototype.fillLegend = function () {
135
136
var legend = document.getElementsByClassName("tile-legend");
137
for (var i = 1; i <= 13; i++) {
138
var exp = Math.pow(2, i);
139
var row = document.createElement("div");
140
var grid = document.createElement("div");
141
var cell = document.createElement("div");
142
var img = document.createElement("img");
143
var p = document.createElement("p");
144
row.classList.add('legend-row');
145
grid.classList.add('legend-grid');
146
cell.classList.add('legend-cell');
147
cell.classList.add('cell-' + exp);
148
img.src = "style/img/" + exp + ".jpg";
149
cell.appendChild(img);
150
grid.appendChild(cell);
151
row.appendChild(grid);
152
p.textContent = Localize(exp) + " (" + kcal(exp) + " Kcal)";
153
row.appendChild(p);
154
155
legend[0].appendChild(row);
156
}
157
158
};
159
160
// Set up the initial tiles to start the game with
161
GameManager.prototype.addStartTiles = function () {
162
for (var i = 0; i < this.startTiles; i++) {
163
this.addRandomTile();
164
}
165
};
166
167
// Adds a tile in a random position
168
GameManager.prototype.addRandomTile = function () {
169
if (this.grid.cellsAvailable()) {
170
var value = Math.random() < 0.9 ? 2 : 4;
171
var tile = new Tile(this.grid.randomAvailableCell(), value);
172
173
this.grid.insertTile(tile);
174
}
175
};
176
177
// Sends the updated grid to the actuator
178
GameManager.prototype.actuate = function () {
179
if (this.storageManager.getBestScore() < this.score) {
180
this.storageManager.setBestScore(this.score);
181
}
182
if (this.storageManager.getBestPoints() < this.points) {
183
this.storageManager.setBestPoints(this.points);
184
}
185
// Clear the state when the game is over (game over only, not win)
186
if (this.over) {
187
this.storageManager.clearGameState();
188
} else {
189
this.storageManager.setGameState(this.serialize());
190
}
191
192
this.actuator.actuate(this.grid, {
193
score: this.score,
194
points: this.points,
195
over: this.over,
196
won: this.won,
197
bestScore: this.storageManager.getBestScore(),
198
bestPoints: this.storageManager.getBestPoints(),
199
terminated: this.isGameTerminated()
200
});
201
202
};
203
204
// Represent the current game as an object
205
GameManager.prototype.serialize = function () {
206
return {
207
grid: this.grid.serialize(),
208
score: this.score,
209
points: this.points,
210
over: this.over,
211
won: this.won,
212
keepPlaying: this.keepPlaying
213
};
214
};
215
216
// Save all tile positions and remove merger info
217
GameManager.prototype.prepareTiles = function () {
218
this.grid.eachCell(function (x, y, tile) {
219
if (tile) {
220
tile.mergedFrom = null;
221
tile.savePosition();
222
}
223
});
224
};
225
226
// Move a tile and its representation
227
GameManager.prototype.moveTile = function (tile, cell) {
228
this.grid.cells[tile.x][tile.y] = null;
229
this.grid.cells[cell.x][cell.y] = tile;
230
tile.updatePosition(cell);
231
};
232
233
// Move tiles on the grid in the specified direction
234
GameManager.prototype.move = function (direction) {
235
// 0: up, 1: right, 2: down, 3: left
236
var self = this;
237
238
if (this.isGameTerminated()) return; // Don't do anything if the game's over
239
240
var cell, tile;
241
242
var vector = this.getVector(direction);
243
var traversals = this.buildTraversals(vector);
244
var moved = false;
245
246
// Save the current tile positions and remove merger information
247
this.prepareTiles();
248
249
// Traverse the grid in the right direction and move tiles
250
traversals.x.forEach(function (x) {
251
traversals.y.forEach(function (y) {
252
cell = { x: x, y: y };
253
tile = self.grid.cellContent(cell);
254
255
if (tile) {
256
var positions = self.findFarthestPosition(cell, vector);
257
var next = self.grid.cellContent(positions.next);
258
259
// Only one merger per row traversal?
260
if (next && next.value === tile.value && !next.mergedFrom) {
261
var merged = new Tile(positions.next, tile.value * 2);
262
merged.mergedFrom = [tile, next];
263
264
self.grid.insertTile(merged);
265
self.grid.removeTile(tile);
266
267
// Converge the two tiles' positions
268
tile.updatePosition(positions.next);
269
270
// Update the score
271
self.points += kcal(tile.value) * 2;
272
if (merged.value > self.score) self.score = merged.value;
273
274
// The mighty 2048 tile
275
if (merged.value === 2048 || merged.value === 2048) self.won = true;
276
} else {
277
self.moveTile(tile, positions.farthest);
278
}
279
280
if (!self.positionsEqual(cell, tile)) {
281
moved = true; // The tile moved from its original cell!
282
}
283
}
284
});
285
});
286
287
if (moved) {
288
this.addRandomTile();
289
290
if (!this.movesAvailable()) {
291
this.over = true; // Game over!
292
}
293
294
this.actuate();
295
}
296
};
297
298
// Get the vector representing the chosen direction
299
GameManager.prototype.getVector = function (direction) {
300
// Vectors representing tile movement
301
var map = {
302
0: { x: 0, y: -1 }, // Up
303
1: { x: 1, y: 0 }, // Right
304
2: { x: 0, y: 1 }, // Down
305
3: { x: -1, y: 0 } // Left
306
};
307
308
return map[direction];
309
};
310
311
// Build a list of positions to traverse in the right order
312
GameManager.prototype.buildTraversals = function (vector) {
313
var traversals = { x: [], y: [] };
314
315
for (var pos = 0; pos < this.size; pos++) {
316
traversals.x.push(pos);
317
traversals.y.push(pos);
318
}
319
320
// Always traverse from the farthest cell in the chosen direction
321
if (vector.x === 1) traversals.x = traversals.x.reverse();
322
if (vector.y === 1) traversals.y = traversals.y.reverse();
323
324
return traversals;
325
};
326
327
GameManager.prototype.findFarthestPosition = function (cell, vector) {
328
var previous;
329
330
// Progress towards the vector direction until an obstacle is found
331
do {
332
previous = cell;
333
cell = { x: previous.x + vector.x, y: previous.y + vector.y };
334
} while (this.grid.withinBounds(cell) &&
335
this.grid.cellAvailable(cell));
336
337
return {
338
farthest: previous,
339
next: cell // Used to check if a merge is required
340
};
341
};
342
343
GameManager.prototype.movesAvailable = function () {
344
return this.grid.cellsAvailable() || this.tileMatchesAvailable();
345
};
346
347
// Check for available matches between tiles (more expensive check)
348
GameManager.prototype.tileMatchesAvailable = function () {
349
var self = this;
350
351
var tile;
352
353
for (var x = 0; x < this.size; x++) {
354
for (var y = 0; y < this.size; y++) {
355
tile = this.grid.cellContent({ x: x, y: y });
356
357
if (tile) {
358
for (var direction = 0; direction < 4; direction++) {
359
var vector = self.getVector(direction);
360
var cell = { x: x + vector.x, y: y + vector.y };
361
362
var other = self.grid.cellContent(cell);
363
364
if (other && other.value === tile.value) {
365
return true; // These two tiles can be merged
366
}
367
}
368
}
369
}
370
}
371
372
return false;
373
};
374
375
GameManager.prototype.positionsEqual = function (first, second) {
376
return first.x === second.x && first.y === second.y;
377
};
378
379