當前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
JS实现2048
2048這個游戲是通過對二維數組的操作來實現的,其算法核心如下:
(以一行左移為例)
c從0開始,遍歷當前行中的元素,到<CN-1(CN是一個常量,表示的是游戲格子的列數)結束,每次 1
找到當前位置下一個不為0的位置
如果沒找到
直接退出循環
否則
如果當前值等于0
將下一位置的值與當前位置的值交換
將下一位置設為0
將c-1(為了讓下次循環依然檢查當前位置)
? 否則,如果當前值等于下一個值
將當前值 * 2
下一位置值設為0
具體代碼如下
1 <!-- 2048.html --> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>2048</title> 7 <link rel="stylesheet" href="2048.css"> 8 <script src="2048.js"></script> 9 </head> 10 <body> 11 <!-- 分數 --> 12 <p>Score:<span id="score">0</span></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 '<div id="g' arr.join('" class="grid"></div><div id="g') '" class="grid"></div>'; 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 '<div id="c' arr.join('" class="cell"></div><div id="c') '" class="cell"></div>'; 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開始,到<RN結束,每次 1 70 this.data.push([]); //在data中壓入一個空數組 71 for(var c = 0; c < this.CN; c ){ //c從0開始,到<CN結束,每次 1 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開始,遍歷當前行中的元素,到<CN-1結束,每次 1 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開始,遍歷當前列中的元素,到<RN-1結束,每次 1 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
總結
- 上一篇: 对 Vue 的理解(一)
- 下一篇: 怎么在ReactNative里面使用Ty