JS实现2048

2048这个游戏是通过对二维数组的操作来实现的,其算法核心如下:

  (以一行左移为例)

    c从0开始,遍历当前行中的元素,到

    找到当前位置下一个不为0的位置

    如果没找到

      直接退出循环

    否则

      如果当前值等于0

        将下一位置的值与当前位置的值交换

        将下一位置设为0

        将c-1(为了让下次循环依然检查当前位置)

       否则,如果当前值等于下一个值

        将当前值 * 2

        下一位置值设为0

具体代码如下

 1 
 2 DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title>2048title>
 7     <link rel="stylesheet" href="2048.css">
 8     <script src="2048.js">script>
 9 head>
10 <body>
11     
12     <p>Score:<span id="score">0span>p>
13     
14     
15     <div id="gridPanel">
16     div>    
17 
18     <div id="gameOver">
19         <div>div>
20          <p>
21              Game Over!<br>
22              Score:<span id="finalScore">span><br>
23               <a class="button" onclick="game.start()">Try again!a>
24         p>
25     div>
26 body>
27 html>

 

  1 /*2048.css*/
  2  @charset "utf-8";
  3 
  4 
  5 #gridPanel{
  6     width: 480px;
  7     height: 480px;
  8     margin: 0 auto;
  9     background-color: #bbada0;
 10     border-radius: 10px;
 11     position: relative;
 12 }
 13 .grid, .cell{
 14     width: 100px;
 15     height: 100px;
 16     border-radius: 6px;
 17 }
 18 .grid{
 19     background-color: #ccc0b3;
 20     float: left;
 21     margin-top: 16px;
 22     margin-left: 16px;
 23 }
 24 .cell{
 25     text-align: center;
 26     line-height: 100px;
 27     color: #fff;
 28     font-size: 60px;
 29     position: absolute;
 30 }
 31 
 32 /*由于想要更改游戏的网格数,设置了最大限制为8*8,所以此处前景格和背景格的样式如下*/
 33 /*此功能的实现应该还有更简便的方法,能力有限,目前学的知识只能做到这样*/
 34 #c00, #c01, #c02, #c03, #c04, #c05, #c06, #c07{top: 16px;}
 35 #c10, #c11, #c12, #c13, #c14, #c15, #c16, #c17{top: 132px;}
 36 #c20, #c21, #c22, #c23, #c24, #c25, #c26, #c27{top: 248px;}
 37 #c30, #c31, #c32, #c33, #c34, #c35, #c36, #c37{top: 364px;}
 38 #c40, #c41, #c42, #c43, #c44, #c45, #c46, #c47{top: 480px;}
 39 #c50, #c51, #c52, #c53, #c54, #c55, #c56, #c57{top: 596px;}
 40 #c60, #c61, #c62, #c63, #c64, #c65, #c66, #c67{top: 712px;}
 41 #c70, #c71, #c72, #c73, #c74, #c75, #c76, #c77{top: 828px;}
 42 
 43 #c00, #c10, #c20, #c30, #c40, #c50, #c60, #c70{left: 16px;}
 44 #c01, #c11, #c21, #c31, #c41, #c51, #c61, #c71{left: 132px;}
 45 #c02, #c12, #c22, #c32, #c42, #c52, #c62, #c72{left: 248px;}
 46 #c03, #c13, #c23, #c33, #c43, #c53, #c63, #c73{left: 364px;}
 47 #c04, #c14, #c24, #c34, #c44, #c54, #c64, #c74{left: 480px;}
 48 #c05, #c15, #c25, #c35, #c45, #c55, #c65, #c75{left: 596px;}
 49 #c06, #c16, #c26, #c36, #c46, #c56, #c66, #c76{left: 712px;}
 50 #c07, #c17, #c27, #c37, #c47, #c57, #c67, #c77{left: 828px;}
 51 
 52 .n2{background-color: #eee3da;}
 53 .n4{background-color: #ede0c8;}
 54 .n8{background-color: #f2b179;}
 55 .n16{background-color: #f59563;}
 56 .n32{background-color: #f67c5f;}
 57 .n64{background-color: #f65e3d;}
 58 .n128{background-color: #edcf72;}
 59 .n256{background-color: #edcc61;}
 60 .n512{background-color: #9c0;}
 61 .n1024{background-color: #33b5e5;}
 62 .n2048{background-color: #09c;}
 63 .n4096{background-color: #a6c;}
 64 .n8192{background-color: #93c;}
 65 
 66 .n2, .n4{color: #776e65;}
 67 .n1024, .n2048, .n4096, .n8192{font-size: 40px;}
 68 
 69 /*显示分数*/
 70 p{
 71     width: 480px;
 72     margin: 0 auto;
 73     font-family: Arial;
 74     font-weight: bold;
 75     font-size: 40px;
 76     padding-top: 50px;
 77 }
 78 
 79 /* 游戏结束 */
 80 /*Game Over*/
 81 #gameOver{
 82     width:100%;
 83     height:100%;
 84     position:absolute;
 85     top:0;
 86     left:0;
 87      display:none;
 88 }
 89 #gameOver>div{
 90     width:100%;
 91     height:100%;
 92      background-color:#555;
 93      opacity:0.5;
 94 }
 95 #gameOver>p{
 96     width:300px;
 97     height:200px;
 98      border:1px solid #edcf72;
 99      line-height:1.6em;
100      text-align:center;
101      background-color:#fff;
102      border-radius:10px;
103      position:absolute;
104      top:50%;
105      left:50%;
106      margin-top:-100px;
107      margin-left:-150px;
108 }
109 .button{
110     padding:10px;
111     border-radius:6px;
112      background-color:#9f8b77;
113      color:#fff;
114      cursor:pointer;
115 }

 

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

 

 

 

 

 


更多专业前端知识,请上 【猿2048】www.mk2048.com

你可能感兴趣的:(前端,js,js小游戏,js2048)