使用纯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 相关文章推荐
关于可运行代码无法正常执行的使用说明
May 13 Javascript
一个奇葩的最短的 IE 版本判断JS脚本
May 28 Javascript
jquery分割字符串的方法
Jun 24 Javascript
微信小程序 wxapp内容组件 progress详细介绍
Oct 31 Javascript
Bootstrap CSS组件之导航(nav)
Dec 17 Javascript
Angular.js中处理页面闪烁的方法详解
Mar 09 Javascript
ES6新特性五:Set与Map的数据结构实例分析
Apr 21 Javascript
解决axios会发送两次请求,有个OPTIONS请求的问题
Oct 25 Javascript
VUE2.0+ElementUI2.0表格el-table循环动态列渲染的写法详解
Nov 30 Javascript
JavaScript数组及常见操作方法小结
Nov 13 Javascript
vue 授权获取微信openId操作
Nov 13 Javascript
手写Vue源码之数据劫持示例详解
Jan 04 Vue.js
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
BBS(php &amp; mysql)完整版(五)
2006/10/09 PHP
详解WordPress中给链接添加查询字符串的方法
2015/12/18 PHP
WordPress分页伪静态加html后缀
2016/06/08 PHP
php的debug相关函数用法示例
2016/07/11 PHP
php 无限分类 树形数据格式化代码
2016/10/11 PHP
PHP机器学习库php-ml的简单测试和使用方法
2017/07/14 PHP
JQuery实现的在新窗口打开链接的方法小结
2010/04/22 Javascript
jquery选择checked在ie8普通模式下的问题
2014/02/12 Javascript
window.open()详解及浏览器兼容性问题示例探讨
2014/05/29 Javascript
JS常见问题之为什么点击弹出的i总是最后一个
2016/01/05 Javascript
jQuery代码实现图片墙自动+手动淡入淡出切换效果
2016/05/09 Javascript
jQuery基础知识点总结(必看)
2016/05/31 Javascript
AngularJS通过ng-route实现基本的路由功能实例详解
2016/12/13 Javascript
JavaScript中的Proxy对象
2020/11/27 Javascript
vue+element实现动态加载表单
2020/12/13 Vue.js
rhythmbox中文名乱码问题解决方法
2008/09/06 Python
Python中的装饰器用法详解
2015/01/14 Python
Python自定义scrapy中间模块避免重复采集的方法
2015/04/07 Python
机器学习的框架偏向于Python的13个原因
2017/12/07 Python
python脚本生成caffe train_list.txt的方法
2018/04/27 Python
python制作简单五子棋游戏
2019/06/18 Python
python hough变换检测直线的实现方法
2019/07/12 Python
Python sklearn中的.fit与.predict的用法说明
2020/06/28 Python
伦敦鲜花递送:Flower Station
2021/02/03 全球购物
一套.net面试题及答案
2016/11/02 面试题
WINDOWS域的具体实现方式是什么
2014/02/20 面试题
安全生产汇报材料
2014/02/17 职场文书
机关党员进社区活动总结
2014/07/05 职场文书
汉语专业毕业生自荐信
2014/07/06 职场文书
城市规划应届生推荐信
2014/09/08 职场文书
乡镇党委书记第三阶段个人整改措施
2014/09/16 职场文书
公司领导班子四风对照检查材料
2014/09/27 职场文书
滴水洞导游词
2015/02/10 职场文书
三八妇女节寄语
2015/02/27 职场文书
大学学习委员竞选稿
2015/11/20 职场文书
关于Vue Router的10条高级技巧总结
2021/05/06 Vue.js