-
Notifications
You must be signed in to change notification settings - Fork 0
/
2048.js
342 lines (338 loc) · 11.8 KB
/
2048.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
/*2048.js*/
var game = {
data1 : [], //存储所有单元格数据,二维数组
RN : 4, //总行数,可在此处改变,最大为8
CN : 4, //总列数,可在此处改变,最大为8
score : 0, //保存分数
state: 1, //游戏当前状态:Running|GameOver
RUNNING : 1, //运行中
GAMEOVER : 0, //游戏结束
PLAYING : 2, //动画播放中
//获得所有背景格的html代码
getGridHTML : function(){
for(var r = 0, arr = []; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
arr.push("" + r + c);
}
}
return '<div id="g' + arr.join('" class="grid"></div><div id="g') + '" class="grid"></div>';
},
//获得所有前景格的html代码
getCellHTML : function(){
for(var r = 0, arr = []; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
arr.push("" + r + c);
}
}
return '<div id="c' + arr.join('" class="cell"></div><div id="c') + '" class="cell"></div>';
},
//判断游戏状态为结束
isGameOver:function(){
//如果没有满,则返回false
if(!this.isFull()){
return false;
}else{//否则
//从左上角第一个元素开始,遍历二维数组
for(var r = 0; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
//如果当前元素不是最右侧元素
if(c < this.CN-1){
// 如果当前元素==右侧元素
if(this.data1[r][c] == this.data1[r][c + 1]){
return false;
}
}
//如果当前元素不是最下方元素
if(r < this.RN - 1){
// 如果当前元素==下方元素
if(this.data1[r][c] == this.data1[r + 1][c]){
return false;
}
}
}
}
return true;
}
},
//开始游戏
start : function(){
var panel = document.getElementById('gridPanel');
//游戏开始获得网格布局
panel.innerHTML = this.getGridHTML() + this.getCellHTML();
//将panel的高度设置为RN*116+16+"px"
panel.style.height = this.RN * 116 + 16 + 'px';
//将panel的宽度设置为CN*116+16+"px"
panel.style.width = this.CN * 116 + 16 + 'px';
this.data1 = []; //清空旧数组
for(var r = 0; r < this.RN; r++){ //r从0开始,到<RN结束,每次+1
this.data1.push([]); //在data1中压入一个空数组
for(var c = 0; c < this.CN; c++){ //c从0开始,到<CN结束,每次+1
this.data1[r].push(0); //向data1中r行,压入一个0
}
}
this.state = this.RUNNING; //设置游戏状态
this.score = 0; //分数重置为0
//找到游戏结束界面,隐藏
document.getElementById("gameOver").style.display = "none";
this.randomNum();
this.randomNum();
this.updateView();
},
//初始界面生成两个随机数
randomNum : function(){ //在随机的不重复的位置生成一个2或4
if(!this.isFull()){ //只有不满时,才尝试生成随机数
for(;;){
var r = Math.floor(Math.random() * this.RN); //在0~RN-1之间生成一个行下标,存在r中
var c = Math.floor(Math.random() * this.CN); //在0~CN-1之间生成一个列下标,存在c中
/*
如果data1中r行c列等于0
生成一个0~1之间的随机数
如果随机数>0.5,就在r行c列放入4
否则放入2
*/
if (this.data1[r][c] == 0) {
this.data1[r][c] = Math.random() > 0.5 ? 4 : 2;
break;// 退出循环
}
}
}
},
//将data1数组中每个元素更新到页面div
updateView : function(){
//遍历data1中每个元素的值
for(var r = 0; r < this.RN; r++){
for(var c = 0; c < this.CN; c++){
//找到页面上和当前位置对相应的div
var divObj = document.getElementById("c" + r + c);
if (this.data1[r][c] == 0) { //如果当前值为0
divObj.innerHTML = ""; //清除innerHTML
divObj.className = "cell"; //还原className为"cell"
}else{
divObj.innerHTML = this.data1[r][c]; //否则,将当前值放入innerHTML
divObj.className = "cell n" + this.data1[r][c]; //修改className为"cell n" + 当前值
}
}
}
var span = document.getElementById("score");
span.innerHTML = this.score;
//判断并修改游戏状态为GAMEOVER
if(this.isGameOver()){
this.state = this.GAMEOVER;
document.getElementById("finalScore").innerHTML=this.score;
document.getElementById("gameOver").style.display="block"; //修改div的style属性下的display子属性为"block"
}
},
//判断是否满格
isFull : function(){
for (var r = 0; r < this.RN; r++) {
for (var c = 0; c < this.CN; c++) {
if (this.data1[r][c] == 0) { //如果当前元素等于0
return false; //返回false
}
}
}
return true; //遍历结束,返回true
},
//左移所有行
moveLeft : function(){
var before = this.data1.toString();
for (var r = 0; r < this.RN; r++) { //遍历data1中的每一行
this.moveLeftInRow(r); //左移当前行
}
var after = this.data1.toString();
if(before != after){
animation.start();
}
},
//左移一行,传入要移动的行号
moveLeftInRow : function(r){
//c从0开始,遍历当前行中的元素,到<CN-1结束,每次+1
for (var c = 0; c < this.CN-1; c++) {
//找到c之后下一个不为0的值的位置,存在next中
var nextc = this.getNextInRow(r,c);
if(nextc == -1){
break; //如果nextc等于-1,退出循环
}else{ //否则
if(this.data1[r][c] == 0){ //如果当前位置等于0
this.data1[r][c] = this.data1[r][nextc]; //将当前位置设为下一个位置的值
this.data1[r][nextc] = 0; //将下一位置设为0
var div = document.getElementById("c" + r + nextc);
animation.addTask(div,r,nextc,r,c);
c--; //保证下次依然检查当前元素
}else if(this.data1[r][c] == this.data1[r][nextc]){ //否则,如果当前位置等于下一位置
this.data1[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data1[r][c]; //增加分数
this.data1[r][nextc] = 0; //将下一位置设为0
var div = document.getElementById("c" + r + nextc);
animation.addTask(div,r,nextc,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getNextInRow : function(r,c){
for(var nextc = c + 1; nextc < this.CN; nextc++){ //nextc从c+1开始,遍历r行剩余元素
if(this.data1[r][nextc] != 0){ //如果nextc不等于0
return nextc;
}
}
return -1; //循环结束,返回-1
},
//右移所有行
moveRight : function(){
var before = this.data1.toString();
for (var r = 0; r < this.RN; r++) { //遍历data1中的每一行
this.moveRightInRow(r); //右移当前行
}
var after = this.data1.toString();
if(before != after){
animation.start();
}
},
//右移一行,传入要移动的行号
moveRightInRow : function(r){
//c从CN-1开始,到>0结束,每次-1
for (var c = this.CN-1; c > 0 ; c--) {
//找到c之后下一个不为0的值的位置,存在next中
var prevc = this.getPrevInRow(r,c);
if(prevc == -1){
break; //如果prevc等于-1,退出循环
}else{ //否则
if(this.data1[r][c] == 0){ //如果当前位置等于0
this.data1[r][c] = this.data1[r][prevc]; //将当前位置设为下一个位置的值
this.data1[r][prevc] = 0; //将下一位置设为0
var div = document.getElementById("c" + r + prevc);
animation.addTask(div, r, prevc, r, c);
c++; //保证下次依然检查当前元素
}else if(this.data1[r][c] == this.data1[r][prevc]){ //否则,如果当前位置等于下一位置
this.data1[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data1[r][c]; //增加分数
this.data1[r][prevc] = 0; //将下一位置设为0
var div = document.getElementById("c"+r+prevc);
animation.addTask(div,r,prevc,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getPrevInRow : function(r,c){
for(var prevc = c - 1; prevc >= 0; prevc--){ //prevc从c+1开始,遍历r行剩余元素
if(this.data1[r][prevc] != 0){ //如果prevc不等于0
return prevc;
}
}
return -1; //循环结束,返回-1
},
//上移所有行
moveUp : function(){
var before = this.data1.toString();
for (var c = 0; c < this.CN; c++) { //遍历data1中的每一列
this.moveUpInCol(c); //右移当前行
}
var after = this.data1.toString();
if(before != after){
animation.start();
}
},
//上移一列,传入要移动的列号
moveUpInCol : function(c){
//r从0开始,遍历当前列中的元素,到<RN-1结束,每次+1
for (var r = 0; r < this.RN-1 ; r++) {
//找到c之后下一个不为0的值的位置,存在next中
var nextr = this.getNextInCol(r,c);
if(nextr == -1){
break; //如果nextr等于-1,退出循环
}else{ //否则
if(this.data1[r][c] == 0){ //如果当前位置等于0
this.data1[r][c] = this.data1[nextr][c]; //将当前位置设为下一个位置的值
this.data1[nextr][c] = 0; //将下一位置设为0
var div = document.getElementById("c"+ nextr + c);
animation.addTask(div,nextr,c,r,c);
r--; //保证下次依然检查当前元素
}else if(this.data1[r][c] == this.data1[nextr][c]){ //否则,如果当前位置等于下一位置
this.data1[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data1[r][c]; //增加分数
this.data1[nextr][c] = 0; //将下一位置设为0
var div = document.getElementById("c"+ nextr + c);
animation.addTask(div,nextr,c,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getNextInCol : function(r,c){
for(var nextr = r + 1; nextr < this.RN; nextr++){ //nextr从c+1开始,遍历c列剩余元素
if(this.data1[nextr][c] != 0){ //如果nextr不等于0
return nextr;
}
}
return -1; //循环结束,返回-1
},
//下移所有行
moveDown : function(){
var before = this.data1.toString();
for (var c = 0; c < this.CN; c++) { //遍历data1中的每一列
this.moveDownInCol(c); //右移当前行
}
var after = this.data1.toString();
if(before != after){
animation.start();
}
},
//下移一列,传入要移动的列号
moveDownInCol : function(c){
//r从RN-1开始,遍历当前列中的元素,到>0结束,每次-1
for (var r = this.RN-1; r > 0 ; r--) {
//找到c之后下一个不为0的值的位置,存在next中
var prevr = this.getPrevInCol(r,c);
if(prevr == -1){
break; //如果prevr等于-1,退出循环
}else{ //否则
if(this.data1[r][c] == 0){ //如果当前位置等于0
this.data1[r][c] = this.data1[prevr][c]; //将当前位置设为下一个位置的值
this.data1[prevr][c] = 0; //将下一位置设为0
var div = document.getElementById("c" + prevr + c);
animation.addTask(div,prevr,c,r,c);
r++; //保证下次依然检查当前元素
}else if(this.data1[r][c] == this.data1[prevr][c]){ //否则,如果当前位置等于下一位置
this.data1[r][c] *= 2; //当前位置 = 当前位置值*2
this.score += this.data1[r][c]; //增加分数
this.data1[prevr][c] = 0; //将下一位置设为0
var div = document.getElementById("c" + prevr + c);
animation.addTask(div,prevr,c,r,c);
}
}
}
},
//找r行c列位置之后,不为0的下一个位置
getPrevInCol : function(r,c){
for(var prevr = r - 1; prevr >= 0; prevr--){ //prevr从r-1开始,遍历c列剩余元素
if(this.data1[prevr][c] != 0){ //如果prevr不等于0
return prevr;
}
}
return -1; //循环结束,返回-1
}
};
//当窗口加载后
window.onload = function(){
game.start();
/*键盘事件绑定*/
document.onkeydown = function(){
if(game.state == game.RUNNING){
var e = window.event || arguments[0];
var code = e.keyCode;
//如果按的是向左箭头,就调用左移方法
//左37 上38 右39 下40
if(code == 37){
game.moveLeft();
}else if(code == 39){
game.moveRight();
}else if(code == 38){
game.moveUp();
}else if(code == 40){
game.moveDown();
}
}
}
}