我的簡易2048小遊戲記錄整理

前言

註:該篇記錄暫未實現過渡動畫以及移動端的上下左右操作

我的2048後續最新效果展示2048GAME (226yzy.com)

還有後續程式碼我放在了我的Github上了226YZY/my2048game: 我的簡易2048小遊戲 (github.com)

(本篇效果煩請自行根據程式碼復現😂)

本篇記錄中的程式碼是我根據我對2048的基本遊戲邏輯,嘗試通過html+css+原生js實現

由於個人水平有限,相關內容有不完善的地方歡迎大佬指出Orz

原版2048相關內容

原版2048首先在GitHub上發布,原作者是Gabriele Cirulli

在Github上可以找到這個這個小遊戲的源程式碼,傳送門🚪gabrielecirulli/2048: A small clone of 1024 (//play.google.com/store/apps/details?id=com.veewo.a1024) (github.com)

你也可以試玩原作者製作的2048,傳送門🚪2048 (play2048.co)

遊戲基本邏輯梳理

1.頁面基本內容

頁面上至少需要有以下基本內容

  1. 4*4的表作為遊戲主體
  2. 顯示當前多少分
  3. 新遊戲
  4. 提示遊戲結束(一開始不顯示)
  5. 初始隨機兩個格子內有數字(該數字為2或4)
  6. 根據格子內的數字,格子內的顏色改變

2.隨機產生數字

在沒有數字的格子產生一個數字

該數字只能為2或4

其中4的概率較小為10%(後來我粗略觀察原版2048的程式碼發現的)

3.計分

每對相同的數字合併後,總分加上這對數字的和

例如2和2合併後總分加4分,8和8合併後總分加16分

4.合併數字

基礎的(0表示空,下同)比如 0 2 0 2左移後應為4 0 0 0

需要注意的情況

與原版2048對比後我發現網上一些自製的2048在這方面有些問題,下面列出我注意到的情況

  • 例1(是否重複合併)

2 2 2 2這行如果向左移動後結果應為4 4 0 0

也就是說,合併後的數字不與接下來相同的數字再合併

  • 例2(合併順序)

2 2 2 4右移後應為0 2 4 4

說明向右移,合併應從右側開始

若為左移,則合併應從左側開始,上下移動的同理

5.上下左右操作響應

對鍵盤事件響應,通過wsad或方向鍵對應上下左右操作

6.遊戲是否結束或達到勝利值

在每次操作後,隨即遍歷每個格子上下左右是否還有相同的數字

若存在某個格子上下左右中存在與之相同的數字,那麼遊戲尚未結束

否則,若所有格子上下左右中都不存在與之相同的數字,則說明遊戲結束

勝利值在上面遍歷時查找即可

程式碼實現

HTML部分

html部分總覽

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2048GAME</title>
    <!-- css引入 -->
    <link href="css/main.css" rel="stylesheet">
</head>
    
<body onkeydown="keyboardEvents()">
  
  <!-- 當前總分顯示與新遊戲按鈕-->
  <div id="theMark">
    <div>當前分數為 <span id="mark"></span> 分</div>
    <button id="newgame" onclick="main()"><span>NEW GAME</span></button>
  </div>
  
  <!-- 2048遊戲主體-->
  <!-- cellspacing屬性可設置或返回在表格中的單元格之間的空白量 -->
  <table id="maintable" cellspacing="10"></table> 
    
  <!-- 遊戲結束時顯示 GAME OVER-->
  <div id="gameover"></div>
  
</body>
<!-- js引入 -->
<script src="js/main2048.js"></script>
</html>

當前總分顯示與新遊戲按鈕

該部分程式碼如下

 <div id="theMark">
    <div>當前分數為 <span id="mark"></span> 分</div>
    <button id="newgame" onclick="main()"><span>NEW GAME</span></button>
  </div>

<span id="mark"></span>這部分的內容會後續通過js渲染上去,表示總分

<button id="newgame" onclick="main()"><span>NEW GAME</span></button>點擊後會執行js中寫好的main()函數,以重新載入2048遊戲的4*4表格主體以及總分歸零

2048遊戲主體

<!-- cellspacing屬性可設置或返回在表格中的單元格之間的空白量 -->
  <table id="maintable" cellspacing="10"></table> 

這部分我用table實現,裡面的td通過js渲染上去,渲染後如下

<table id="maintable" cellspacing="10">
        <tbody>
            <tr>
                <td id="1" ></td>
                <td id="2" ></td>
                <td id="3" >4</td>
                <td id="4" ></td>
            </tr>
            <tr>
                <td id="5" >2</td>
                <td id="6" ></td>
                <td id="7" ></td>
                <td id="8" ></td>
            </tr>
            <tr>
                <td id="9" ></td>
                <td id="10" ></td>
                <td id="11" ></td>
                <td id="12" ></td>
            </tr>
            <tr>
                <td id="13" ></td>
                <td id="14" ></td>
                <td id="15" ></td>
                <td id="16" ></td>
            </tr>
        </tbody>
    </table>

註:原作者是用div實現的,你也可以用這種做法

遊戲結束或達到勝利值時提示

<!-- 遊戲結束或達到勝利值時顯示對應的內容-->
<div id="gameover"></div>

該部分通過css使其初始不顯示,當判斷遊戲結束或達到勝利值時由js更改其style和內容,使其顯示

CSS部分

css部分總覽

body {
    text-align: center;/*文本居中*/
    background-color: #E0FFFF;  /*背景顏色*/
}
/*表格樣式*/
table {
    min-width: 340px;
    font-size: 30px;
    font-weight: bold;
    background-color: #bbada0;
    margin: 0px auto; /*上下外邊距為0,左右根據寬度自適應,即水平居中*/
    margin-top: 20px;
    border-radius: 10px; /* 設置表格邊框圓角 */
}

/*單元格樣式*/
td {
    width: 80px;
    height: 80px;
    border-radius: 10px;
    background-color: rgb(29, 180, 250);
}

/*總分提示部分字體大小和粗細*/
#theMark{
    font-size: 36px;
    font-weight: bold;
}

/*突出分數,設置其字體顏色*/
#mark{
    color: rgb(33, 132, 245);
}

/*新遊戲按鈕樣式*/
#newgame{
    background-color: rgb(241, 176, 56);
    border-radius: 5px;
    width: 150px;
    height: 50px;
    color: aliceblue;
    font-size: large;
    font-weight: 800;
    margin-top: 20px;
}

/*遊戲結束提示*/
#gameover{
    font-size: 36px;
    font-weight: bold;
    display:none;/*初始不顯示*/
    color: crimson;
    margin: 0 auto;
}

各部分內容見上面css程式碼及其注釋,及不過多贅述了😆

JS部分

這部分重點🚩

路漫漫其修遠兮,上面合併數字中提到的問題以及各種bug,讓我重寫了好幾次核心部分🤣

1.初始

各種頁面渲染及各種函數調用都基本在這一部分實現

window.onload = main(); //載入主函數
var overflag=true;//判斷遊戲是否結束的標誌
//主函數
function main() {
    mapx=4,mapy=4,mapt=mapx*mapy;//設定表框大小,一般為4*4
    var table = document.getElementById("maintable"); //獲取表格
    var tableStructure = ""; //儲存表格結構
    var tdid = 1; //單元格id
    var mark=document.getElementById("mark");//獲取html中顯示分數的位置
    overflag=true; //因為後續新遊戲是重新執行該函數,所以該值要重新變為true

    //載入表格結構(4行4列)
    for (var i = 1; i <= mapx; i++) {
        tableStructure += "<tr>"; //為表格加1行
        //為該行添加單元格
        for (var j = 1; j <= mapy; j++) {
            tableStructure += "<td id=" + tdid + "></td>"; //添加單元格與該單元格id
            tdid++;
        }
        tableStructure += "</tr>";
    }
    table.innerHTML = tableStructure; //向表格返回該表格結構
    tdRandom();
    tdRandom();//隨機兩個格子給予2或 4
    tdcolor();//渲染格子的顏色
    mark.innerHTML=0;//初始總分為0
    document.getElementById("gameover").style.display="none";//初始GME OVER提示不顯示
    document.getElementById("gameover").innerHTML="";
    youwin=2048;//設置勝利值
}

原版2048為4*4的表格,我在程式碼中的mapx,mapy的值可以自行設置,以便魔改成更大的表格供遊玩。

youwin的值代表勝利值可自行修改

2.隨機相關

這部分需要解決隨機格子隨機產生2或4

//因為Math.random() 返回 0(包括) 至 1(不包括) 之間的隨機數,隨機的數不一定是整數。這裡我用向下取整
function myrandom(min, max) {
    return min + Math.floor(Math.random() * max);
}

//隨機為單元格隨機填入2或4 (4出現的概率應相對較小)
function tdRandom() {
    var temp = myrandom(1, mapt);
    if (document.getElementById(temp).innerHTML == "") {
        document.getElementById(temp).innerHTML = Math.random()<0.9?2:4;
    } else {
        tdRandom();
    }
}

我一開始想設出現4的概率為25%,後來感覺手感不對,於是看原版的程式碼後發現其設置為10%

3.鍵盤事件響應

註:由於所學有限,本篇的移動操作暫時只實現了鍵盤事件的響應,移動端的觸屏操作待後續研究

//鍵盤事件響應
function keyboardEvents() {
    //以鍵盤方向鍵或wasd操作移動
    if (overflag) {
        if (event.keyCode == 37 || event.keyCode == 65) Left();
        else if (event.keyCode == 38 || event.keyCode == 87) Up();
        else if (event.keyCode == 39 || event.keyCode == 68) Right();
        else if (event.keyCode == 40 || event.keyCode == 83) Down();
        //保證其它按鍵不會觸發
        if (flag_r) {
            tdRandom();//隨機再產生一個數字
            flag_r = false;
        }
    }
    tdcolor();//更新格子顏色
    if(overflag)
    isover();//判斷遊戲是否結束
}

上下左右的操作函數見下文,設置flag_r是為了限制該函數其他鍵響應。每按一次按鍵會對遊戲是否結束以及格子顏色更新進行重判

4.上下左右操作函數

這部分四個操作的程式碼邏輯基本相同,只是根據方向的不同存入數字的順序不同

格子編號布局順序如下

1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 16

我用的是兩個一維數組分別存對應編號格子的內容和是否合併處理過(合併處理在我寫的changetd()函數中,具體見下一部分)

在4*4的表格中我是分成了4列(或行,上下是列,左右是行)讀入,讀入順序是正序或倒序(我是上左倒,下右正,這主要是方便changetd()函數統一操作),然後按此順序存入上述的數組中。

changetd()函數處理完後再更新對應編號的格子顯示的內容

該部分程式碼如下

//上下左右操作函數

//向上操作
function Up() {
   for(var i=1;i<=mapy;i++){
      var tempmap=[];//記錄該編號格子內的數
      var tempflag=[];//記錄該編號格子是否合併過(本次操作內)
      var z=0;
      for(var j=i+(mapx-1)*mapy;j>=i;j-=mapy){ 
          var thetd=document.getElementById(j); 
          if(thetd.innerHTML==""){
              tempmap[z]=0;
          }          
          else{
              tempmap[z]=parseInt(thetd.innerHTML);
          } 
          tempflag[z]=true;  
          z++;                
      }
      //數字合併統一由changetd函數完成
      tempmap=changetd(tempmap,tempflag,tempmap.length,0);
      z=0;
      //更新格子內的顯示內容
      for(var j=i+(mapx-1)*mapy;j>=i;j-=mapy){ 
        var thetd=document.getElementById(j);
        if(tempmap[z]==0){
            thetd.innerHTML="";
        }  
        else{
            thetd.innerHTML=tempmap[z];
        } 
        z++;  
    }
   } 
}

//向下操作
function Down() {

    for(var i=1;i<=mapy;i++){
        var tempmap=[];
        var tempflag=[];
        var z=0;
        for(var j=i;j<=i+(mapx-1)*mapy;j+=mapy){ 
            var thetd=document.getElementById(j); 
            if(thetd.innerHTML==""){
                tempmap[z]=0;
            }          
            else{
                tempmap[z]=parseInt(thetd.innerHTML);
            } 
            tempflag[z]=true;  
            z++;                
        }
        tempmap=changetd(tempmap,tempflag,tempmap.length,0);  
        z=0;
        for(var j=i;j<=i+(mapx-1)*mapy;j+=mapy){ 
          var thetd=document.getElementById(j);
          if(tempmap[z]==0){
              thetd.innerHTML="";
          }  
          else{
              thetd.innerHTML=tempmap[z];
          } 
          z++;  
      }
     } 
}

//向左操作
function Left() {
     for(var i=mapy;i<=mapy+(mapx-1)*mapy;i+=mapy){
        var tempmap=[];
        var tempflag=[];
        var z=0;
        for(var j=i;j>=i-mapy+1;j--){ 
            var thetd=document.getElementById(j); 
            if(thetd.innerHTML==""){
                tempmap[z]=0;
            }          
            else{
                tempmap[z]=parseInt(thetd.innerHTML);
            } 
            tempflag[z]=true;  
            z++;                
        }
        tempmap=changetd(tempmap,tempflag,tempmap.length,0);  
        z=0;
        for(var j=i;j>=i-mapy+1;j--){ 
          var thetd=document.getElementById(j);
          if(tempmap[z]==0){
              thetd.innerHTML="";
          }  
          else{
              thetd.innerHTML=tempmap[z];
          } 
          z++;  
      }
     } 
}

//向右操作
function Right() {
    for(var i=1;i<=1+(mapx-1)*mapy;i+=mapy){
        var tempmap=[];
        var tempflag=[];
        var z=0;
        console.log(i+" i");
        for(var j=i;j<i+mapy;j++){ 
            console.log(j);
            var thetd=document.getElementById(j); 
            if(thetd.innerHTML==""){
                tempmap[z]=0;
            }          
            else{
                tempmap[z]=parseInt(thetd.innerHTML);
            } 
            tempflag[z]=true;  
            z++;                
        }
        tempmap=changetd(tempmap,tempflag,tempmap.length,0);  
        z=0;
        for(var j=i;j<i+mapy;j++){ 
          var thetd=document.getElementById(j);
          if(tempmap[z]==0){
              thetd.innerHTML="";
          }  
          else{
              thetd.innerHTML=tempmap[z];
          } 
          z++;  
      }
     }
}

5.合併數字

上文提到的changetd()函數來實現這部分內容

程式碼如下

function changetd(tempmap, tempflag, k, u) {
    for (var i = k - 1; i > u; i--) {
        if (tempmap[i - 1] != 0 && tempmap[i] == 0) {
            //移動
            tempmap[i] = tempmap[i - 1];
            tempmap[i - 1] = 0;
            //移動時合併標記也同樣跟隨移動
            if (tempflag[i - 1] == false) {
                tempflag[i - 1] = true;
                tempflag[i] = false;
            }
            flag_r = true;
        } else if (tempmap[i - 1] != 0 && tempmap[i] == tempmap[i - 1] && tempflag[i] == true && tempflag[i - 1] == true) {
            //合併
            tempmap[i] *= 2;
            tempmap[i - 1] = 0;
            //標記經過合併處理
            tempflag[i] = false;
            flag_r = true;
            mark.innerHTML = parseInt(mark.innerHTML) + tempmap[i];
        }
        //遞歸,以解決可能出現的合併後產生空位未處理
        tempmap = changetd(tempmap, tempflag, k, i);
    }
    return tempmap;
}

這部分首先解決的是判斷某個數是否在本輪操作中合併過,這問題的解決我是通過另一個數再記錄對應是否合併的狀態,具體見上面程式碼、

然後通過遞歸來解決一次遍歷操作過後,可能留下的空位的問題

6.顏色更改

為了美觀以及更好的體現數值的不同,根據不同數值給格子渲染上不同背景色

function tdcolor() {
    //根據不同數值給格子渲染上不同背景色
    var tdcolors = {
        "": "#cdc1b4",
        "2": "#eee4da",
        "4": "#ede0c8",
        "8": "#f2b179",
        "16": "#f59563",
        "32": "#f67c5f",
        "64": "#f65e3b",
        "128": "#edcf72",
        "256": "#edcc61",
        "512": "#9c0",
        "1024": "#33b5e5",
        "2048": "#09c",
        "4096": "#a6c",
        "8192": "#93c"
    }
    //數字顏色更改
    for (var i = 1; i <= mapx * mapy; i++) {
        var thetd = document.getElementById(i);
        thetd.style.backgroundColor = tdcolors[thetd.innerHTML];
        if (thetd.innerHTML == 2 || thetd.innerHTML == 4) {
            thetd.style.color = "#776e65";
        } else {
            thetd.style.color = "#f8f5f1";
        }
    }
}

這部分我擔心我自己配色會配的亂七八糟,所以我根據原版的2048配色來😂

當格子里的數字大於4時數字顏色變白的情況我也順便還原了

另外我還設置了大於2048的值🤪

遊戲結束或達到勝利值時提示

遊戲結束或達到勝利值時需要有所提示

function isover() {
    var f = 0;
    for (var i = 1; i <= mapx * mapy; i++) {
        var td = document.getElementById(i);
        if(td.innerHTML >= youwin){
            document.getElementById("gameover").innerHTML="恭喜你達到了 "+td.innerHTML;
            document.getElementById("gameover").style.display = "block";
            youwin=parseInt(td.innerHTML);
        }
        if (td.innerHTML == "") {
            //空值跳過
        } else if (i <= (mapx - 1) * mapy && td.innerHTML == document.getElementById(i + mapy).innerHTML) {
            //判斷該格子下方的數是否與之相同
        } else if (i % mapy != 0 && td.innerHTML == document.getElementById(i + 1).innerHTML) {
            //判斷該格子右邊的數是否與之相同
        } else {
            f++;
        }
    }
    if (f == mapx * mapy) {
        document.getElementById("gameover").innerHTML+="<br>GAME OVER"
        document.getElementById("gameover").style.display = "block";
        overflag = false;
    }
}

這部分程式碼邏輯具體見注釋🤣

youwin的值在初始的那一部分中main()函數中設置

js部分總覽

綜上,js部分總體程式碼如下

window.onload = main(); //載入主函數
var overflag = true; //判斷遊戲是否結束的標誌
//主函數
function main() {
    mapx = 4, mapy = 4, mapt = mapx * mapy; //設定表框大小,一般為4*4
    var table = document.getElementById("maintable"); //獲取表格
    var tableStructure = ""; //儲存表格結構
    var tdid = 1; //單元格id
    var mark = document.getElementById("mark"); //獲取html中顯示分數的位置
    overflag = true; //因為後續新遊戲是重新執行該函數,所以該值要重新變為true

    //載入表格結構(4行4列)
    for (var i = 1; i <= mapx; i++) {
        tableStructure += "<tr>"; //為表格加1行
        //為該行添加單元格
        for (var j = 1; j <= mapy; j++) {
            tableStructure += "<td id=" + tdid + "></td>"; //添加單元格與該單元格id
            tdid++;
        }
        tableStructure += "</tr>";
    }
    table.innerHTML = tableStructure; //向表格返回該表格結構
    tdRandom();
    tdRandom(); //隨機兩個格子給予2或 4
    tdcolor(); //渲染格子的顏色
    mark.innerHTML = 0; //初始總分為0
    document.getElementById("gameover").style.display = "none"; //初始GME OVER提示不顯示
    document.getElementById("gameover").innerHTML="";
    youwin=2048;
}

//因為Math.random() 返回 0(包括) 至 1(不包括) 之間的隨機數,隨機的數不一定是整數。這裡我用向下取整
function myrandom(min, max) {
    return min + Math.floor(Math.random() * max);
}

//隨機為單元格隨機填入2或4 (4出現的概率應相對較小)
function tdRandom() {
    var temp = myrandom(1, mapt);
    if (document.getElementById(temp).innerHTML == "") {
        document.getElementById(temp).innerHTML = Math.random() < 0.9 ? 2 : 4;
    } else {
        tdRandom();
    }
}

//鍵盤事件響應
function keyboardEvents() {
    //以鍵盤方向鍵或wasd操作移動
    if (overflag) {
        if (event.keyCode == 37 || event.keyCode == 65) Left();
        else if (event.keyCode == 38 || event.keyCode == 87) Up();
        else if (event.keyCode == 39 || event.keyCode == 68) Right();
        else if (event.keyCode == 40 || event.keyCode == 83) Down();
        //保證其它按鍵不會觸發
        if (flag_r) {
            tdRandom(); //隨機再產生一個數字
            flag_r = false;
        }
    }
    tdcolor(); //更新格子顏色
    if(overflag)
    isover(); //判斷遊戲是否結束
}

//上下左右操作函數

//向上操作
function Up() {
    for (var i = 1; i <= mapy; i++) {
        var tempmap = []; //記錄該編號格子內的數
        var tempflag = []; //記錄該編號格子是否合併過(本次操作內)
        var z = 0;
        for (var j = i + (mapx - 1) * mapy; j >= i; j -= mapy) {
            var thetd = document.getElementById(j);
            if (thetd.innerHTML == "") {
                tempmap[z] = 0;
            } else {
                tempmap[z] = parseInt(thetd.innerHTML);
            }
            tempflag[z] = true;
            z++;
        }
        //數字合併統一由changetd函數完成
        tempmap = changetd(tempmap, tempflag, tempmap.length, 0); 
        z = 0;
        //更新格子內的顯示內容
        for (var j = i + (mapx - 1) * mapy; j >= i; j -= mapy) {
            var thetd = document.getElementById(j);
            if (tempmap[z] == 0) {
                thetd.innerHTML = "";
            } else {
                thetd.innerHTML = tempmap[z];
            }
            z++;
        }
    }
}

//向下操作
function Down() {

    for (var i = 1; i <= mapy; i++) {
        var tempmap = [];
        var tempflag = [];
        var z = 0;
        for (var j = i; j <= i + (mapx - 1) * mapy; j += mapy) {
            var thetd = document.getElementById(j);
            if (thetd.innerHTML == "") {
                tempmap[z] = 0;
            } else {
                tempmap[z] = parseInt(thetd.innerHTML);
            }
            tempflag[z] = true;
            z++;
        }
        tempmap = changetd(tempmap, tempflag, tempmap.length, 0);
        z = 0;
        for (var j = i; j <= i + (mapx - 1) * mapy; j += mapy) {
            var thetd = document.getElementById(j);
            if (tempmap[z] == 0) {
                thetd.innerHTML = "";
            } else {
                thetd.innerHTML = tempmap[z];
            }
            z++;
        }
    }
}

//向左操作
function Left() {
    for (var i = mapy; i <= mapy + (mapx - 1) * mapy; i += mapy) {
        var tempmap = [];
        var tempflag = [];
        var z = 0;
        for (var j = i; j >= i - mapy + 1; j--) {
            var thetd = document.getElementById(j);
            if (thetd.innerHTML == "") {
                tempmap[z] = 0;
            } else {
                tempmap[z] = parseInt(thetd.innerHTML);
            }
            tempflag[z] = true;
            z++;
        }
        tempmap = changetd(tempmap, tempflag, tempmap.length, 0);
        z = 0;
        for (var j = i; j >= i - mapy + 1; j--) {
            var thetd = document.getElementById(j);
            if (tempmap[z] == 0) {
                thetd.innerHTML = "";
            } else {
                thetd.innerHTML = tempmap[z];
            }
            z++;
        }
    }
}

//向右操作
function Right() {
    for (var i = 1; i <= 1 + (mapx - 1) * mapy; i += mapy) {
        var tempmap = [];
        var tempflag = [];
        var z = 0;
        console.log(i + " i");
        for (var j = i; j < i + mapy; j++) {
            console.log(j);
            var thetd = document.getElementById(j);
            if (thetd.innerHTML == "") {
                tempmap[z] = 0;
            } else {
                tempmap[z] = parseInt(thetd.innerHTML);
            }
            tempflag[z] = true;
            z++;
        }
        tempmap = changetd(tempmap, tempflag, tempmap.length, 0);
        z = 0;
        for (var j = i; j < i + mapy; j++) {
            var thetd = document.getElementById(j);
            if (tempmap[z] == 0) {
                thetd.innerHTML = "";
            } else {
                thetd.innerHTML = tempmap[z];
            }
            z++;
        }
    }
}



function changetd(tempmap, tempflag, k, u) {
    for (var i = k - 1; i > u; i--) {
        if (tempmap[i - 1] != 0 && tempmap[i] == 0) {
            //移動
            tempmap[i] = tempmap[i - 1];
            tempmap[i - 1] = 0;
            //移動時合併標記也同樣跟隨移動
            if (tempflag[i - 1] == false) {
                tempflag[i - 1] = true;
                tempflag[i] = false;
            }
            flag_r = true;
        } else if (tempmap[i - 1] != 0 && tempmap[i] == tempmap[i - 1] && tempflag[i] == true && tempflag[i - 1] == true) {
            //合併
            tempmap[i] *= 2;
            tempmap[i - 1] = 0;
            //標記經過合併處理
            tempflag[i] = false;
            flag_r = true;
            mark.innerHTML = parseInt(mark.innerHTML) + tempmap[i];
        }
        //遞歸,以解決可能出現的合併後產生空位未處理
        tempmap = changetd(tempmap, tempflag, k, i);
    }
    return tempmap;
}

function tdcolor() {
    //根據不同數值給格子渲染上不同背景色
    var tdcolors = {
        "": "#cdc1b4",
        "2": "#eee4da",
        "4": "#ede0c8",
        "8": "#f2b179",
        "16": "#f59563",
        "32": "#f67c5f",
        "64": "#f65e3b",
        "128": "#edcf72",
        "256": "#edcc61",
        "512": "#9c0",
        "1024": "#33b5e5",
        "2048": "#09c",
        "4096": "#a6c",
        "8192": "#93c"
    }
    //數字顏色更改
    for (var i = 1; i <= mapx * mapy; i++) {
        var thetd = document.getElementById(i);
        thetd.style.backgroundColor = tdcolors[thetd.innerHTML];
        if (thetd.innerHTML == 2 || thetd.innerHTML == 4) {
            thetd.style.color = "#776e65";
        } else {
            thetd.style.color = "#f8f5f1";
        }
    }
}

function isover() {
    var f = 0;
    for (var i = 1; i <= mapx * mapy; i++) {
        var td = document.getElementById(i);
        if(td.innerHTML >= youwin){
            document.getElementById("gameover").innerHTML="恭喜你達到了 "+td.innerHTML;
            document.getElementById("gameover").style.display = "block";
            youwin=parseInt(td.innerHTML);
        }
        if (td.innerHTML == "") {
            //空值跳過
        } else if (i <= (mapx - 1) * mapy && td.innerHTML == document.getElementById(i + mapy).innerHTML) {
            //判斷該格子下方的數是否與之相同
        } else if (i % mapy != 0 && td.innerHTML == document.getElementById(i + 1).innerHTML) {
            //判斷該格子右邊的數是否與之相同
        } else {
            f++;
        }
    }
    if (f == mapx * mapy) {
        document.getElementById("gameover").innerHTML+="<br>GAME OVER"
        document.getElementById("gameover").style.display = "block";
        overflag = false;
    }
}

後記

限於我個人糟糕的水平,上面的內容可能有許多不完善的地方或者有更好的辦法,歡迎指出

也歡迎訪問我的小破站//www.226yzy.com/或者//226yzy.github.io/

我的Github226YZY (星空下的YZY) (github.com)