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