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