使用纯javascript实现经典扫雷游戏


Posted in Javascript onApril 23, 2015

很久以前写的 当时都没写注释的 刚加上了 (尼玛,好多自己都不认识了 ... )

不足的地方就是本来想写个游戏排名的统计的,等有空了再加上(好像每次都这么说 然后就等好久好久...)

还有就是没有实现:点击第一个格子不能是雷的功能

<style>
  ul{padding:0;list-style:none;}
  #mine{overflow:hidden;width:30px;height:30px;border:1px solid #966;}
  #mine li{float:left;width:30px;height:30px;line-height:30px;text-align:center;font-size:14px;color:#222;}
  #mine .mine_01{background:url(mine.gif) no-repeat;}
  #mine .mine_02{background:url(mine.gif) -30px 0 no-repeat;}
  #mine .mine_03{background:url(mine.gif) -60px 0 no-repeat;}
  #mine .mine_04{background:url(mine.gif) -90px 0 no-repeat;}
  #mine .mine_05{background:url(mine.gif) -120px 0 no-repeat;}
  #mine .mine_06{background:url(mine.gif) -150px 0 no-repeat;}
  #mine .mine_07{background:url(mine.gif) -180px 0 no-repeat;}

  #count{font-size:12px;}
  #time{color:#900;font-weight:bold;}
</style>
 <select id="wh">
 <option value="8*10">8*10</option>
 <option value="10*10">10*10</option>
 <option value="12*12">12*12</option>
 </select>
 <button id='ready'>重新开始</button>
<div id="count">
 计时: <span id="time">0</span>
</div>

<ul id="mine">

</ul>

ie6+ ff oprea 谷歌
opera 早期版本 默认不支持document.oncontextmenu事件 没有找到好的替代方法
<script>
var $ = function(id){return document.getElementById(id)};
window.onload=function ready(){
  var V=$('wh').value.split('*')
  setMineField(Number(V[0]),Number(V[1]));
  
  $('wh').onchange=$('ready').onclick=function(){
    V=$('wh').value.split('*')
    setMineField(Number(V[0]),Number(V[1]))
  }
}
//---------------------------------------------------雷区
var mineField={
  _mineW:30,   //每一个雷的宽度 必须和样式同步
  _mineH:30,
  _mineFieldBlock:$('mine'),
  _mineFieldEle:$('mine').getElementsByTagName('li'),
  _time:$('time'),
  
  status:0,   //雷区状态 0还没开始 1开始了 已经在计时了 2游戏结束了  
  mineNum:0,   //雷数
  clearPlace:0, //统计扫除的格子;
  x:0,      //列
  y:0,      //行
  density:0.2,  //雷的密度 雷区密度不宜超过0.5 在有些尺寸的雷区下 过高的设置 无法生成mineMap数组
  mineMap:[],  //雷区的二维图,0 : 不是雷  -1 : 雷
  time:-1,    //计时 s
  debug:false   //调试模式
}//mineField object end

function timedCount(){
  if(mineField.status!=1){return false}
  mineField.time++;
  mineField._time.innerHTML=mineField.time;
  setTimeout("timedCount()",1000);
}
//--------------------------------------对雷的状态设置
function setThisMine(str,index){

  var allMine=mineField._mineFieldEle;
  //设置雷是否插旗
  if(str=='setOn'){
    var thisMine=allMine[index];
    thisMine.on=thisMine.on<2?thisMine.on+1:0;
    if(thisMine.on==1){thisMine.className='mine_03';}//插旗
    else if(thisMine.on==2){thisMine.className='mine_04'}//取消插旗
    else if(thisMine.on==0){thisMine.className='mine_01'}//取消插旗
    return false;
  }
  //显示方格
  if(str=='show'){
    if(Object.prototype.toString.call(index) === '[object Array]'){//显示一大片
      mineField.clearPlace=mineField.clearPlace+index.length;
      for(var i=0;i<index.length;i++){
        var thisMine=allMine[index[i]];
        thisMine.innerHTML=mineField.mineMap[thisMine.index];
        thisMine.className='mine_02';
        thisMine.on=3;
      }
    }
    else{//显示单个非雷区域
      mineField.clearPlace++;
      allMine[index].on=3;
      allMine[index].innerHTML=mineField.mineMap[index];
      allMine[index].className='mine_02';
    }
    if(mineField.clearPlace==mineField.x*mineField.y-mineField.mineNum){//恭喜你
      alert('恭喜你');
      for(var i=0;i<allMine.length;i++){
        if(mineField.mineMap[allMine[i].index]==-1){
          allMine[i].className='mine_07';
        }
      }
      mineField.status=2;
      return false;
    }
  }
  //显示全部雷
  if(str=='peng'){
      for(var i=0;i<allMine.length;i++){
        if(mineField.mineMap[allMine[i].index]==-1){
          allMine[i].className=i==index?'mine_06':'mine_05';
        }
      }
      mineField.status=2;
  }
}
//-----------------------------------------------设置行数 和 列数
function setMineField(a,b){
    var thisMineFiele=mineField._mineFieldBlock;
    DivBox=document.createElement("div"),
    num=a*b,k=0;
    thisMineFiele.innerHTML='';
    //雷区参数调整
    mineField.x=a;//有几列
    mineField.y=b;//有几行
    mineField.mineNum=Math.floor(a*b*mineField.density);
    mineField.status=0;
    mineField.time=-1;
    mineField.clearPlace=0;
    mineField.mineMap.length=0;
    mineField._time.innerHTML=0;
        
    //生成雷区地图
    setMineMap();
    //生成雷区(创建li)
    while(k<num){
    var newLi=document.createElement("li");
    if(mineField.debug) newLi.innerHTML=mineField.mineMap[k];//作弊
    newLi.className='mine_01';
    DivBox.appendChild(newLi);
    k++;
    }
    thisMineFiele.style.height=mineField._mineW*b+'px';
    thisMineFiele.style.width=mineField._mineH*a+'px';
    thisMineFiele.innerHTML=DivBox.innerHTML;
    DivBox=null;
    setEvent();//事件
}
//-----------------------------------生成雷区地图
function setMineMap(){
    var num=mineField.x*mineField.y,
      mineNum=mineField.mineNum,
      Interval=Math.floor(num/mineNum);
    for(var i=0;i<num;i++){
      if(i%Interval==0&&i<mineNum*Interval){mineField.mineMap[i]=-1;}else{mineField.mineMap[i]=0;}//雷等距离分布与数组中
    }
    mineField.mineMap.sort(function(){ return 0.5 - Math.random()})//打乱数组
    
    //判断方格周围是否都是雷 如果是的话 要重新生成雷区 从而避免成片雷区的存在
    var br=0,//所在行
      x=mineField.x,
      L_T,T,R_T,L,R,L_B,B,R_B;
    for(var i=0;i<num;i++){
      br=Math.ceil((i+1)/x);
      L_T = i-x-1;
      T  = i-x;
      R_T = i-x+1;
      L  = i-1;
      R  = i+1;
      L_B = i+x-1;
      B  = i+x;
      R_B = i+x+1;
       //坐上角 如果在雷区 并且是在上一行 并且他不是雷 就进行下一方格检测
       if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&mineField.mineMap[L_T]==0){continue}
       if(T >=0&&Math.ceil((T +1)/x)==br-1&&mineField.mineMap[T ]==0){continue}
       if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&mineField.mineMap[R_T]==0){continue}
       
       if(L>=0 &&Math.ceil((L+1)/x)==br&&mineField.mineMap[L]==0){continue}
       if(R<num&&Math.ceil((R+1)/x)==br&&mineField.mineMap[R]==0){continue}
       
       if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&mineField.mineMap[L_B]==0){continue}
       if(B <num&&Math.ceil((B +1)/x)==br+1&&mineField.mineMap[B ]==0){continue}
       if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&mineField.mineMap[R_B]==0){continue}
       
       setMineMap();
       return false
    }
    //统计非雷方格周围的雷数
    for(i=0;i<num;i++){
       if(mineField.mineMap[i]==-1){continue}
       var thisMineNum=0
       br=Math.ceil((i+1)/x);
       L_T = i-x-1;
       T  = i-x;
       R_T = i-x+1;
       L  = i-1;
       R  = i+1;
       L_B = i+x-1;
       B  = i+x;
       R_B = i+x+1;
       
       if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&mineField.mineMap[L_T]==-1){thisMineNum++;}
       if(T >=0&&Math.ceil((T +1)/x)==br-1&&mineField.mineMap[T ]==-1){thisMineNum++;}
       if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&mineField.mineMap[R_T]==-1){thisMineNum++;}
       
       if(L>=0 &&Math.ceil((L+1)/x)==br&&mineField.mineMap[L]==-1){thisMineNum++;}
       if(R<num&&Math.ceil((R+1)/x)==br&&mineField.mineMap[R]==-1){thisMineNum++;}
       
       if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&mineField.mineMap[L_B]==-1){thisMineNum++;}
       if(B <num&&Math.ceil((B +1)/x)==br+1&&mineField.mineMap[B ]==-1){thisMineNum++;}
       if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&mineField.mineMap[R_B]==-1){thisMineNum++;}
       
       mineField.mineMap[i]=thisMineNum;
    }
}
//----------------------------------雷区事件
function setEvent(){
    var thisMineFiele=mineField._mineFieldBlock,
      allMine=mineField._mineFieldEle,
      iMax=mineField.x*mineField.y;
    for(var i=0;i<iMax;i++){
      allMine[i].index=i;
      allMine[i].on=0;//0为默认 1为插旗 2为问号 3为显示
    }
    thisMineFiele.onmouseup=function(e){
      if(mineField.status==2){return false;}
      if(mineField.status==0){mineField.status=1;timedCount();}
      var e=e||window.event,
        thisObj=e.target||e.srcElement;
      if(thisObj.nodeName=='UL'||thisObj.on==3){return false;}
      
      var Btn=getButton(e);
      if(Btn== 0){//左键
        if(thisObj.on==1){return false;}//插旗子了 就不能点了

        if(mineField.mineMap[thisObj.index]==-1){//点雷了
          setThisMine('peng',thisObj.index);
        }else if(mineField.mineMap[thisObj.index]==0){//点到空地了 打开一大片
          //alert('你运气真好')
          var allShowMine=minesShow(thisObj.index);
          setThisMine('show',allShowMine)
        }else{//显示一个格子
          setThisMine('show',thisObj.index)
        }
      }
      if(Btn== 2){//右键
       setThisMine('setOn',thisObj.index);
      }
    }
}
//--------------------------------按到空格时 显示一大片
function minesShow(I){
      var allMine=mineField._mineFieldEle,
        allShowMine=[I];//保存要显示的雷的下标
      
      allMine[I].on=3;
      
      see(I);//查询下标为I的周围的方格
      function see(allI){
        var _allI=[];
        if(Object.prototype.toString.call(allI) === '[object Array]'){
          for(var i=0;i<allI.length;i++){f(allI[i])}
        }
        else{f(allI)}
        function f(thisI){
          var text,
          x=mineField.x,
          br,
          num=x*mineField.y,
          L_T,T,R_T,L,R,L_B,B,R_B;
        
          text='_'+allShowMine.join('_')+'_';//用来判断下标是否已经写入数组
          br=Math.ceil((thisI+1)/x);
          L_T = thisI-x-1;
          T  = thisI-x;
          R_T = thisI-x+1;
          L  = thisI-1;
          R  = thisI+1;
          L_B = thisI+x-1;
          B  = thisI+x;
          R_B = thisI+x+1;
         //左上角的方格 如果是在雷区 又是在上一行 又是没翻开的格子 又是空格 那么就写如_allI数组 来进行下一次检索
         if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&allMine[L_T].on==0&&mineField.mineMap[L_T] == 0){_allI.push(L_T);}
         if(T >=0&&Math.ceil((T +1)/x)==br-1&&allMine[T ].on==0&&mineField.mineMap[T ] == 0){_allI.push(T);}
         if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&allMine[R_T].on==0&&mineField.mineMap[R_T] == 0){_allI.push(R_T);}
       
         if(L>=0&&Math.ceil((L+1)/x)==br&&allMine[L].on==0&&mineField.mineMap[L] == 0){_allI.push(L);}
         if(R<num&&Math.ceil((R+1)/x)==br&&allMine[R].on==0&&mineField.mineMap[R] == 0){_allI.push(R);}
       
         if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&allMine[L_B].on==0&&mineField.mineMap[L_B] == 0){_allI.push(L_B);}
         if(B <num&&Math.ceil((B +1)/x)==br+1&&allMine[B ].on==0&&mineField.mineMap[B ] == 0){_allI.push(B);}
         if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&allMine[R_B].on==0&&mineField.mineMap[R_B] == 0){_allI.push(R_B);}
         //------------------------------------------------
         //左上角的的格子 如果在雷区 又是在上一行 又是没翻开的格子 又是没写入allShowMine数组的 那就写入吧 并提前标记为翻开的格子
         if(L_T>=0&&Math.ceil((L_T+1)/x)==br-1&&allMine[L_T].on==0&&text.indexOf('_'+L_T+'_') == -1){allShowMine.push(L_T);allMine[L_T].on=3}
         if(T >=0&&Math.ceil((T +1)/x)==br-1&&allMine[T ].on==0&&text.indexOf('_'+T+'_') == -1){allShowMine.push(T);allMine[T].on=3}
         if(R_T>=0&&Math.ceil((R_T+1)/x)==br-1&&allMine[R_T].on==0&&text.indexOf('_'+R_T+'_') == -1){allShowMine.push(R_T);allMine[R_T].on=3}
       
         if(L>=0&&Math.ceil((L+1)/x)==br&&allMine[L].on==0&&text.indexOf('_'+L+'_') == -1){allShowMine.push(L);allMine[L].on=3}
         if(R<num&&Math.ceil((R+1)/x)==br&&allMine[R].on==0&&text.indexOf('_'+R+'_') == -1){allShowMine.push(R);allMine[R].on=3}
       
         if(L_B<num&&Math.ceil((L_B+1)/x)==br+1&&allMine[L_B].on==0&&text.indexOf('_'+L_B+'_') == -1){allShowMine.push(L_B);allMine[L_B].on=3}
         if(B <num&&Math.ceil((B +1)/x)==br+1&&allMine[B ].on==0&&text.indexOf('_'+B+'_') == -1){allShowMine.push(B );allMine[B ].on=3}
         if(R_B<num&&Math.ceil((R_B+1)/x)==br+1&&allMine[R_B].on==0&&text.indexOf('_'+R_B+'_') == -1){allShowMine.push(R_B);allMine[R_B].on=3}
         
         if(_allI.length!=0){see(_allI)}
        }
      }
      return allShowMine
}
//--------------------------------------
document.oncontextmenu=function(){return false;}//禁止右键菜单
function getButton(e){
  var Btn;
  if(document.implementation.hasFeature('MouseEvents','2.0')){Btn=e.button;}
  else{    switch(e.button){
          case 0:
          case 1:
          case 3:
          case 5:
          case 7:
           Btn=0;
           break;
          case 2:
          case 6:
           Btn=2;
           break;
          case 4:
           Btn=9;
           break;
        }
  }
  return Btn;
}
</script>

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
Javascript实现的分页函数
Dec 22 Javascript
推荐25个超炫的jQuery网格插件
Nov 28 Javascript
jQuery前端分页示例分享
Feb 10 Javascript
Js可拖拽放大的层拖动特效实现方法
Feb 25 Javascript
javascript实现的闭包简单实例
Jul 17 Javascript
全面解析bootstrap格子布局
May 22 Javascript
解决同一页面中两个iframe互相调用jquery,js函数的方法
Dec 12 Javascript
Javascript实现倒计时时差效果
May 18 Javascript
jQuery动态添加元素无法触发绑定事件的解决方法分析
Jan 02 jQuery
JS实现字符串去重及数组去重的方法示例
Apr 21 Javascript
element ui 表格动态列显示空白bug 修复方法
Sep 04 Javascript
layui实现下拉框三级联动
Jul 26 Javascript
jQuery图片特效插件Revealing实现拉伸放大
Apr 22 #Javascript
jQuery插件jcrop+Fileapi完美实现图片上传+裁剪+预览的代码分享
Apr 22 #Javascript
javascript解三阶幻方(九宫格)
Apr 22 #Javascript
javascript递归回溯法解八皇后问题
Apr 22 #Javascript
使用C++为node.js写扩展模块
Apr 22 #Javascript
node.js 使用ejs模板引擎时后缀换成.html
Apr 22 #Javascript
JavaScript模拟深蓝vs卡斯帕罗夫的国际象棋对局示例
Apr 22 #Javascript
You might like
压力如何影响浓缩咖啡品质
2021/03/03 咖啡文化
php使用自定义函数实现汉字分割替换功能示例
2017/01/30 PHP
详谈symfony window下的安装 安装时候出现的问题以及解决方法
2017/09/28 PHP
PHP连接sftp并下载文件的方法教程
2018/08/26 PHP
php连接mysql之mysql_connect()与mysqli_connect()的区别
2020/07/19 PHP
JQuery动态给table添加、删除行 改进版
2011/01/19 Javascript
关于jquery.validate1.9.0前台验证的使用介绍
2013/04/26 Javascript
使用jQuery UI的tooltip函数修饰title属性的气泡悬浮框
2013/06/24 Javascript
jquery实现带二级菜单的导航示例
2014/04/28 Javascript
jQuery中wrapAll()方法用法实例
2015/01/16 Javascript
javascript连续赋值问题
2015/07/08 Javascript
Bootstrap表单Form全面解析
2016/06/13 Javascript
修改Jquery Dialog 位置的实现方法
2016/08/26 Javascript
详解使用vue-router进行页面切换时滚动条位置与滚动监听事件
2017/03/08 Javascript
jQuery获取table下某一行某一列的值实现代码
2017/04/07 jQuery
AngularJS 霸道的过滤器小结
2017/04/26 Javascript
Koa项目搭建过程详细记录
2018/04/12 Javascript
vue axios基于常见业务场景的二次封装的实现
2018/09/21 Javascript
浅谈vue引用静态资源需要注意的事项
2018/09/28 Javascript
js中的数组对象排序分析
2018/12/11 Javascript
Python 26进制计算实现方法
2015/05/28 Python
Python切片知识解析
2016/03/06 Python
python 日志增量抓取实现方法
2018/04/28 Python
用Python编写一个简单的CS架构后门的方法
2018/11/20 Python
Kali Linux安装ipython2 和 ipython3的方法
2019/07/11 Python
python通过链接抓取网站详解
2019/11/20 Python
利用Python实现斐波那契数列的方法实例
2020/07/26 Python
基于 Python 实践感知器分类算法
2021/01/07 Python
波兰运动鞋网上商店:e-Sporting
2018/02/16 全球购物
什么是Oracle的后台进程background processes?都有哪些后台进程?
2012/04/26 面试题
甲方资料员岗位职责
2013/12/13 职场文书
标准单位租车协议书
2014/09/23 职场文书
手机销售员岗位职责
2015/04/11 职场文书
中学感恩教育活动总结
2015/05/05 职场文书
2016年大学生社会实践心得体会
2015/10/09 职场文书
python3 删除所有自定义变量的操作
2021/04/08 Python