使用纯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 相关文章推荐
用js生产批量批处理执行命令
Jul 28 Javascript
重写javascript中window.confirm的行为
Oct 21 Javascript
angular.foreach 循环方法使用指南
Jan 06 Javascript
使用Node.js配合Nginx实现高负载网络
Jun 28 Javascript
基于canvas实现的钟摆效果完整实例
Jan 26 Javascript
jQuery实现的精美平滑二级下拉菜单效果代码
Mar 28 Javascript
基于JS代码实现简单易用的倒计时 x 天 x 时 x 分 x 秒效果
Jul 13 Javascript
vue实现图片加载完成前的loading组件方法
Feb 05 Javascript
Element Input组件分析小结
Oct 11 Javascript
JS中使用cavas截图网页并解决跨域及模糊问题
Nov 13 Javascript
Vue拖拽组件列表实现动态页面配置功能
Jun 17 Javascript
ES6的循环与可迭代对象示例详解
Jan 31 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
php自动跳转中英文页面
2008/07/29 PHP
PHP Pear 安装及使用
2009/03/19 PHP
php基于自定义函数记录log日志方法
2017/07/21 PHP
PHP读取目录树的实现方法分析
2019/03/22 PHP
实例分析10个PHP常见安全问题
2019/07/09 PHP
js直接编辑当前cookie的脚本
2008/09/14 Javascript
JavaScript代码简单实现求杨辉三角给定行的最大值
2013/10/29 Javascript
浅谈JavaScript函数节流
2014/12/09 Javascript
Javascript中3个需要注意的运算符
2015/04/02 Javascript
使用JavaScript脚本无法直接改变Asp.net中Checkbox控件的Enable属性的解决方法
2015/09/16 Javascript
使用angularjs创建简单表格
2016/01/21 Javascript
jQuery获取当前点击的对象元素(实现代码)
2016/05/19 Javascript
JavaScript与java语言有什么不同
2016/09/22 Javascript
js代码实现下拉菜单【推荐】
2016/12/15 Javascript
了解VUE的render函数的使用
2017/06/08 Javascript
jQuery取得元素标签名称小结(附代码)
2017/08/16 jQuery
vue axios请求超时的正确处理方法
2018/04/02 Javascript
浅析node.js的模块加载机制
2018/05/25 Javascript
使用Layui搭建后台管理界面的操作方法
2019/09/20 Javascript
浅谈layui 表单元素的选中问题
2019/10/25 Javascript
[51:22]Fnatic vs IG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
[56:42]完美世界DOTA2联赛循环赛 Matador vs Forest 第二场 11.06
2020/11/06 DOTA
Python文件及目录操作实例详解
2015/06/04 Python
python django 增删改查操作 数据库Mysql
2017/07/27 Python
浅谈python str.format与制表符\t关于中文对齐的细节问题
2019/01/14 Python
pip安装python库的方法总结
2019/08/02 Python
Python 实现黑客帝国中的字符雨的示例代码
2020/02/20 Python
用python实现学生管理系统
2020/07/24 Python
HTML5中的Web Notification桌面右下角通知功能的实现
2018/04/19 HTML / CSS
2014机关干部学习“焦裕禄精神”思想汇报
2014/09/19 职场文书
2014年局领导班子自身建设情况汇报
2014/11/21 职场文书
公务员考察材料
2014/12/23 职场文书
六一儿童节园长致辞
2015/07/31 职场文书
商务宴会祝酒词
2015/08/11 职场文书
CSS3点击按钮圆形进度打钩效果的实现代码
2021/03/30 HTML / CSS
mysql字符串截取函数小结
2021/04/05 MySQL