使用纯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 表格排序和表头浮动效果(扩展SortTable)
Apr 07 Javascript
UpdatePanel和Jquery冲突的解决方法
Apr 01 Javascript
JS取文本框中最小值的简单实例
Nov 29 Javascript
JQuery radio(单选按钮)操作方法汇总
Apr 15 Javascript
js jquery获取当前元素的兄弟级 上一个 下一个元素
Sep 01 Javascript
编写高性能Javascript代码的N条建议
Oct 12 Javascript
node.js实现爬虫教程
Aug 25 Javascript
Bootstrap基本组件学习笔记之input输入框组(9)
Dec 07 Javascript
微信小程序 自定义对话框实例详解
Jan 20 Javascript
jquery事件与绑定事件
Mar 16 Javascript
在小程序Canvas中使用measureText的方法示例
Oct 19 Javascript
vue中利用three.js实现全景图的完整示例
Dec 07 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
AJAX for PHP简单表数据查询实例
2007/01/02 PHP
解决文件名解压后乱码的问题 将文件名进行转码的代码
2012/01/10 PHP
PHP生成随机数的方法实例分析
2015/01/22 PHP
PHPStorm+XDebug进行调试图文教程
2016/06/13 PHP
详解cookie验证的php应用的一种SSO解决办法
2017/10/20 PHP
PHP实现的微信APP支付功能示例【基于TP5框架】
2019/09/16 PHP
Javascript基础教程之数据类型 (布尔型 Boolean)
2015/01/18 Javascript
jquery实现对联广告的方法
2015/02/05 Javascript
JavaScript中的函数模式详解
2015/02/11 Javascript
Jquery中基本选择器用法实例详解
2015/05/18 Javascript
详解JavaScript对Date对象的操作问题(生成一个倒数7天的数组)
2015/10/01 Javascript
第一次动手实现bootstrap table分页效果
2016/09/22 Javascript
Extjs gridpanel 中的checkbox(复选框)根据某行的条件不能选中的解决方法
2017/02/17 Javascript
微信小程序中顶部导航栏的实现代码
2017/03/30 Javascript
让nodeJS支持ES6的词法----babel的安装和使用方法
2017/07/31 NodeJs
Vue实战之vue登录验证的实现代码
2017/10/31 Javascript
基于JavaScript中标识符的命名规则介绍
2018/01/06 Javascript
详解在React项目中安装并使用Less(用法总结)
2019/03/18 Javascript
JavaScript中如何对多维数组(矩阵)去重的实现
2019/12/04 Javascript
js页面加载后执行的几种方式小结
2020/01/30 Javascript
uniapp电商小程序实现订单30分钟倒计时
2020/11/01 Javascript
axios解决高并发的方法:axios.all()与axios.spread()的操作
2020/11/09 Javascript
[56:24]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#1Liquid VS MVP.Phx第二局
2016/03/04 DOTA
python求素数示例分享
2014/02/16 Python
Python中编写ORM框架的入门指引
2015/04/29 Python
python读写配置文件操作示例
2019/07/03 Python
Django实现任意文件上传(最简单的方法)
2020/06/03 Python
css3高级选择器使用方法
2013/12/02 HTML / CSS
为什么要有struct关键字
2012/05/08 面试题
工业自动化毕业生自荐信范文
2014/01/04 职场文书
幼儿园教师考核制度
2014/02/01 职场文书
中国梦我的梦演讲稿
2014/04/23 职场文书
民主评议党员自我鉴定
2014/10/21 职场文书
领导班子整改措施
2014/10/24 职场文书
2015领导干部廉洁自律工作总结
2015/07/23 职场文书
协议书格式模板
2016/03/24 职场文书