js实现扫雷游戏

javascript作为前端常用的开发语言,越来越多的人都在学习它,今天就教大家利用js来实现基本的扫雷游戏。由于本篇文章主要面向于初学者,因此在思想上我会尽最大努力让它简单化,若有读者看完有不明白请多多反馈。

预备知识:Javascript语言的基础知识,HTML、CSS(这里用的不多,了解一点即可),数据结构

首先,我先简单的介绍一下扫雷游戏的基本规则(了解规则的跳过即可)

1.初始状态,玩家只能看到若干个方格,方格可以被点击

2.若点击的方格是雷,则显示棋盘上的所有雷,游戏结束,当前被点击的雷块的背景颜色发生变化,并且这时点击任何方格将不会有任何反应。

3.若点击的部分不是雷,并且该块的四周有雷,则该块显示周围雷的数量。

4.若点击的部分不是雷并且该块的四周没有雷,该块显示空白,并且周围无雷区域会自动打开。

5.右键点击,点击后可以标记玩家认为是雷的方格。(如图红色的方格)

主要的规则就这些,接下来整理一下整体思路(以9*9的棋盘为例)

1.创建棋盘边界的div

<h1><span></span>颗雷</h1>
<div id="father"></div> 
#father{
            width: 468px;
            height: 468px;
            border: 3px solid black;
        }

 

2.初始化棋盘,即创建若干个方格div,并且将每个div给予一个id值,保证每个id是相互独立的,之后依次添加到边界div中即可

var square = document.getElementsByTagName("div");
var span = document.getElementsByTagName("span");
function init(n) {
                for(var i=1;i<=n;i++){
                    var check = document.createElement("div");
                    check.id = i;
                    check.setAttribute("displayName",0);
                    check.className="a";
                    check.innerHTML = i;
                    check.style.width = "50px";
                    check.style.height = "50px";
                    check.style.border = "1px solid red";
                    check.style.backgroundColor = "#5ad7d7";
                    check.style.float = "left";
                    square[0].appendChild(check);
                }
            }

以9*9棋盘为例,因此传入n=81

 当然,在真正的游戏中是不显示方格对应id的值,因此后续需要将check.innerHTML = i;注释掉

3.构建图的数据结构

主要思路:用js中的字典来存储,我们知道字典中有键值对,那么每一个id值就可以充当字典的键,而该键对应的值就是四周可以检测到的范围

如图所示:

中间的大圆圈部分都有8个周边方格,

如34,周边的方格有24,25,26,33,35,42,43,44 设当前id值为num,周边方格可总结为num-1,num+1,num-8,num+8,num-9,num+9,num-10,num+10

四个角比较特殊,只有三个周边方格,如9,周边方格有8,17,18,由于格数较少,这里特殊处理

最后就是四周的方格了,分上下左右四个方向来考虑

上(id值大于等于2小于等于8):比如5,周边方格有4,6,13,14,15    设当前id值为num,周边方格可总结为num-1,num+1,num+8,num+9,num+10

下(id值大于等于74小于等于80):比如78,周边方格有,68,69,70,77,79   设当前id值为num,周边方格可总结为num-1,num+1,num-10,num-9,num-8

左(id值除以9余数为1):比如37,周边方格有28,29,38,46,47  设当前id值为num,周边方格可总结为num-9,num+9,num+1,num-8,num+10

右(id值除以9余数为0):比如54,周边方格有44,45,62,63,53  设当前id值为num,周边方格可总结为um-1,num-9,num+9,num-10,num+8

具体代码如下所示(只适合9*9的棋盘,读者熟悉之后可自行修改成适用多行列的):

function sweeper(num){
                var arr = [];
                if(num==1){
                    arr.push(2,10,11)
                }else if(num==9){
                    arr.push(8,17,18)
                }else if(num==73){
                    arr.push(64,65,74)
                }else if(num==81){
                    arr.push(71,72,80)
                }else if((num>=2)&&(num<=8)){
                    arr.push(num-1,num+1,num+8,num+9,num+10)
                }else if((num>=74)&&(num<=80)){
                    arr.push(num-1,num+1,num-10,num-9,num-8)
                }else if(num%9==1){
                    arr.push(num-9,num+9,num+1,num-8,num+10)
                }else if(num%9==0){
                    arr.push(num-1,num-9,num+9,num-10,num+8)
                }
                else{
                    arr.push(num-1,num+1,num-8,num+8,num-9,num+9,num-10,num+10)
                }
                return arr;
        }
           
        function sweeper_graph(n){
             dict = {}
             for(i=1;i<=n;i++){
                arr = sweeper(i);
                dict[i]=arr;
             }
             return dict;
        }

 在真正的开发中,不建议写死,这里为了让大家看的更明白,先写成死数据,待读者熟悉后可自行修改,灵活运用。

上述还是以9*9为例,调用sweeper_graph函数,用变量graph来接收其返回值,并且可打印变量进行查看

 上述为图的部分展示结果,可对应上边棋盘进行对比,形象的展示了每个方格周边方格的id值,即节点的邻接点。之后检测雷的工作都可以利用这个字典即可。

4.随机生成雷

 function radom_thunder(spread_num,n){
            var arr = [];
            for (var i = 1; i <=n; i++) {
                arr.push(i);
            }
            arr.sort(
            function () {
                return 0.5 - Math.random();
            });
            arr.length = spread_num;
            //console.log(arr)
            for(var i=0;i<arr.length;i++){
                    square[arr[i]].className="b";
                }
            }

 同样还是9*9的棋盘,以18颗雷为例,调用radom_thunder(18,81),类似的可以看到square的打印结果

如上图,class为b的方格就是雷区。

5.计算每个方格周围的雷

function cal_spread(graph){
            spread_num_arr = [];  //为了展示效果,单纯开发不需要这个
            for(var key in dict){
                var count = 0;
                if(document.getElementById(key).getAttribute("class")!="b"){     //当前方格不是雷的时候,计算当前方格周围的雷数
                    for(var i=0;i<dict[key].length;i++){
                        if(document.getElementById(dict[key][i]).getAttribute("class")==="b"){
                            count++;               
                        }    
                    }
                    document.getElementById(key).setAttribute("displayName",count); //为方格div创建一个新属性displayName,赋值为当前方格周围雷的数
                    temp = document.getElementById(key).getAttribute("displayName")  // 提取属性displayName值,为了展示效果,单纯开发不需要这行
                }
                else{                                                              //当前方格是雷的时候,
                    document.getElementById(key).setAttribute("displayName",-1);    //为方格div创建一个新属性displayName 赋值为-1.
                    temp = document.getElementById(key).getAttribute("displayName")  // 属性displayName值,为了展示效果,单纯开发不需要这行
                }
                spread_num_arr.push(temp);//为了展示效果,单纯开发不需要这行
            }
            return spread_num_arr; //为了展示效果,单纯开发不需要这个行
        }  
arr = cal_spread(graph);     //为了展示效果,单纯开发不需要这行
for(i=1;i<square.length;i++){   //为了展示效果,单纯开发不需要这行
square[i].innerHTML = arr[i-1];  //为了展示效果,单纯开发不需要这行
}

 如上图, -1代表雷,其他数字代表当前方格周围雷的数量,当然在游戏中不必显示出该结果,因此上边说明需要注释的代码不写也可以,检测代码对错的时候可以这样检测。

6.检测雷

//检测雷
            function open_test(x){   //传入方格的id值
            num = document.getElementById(x).getAttribute("displayName")
            if(num<0){      //当前方格为雷
                document.getElementById(x).style.backgroundColor ="white";  //当前方格背景颜色变为白色
                for(var k=0; k<square.length; k++){
                    square[k].onclick = null;         //所有方格点击后无任何响应
                    if(square[k].className==="b"){    //显示棋盘上的所有雷
                        square[k].innerHTML = "雷";
                        square[k].style.color = "red";
                        square[k].style.fontSize = "32px";
                        square[k].style.textAlign = "center";
                    }
                }
                alert("踩到雷了,再试一次吧!");  //游戏失败结束弹窗
            }else if(num>0){        //当前方格没有雷,并且周围有雷,
                if((seen.includes(x)==false)){      //该方格未被点击过  seen中存放点击过的方格  等会在入口函数中定义
                    seen.push(x);   //将该方格对应id放到seen中
                }
                document.getElementById(x).innerHTML=num;   //当前方格显示周边雷的数量
                document.getElementById(x).style.backgroundColor ="white"; //当前方格背景颜色变为白色
                document.getElementById(x).style.textAlign = "center";  //当前方格文字居中
                return;
            }else{      //当前方格没有雷,并且周围也没有雷,
                document.getElementById(x).innerHTML="";     //显示空白
                document.getElementById(x).style.backgroundColor ="white";   //当前方格背景颜色变为白色
                for(var i=0;i<graph[x].length;i++){
                    if((seen.includes(graph[x][i])==false)){        //若周边的方格没有被点击过 递归的去检测周边的所有方格,直到周围有雷为止跳出递归
                            seen.push(graph[x][i]);
                            open_test(graph[x][i]); 
                        }
                }
            }
          }

7.入口函数:上边一系列函数的调用(完全面向过程的思维方式)

function main(){
                check_num = 81;
                init(check_num);  //初始化棋盘
                spread_num = 18;  //设置雷的数量
                span[0].innerHTML = spread_num;  //显示雷的数量
                seen=[]; //存放被点击过的方格id
                var left = 0;  //鼠标右键监听的两个变量
                var right = 0;
                graph = sweeper_graph(check_num);   //构建图结构
                radom_thunder(spread_num,check_num);    //随机生成雷
                cal_spread(graph);  //计算非雷方格周边雷的数量
                //左键点击扫雷
                for(var i=1; i<square.length; i++){
                    square[i].onclick = function(){
                        open_test(this.id); //检测雷
                        if(seen.length>=(check_num-spread_num)){
                          alert("恭喜您躲过了所有雷");  //游戏成功弹窗提示
                        }
                    }
                }
                //右键点击标记
                for(var l = 1; l<square.length; l++){
                    square[l].oncontextmenu = function () {
                    if(!this.left){
                      if(!right){
                        this.style.backgroundColor = "red";
                        right = 1;
                      }else{
                        this.style.backgroundColor = "#5ad7d7";
                        right = 0;
                      }
                    } else if(this.left){
                          return true;
                        }
                 return false;//返回值为false,避免鼠标原有的右击事件。
              };
         }
}

 运行main()函数完成就可以玩扫雷游戏了。

 

本次思想主要偏于面向过程,有兴趣的读者可以利用面向对象的思维来实现该游戏,体会二者之间的优点与不足。

posted @ 2020-12-16 17:05  小菜代码  阅读(1090)  评论(1编辑  收藏  举报