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