一个简单的扫雷小游戏_纯原生Javascript写的

先来预览一下效果:

下面就直接上代码吧:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>经典扫雷</title>

<style>
.lvld{margin:5px auto;text-align:center;}
.lvld input{width:48px;text-align:center;}
.head{margin:auto;height:24px;padding:5px;border:2px solid #bdbdbd;background:#ddd;text-align:center;position:relative;}
.nnum{position:absolute;z-index:0;left:5px;top:5px;} .ntime{position:absolute;z-index:0;right:5px;top:5px;}
.main{border:2px solid #bdbdbd;margin:auto;width:fit-content;clear:both;}
#mine{margin:auto;border:2px solid #fff;border-left:2px solid #7b7b7b;border-top:2px solid #7b7b7b;width:fit-content;text-align:center;}
#mine div{line-height:1px;white-space:nowrap;}
a{display:inline-block;box-sizing:border-box;width:16px;height:16px;line-height:16px;background-position:-16px -16px;background:url(data:image/png;base64,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);}
a.a0{background-position:-16px -16px;}
a.a1{background-position:0 0}
a.a2{background-position:-16px 0}
a.a3{background-position:-32px 0}
a.a4{background-position:-48px 0}
a.a5{background-position:-64px 0}
a.a6{background-position:-80px 0}
a.a7{background-position:-96px 0}
a.a8{background-position:-112px 0}
a.a11{background-position:0 -16px}
a.a12{background-position:-16px -16px}
a.a13{background-position:-32px -16px}
a.a14{background-position:-48px -16px}
a.a15{background-position:-64px -16px}
a.a16{background-position:-80px -16px}
a.a17{background-position:-96px -16px}
a.a18{background-position:-112px -16px}
a.nbtn{background-position:-1px -56px;height:24px;}
.head a{width:24px;height:23px;}
.nnum a{width:13px;background-position:-0px -32px}
.ntime a{width:13px;background-position:-0px -32px}
</style>
</head>
<body>
<div class="lvld" id="lvld">
  <select id="lvl"><option value="0">自定义</option><option value="1" selected> 初手级 </option><option value="2"> 入门级 </option><option value="3"> 高手级 </option><option value="4"> 困难级 </option></select> &nbsp;
  <span onclick='location.reload()'>行: </span><input type="number" id="row" value="9" readonly> &nbsp; 列: <input type="number" id="col" value="9" readonly>
</div>
<div class="head" id="head">
  <div class="nnum"><a id="nn0"></a><a id="nn1"></a><a id="nn2"></a></div><a class="nbtn" id="nbtn"></a><div class="ntime"><a id="nt0"></a><a id="nt1"></a><a id="nt2"></a></div>
</div>
<div class="main" id="main"><div id="mine"></div></div>

<script>
var $ = function(id){return document.getElementById(id)} //简化原生调用

//开启游戏
window.onload = function(){mine.init();}
document.oncontextmenu = function(e){return false;}
document.onmousemove = function(e){return false;} //阻止移动事件
$('mine').onmousedown = function(e){mine.onMouseDown(e);}
$('mine').onmouseup = function(e){mine.onMouseUp(e);}
$('nbtn').onclick = function(){this.style.backgroundPosition="-1px -56px";mine.init();}
$('lvl').onchange = function(){
  var row = 0, col = 0;$('row').setAttribute('readonly','');$('col').setAttribute('readonly','');
  if(this.value==0){$('row').removeAttribute('readonly');$('col').removeAttribute('readonly');}else
  if(this.value==1){row=9;col=9;}else
  if(this.value==2){row=16;col=16;}else
  if(this.value==3){row=16;col=30;}else
  if(this.value==4){row=30;col=30;}
  $('row').value = row;
  $('col').value = col;
  if(row>=9 && col>=9){mine.init();}
}

function shuffle(arr){ //数组随机打乱,抽牌法
  var temp = new Array(arr.length);
  for(var i = arr.length;i>0;i--){
    var temRandom =  Math.floor(Math.random()*i)
    temp[i-1] = arr[temRandom]; //随机抽一张
    arr[temRandom] = arr[i-1]; //最后那个补进来
  }
  return temp;
}

//游戏代码
var mine = {} //创建游戏对象
var xs = new Array(0,-1,-1,-1,0,1,1,1,0); //周围8格+中间
var ys = new Array(0,-1,0,1,1,1,0,-1,-1); //中,左上,上,右上,右,右下,下,左下,左
mine.gameover = 0; //0正在,1结束
mine.row = 16; //行数
mine.col = 16; //列数
mine.bombs = 40; //雷数
mine.map = new Array(); //游戏地图
mine.mids = new Array(); //左右键按下时的缓存
mine.btn = -1; //-1未按,0左键,1中键,2右键
mine.clk = 0; //点击次数
mine.timer = null;
mine.times = 0;
//初始游戏
mine.init = function(){
  this.gameover = 0;
  this.times = 0;
  this.clk = 0;
  this.mids = new Array();
  mine.row = parseInt($('row').value);
  mine.col = parseInt($('col').value);
  var bombval = 33; //雷值(由图片定的,第0行为数字行,第1行第3个为雷,所以值为13,隐藏+20)
  mine.bombs = parseInt(mine.row * mine.col * 0.2); //雷总数(20%)
  $('head').style.width = mine.col * 16 + 4 - 10 + 'px';
  $('mine').style.width = mine.col * 16 + 'px';
  $('main').style.width = mine.col * 16 + 4 + 'px';
  mine.showNums(mine.bombs, 'nn');
  mine.showNums(mine.times, 'nt');
  //计算出每行的随机雷数
  var allbombs = new Array(), sum = 0, more=1;
  for(var i=0; i<mine.row; i++){
    allbombs[i] = Math.floor(Math.random() * mine.col / 2); //0 ~ 0.5col
    sum += allbombs[i];
  }
  sum = mine.bombs - sum;
  if(sum<0){ more = -1; sum *= more;}
  var k=0, n=Math.ceil(sum / mine.row); //每行补差
  while(sum>0){
    if(k>=mine.row) k = 0;
    if(more==-1 && allbombs[k]<n){k++;continue;}//不够减,跳过
    if(sum < n){n = sum;}
    allbombs[k++] += more * n;
    sum -= n;
  }
  console.log(allbombs);
  allbombs = shuffle(allbombs); //补差完后随机打乱
  console.log(allbombs);
  //开始生成地图
  for(var i=0; i<mine.row; i++){
    mine.map[i] = new Array();
    for(var j=0; j<allbombs[i]; j++){ mine.map[i][j] = bombval; } //布雷
    for(var j=allbombs[i]; j<mine.col; j++){ mine.map[i][j] = 20; } //空白
    mine.map[i] = shuffle(mine.map[i]); //随机打乱
  }
  for(var i=0; i<mine.row; i++){ //计算每个格周围有多少雷
    for(var j=0; j<mine.col; j++){
      if(mine.map[i][j] == bombval){
        for(var k=1; k<9; k++){
          var x0 = i + xs[k];
          var y0 = j + ys[k];
          if(x0<0 || y0<0 || x0>=mine.row || y0>=mine.col) { continue; }
          if(mine.map[x0][y0]!=bombval) { mine.map[x0][y0]++; }
        }
      }
    }
  }
  mine.showMap();
}
//显示数字
mine.showNums = function(n, idt){
  var num = ('000'+n).replace(/[\d]?[-][\d]?([\d]{2})/,'-$1').substr(('000'+n).length-3).replace('0-','-0');
  for(var i=0; i<3; i++){
    n = num.charCodeAt(i)-48;
    if(n<0) n = 10; //'-'-48=45-48=-3
    $(idt+i).style.backgroundPosition = "-" + (n*13) + "px -32px";
  }
}
//整个显示界面
mine.showMap = function(){
  var str = '';
  for(var i=0; i<mine.row; i++){
    str += '<div>';
    for(var j=0; j<mine.col; j++){
      if(mine.map[i][j] >= 20){str += '<a class="a11" id="m_'+i+'_'+j+'"></a>';}
      else{str += '<a class="a'+mine.map[i][j]+'" id="m_'+i+'_'+j+'"></a>';}
    }
    str += '</div>';
  }
  $('mine').innerHTML = str;
}
//单个位置显示
mine.showMapA = function(x, y){
  var id = 'm_'+x+'_'+y;
  if(mine.map[x][y] >= 60){$(id).className = 'a17';}else
  if(mine.map[x][y] >= 40){$(id).className = 'a14';}else
  if(mine.map[x][y] >= 20){$(id).className = 'a11';}else
  $(id).className = 'a'+mine.map[x][y];
}
//左右键点击时预览周围几个的情况
mine.show2Click = function(id){
  var ids = id.split('_');
  var x = parseInt(ids[1]);
  var y = parseInt(ids[2]);
  if(mine.map[x][y]==0) return; //已经是空白格就不再操作了
  var x0, y0, i, v, mns=0; //记录标旗数
  for (i = 0; i < 9; i++) {
    x0 = x + xs[i];
    y0 = y + ys[i];
    if (x0 < 0 || x0 >= mine.row || y0 < 0 || y0 >= mine.col) { mine.mids[i] = 0; continue; } //不在雷区内
    if (mine.map[x0][y0]>=20 && mine.map[x0][y0]<40 || mine.map[x0][y0]>=60) {
      mine.mids[i] = x0 * 100000 + y0 * 1000 + mine.map[x0][y0];
      mine.map[x0][y0] = 0;
      mine.showMapA(x0, y0);
    } else {
      if(!(mine.mids[i]>=0)) { mine.mids[i] = 0; }
      if (mine.map[x0][y0]>=40 && mine.map[x0][y0]<60) { mns++; }
    }
  }
  if (mine.map[x][y] < 20 && mns > 0 && mns >= mine.map[x][y]) { //在数字上左右键,如果标的雷数够了就自动点开其它周围位置
    for (i = 0; i < 9; i++) {
      if (mine.mids[i] == 0) { continue; }
      x = parseInt(mine.mids[i] / 100000);
      y = parseInt(mine.mids[i] / 1000) % 100;
      v = mine.mids[i] % 1000;
      mine.map[x][y] = v; //先还原状态再点击
      mine.onLeftClick(x, y);
      mine.mids[i] = 0; //点过后,置0
    }
  }
}
//游戏结束
mine.showOver = function(){
  for(var i=0; i<mine.row; i++){
    for(var j=0; j<mine.col; j++){
      if(mine.map[i][j] >= 40 && mine.map[i][j] < 60 && mine.map[i][j]!=53) { mine.map[i][j] = 15; } //标错雷了
      if(mine.map[i][j] >= 20) { mine.map[i][j] %= 20; }
      mine.showMapA(i,j);
    }
  }
  $('nbtn').style.backgroundPosition = "-79px -56px";
  if(mine.timer!=null){clearInterval(mine.timer);mine.timer=null;}
}
//验证游戏是否过关
mine.chkSucc = function(){
  var sign = 0;
  for(var i=0; i<mine.row; i++){
    for(var j=0; j<mine.col; j++){
      if((mine.map[i][j]>=20) && (mine.map[i][j]%20)!=13){sign=1;break;}
    }
    if(sign==1)break;
  }
  if(sign==0){
    mine.gameover = 1; //游戏结束
    $('nbtn').style.backgroundPosition = "-105px -56px";
    if(mine.timer!=null){clearInterval(mine.timer);mine.timer=null;}
  }
}
//点击事件
mine.onMouseDown = function(e){
  e = e?e:event; //兼容IE
  e.target = e.target?e.target:e.srcElement; //兼容IE
  var cls = e.target.className;
  if(cls=='' || cls.indexOf('a')!=0){return;}
  if(mine.clk<0) mine.clk = 0; //异常处理,正常不会执行这步
  mine.clk++;
  mine.btn = e.srcElement && e.button==1 ? 0 : e.button; //兼容IE
  if(mine.clk == 2){mine.btn = 1;mine.show2Click(e.target.id);} //预览周围几个的情况
  return false; //e.preventDefault();
}
mine.onMouseUp = function(e){
  e = e?e:event;
  e.target = e.target?e.target:e.srcElement; //兼容IE
  var cls = e.target.className;
  if(cls=='' || cls.indexOf('a')!=0){return;}
  mine.clk--;
  if(mine.clk == 0 || mine.mids.join('')!='000000000'){
    mine.clk = 0;
    var ids = e.target.id.split('_');
    var x = parseInt(ids[1]);
    var y = parseInt(ids[2]);
    if(mine.btn == 0){ mine.onLeftClick(x, y); }else 
    if(mine.btn == 2){ mine.onRightClick(x,y);}else 
    if(mine.btn == 1){ mine.on2Click(x, y);}
  }
  return false; //e.preventDefault();
}
mine.onLeftClick = function(i, j){
  if(mine.gameover==1){ return; }
  if(mine.timer == null){mine.timer = setInterval(function(){mine.times++;mine.showNums(mine.times, 'nt');},1000);}
  if(mine.map[i][j]<20 || mine.map[i][j]>=40 && mine.map[i][j]<60) return; //40~60是标旗的位置,0~19是已显示的位置,都不可点
  mine.map[i][j] %= 20;
  if(mine.map[i][j] == 0){ //递归找出所有空白
    if(j>0) mine.onLeftClick(i, j-1); //左
    if(i>0) mine.onLeftClick(i-1, j); //上
    if(j<mine.col-1) mine.onLeftClick(i,j+1); //右
    if(i<mine.row-1) mine.onLeftClick(i+1,j); //下
    if(i>0 && j>0) mine.onLeftClick(i-1,j-1); //左上
    if(i<mine.row-1 && j>0) mine.onLeftClick(i+1,j-1); //左下
    if(i>0 && j<mine.col-1) mine.onLeftClick(i-1,j+1); //右上
    if(i<mine.row-1 && j<mine.col-1) mine.onLeftClick(i+1,j+1); //右下
  }else if(mine.map[i][j] == 13){ 
    mine.gameover = 1; //游戏结束
    mine.map[i][j] = 16;//点中雷了
    mine.showOver();
    return;
  }
  mine.showMapA(i, j);
  mine.chkSucc();
}
mine.onRightClick = function(x, y){
  if(mine.map[x][y]>=60){mine.map[x][y] -= 40;}else
  if(mine.map[x][y]>=20 && mine.map[x][y]<60){
    mine.map[x][y] += 20;
    if(mine.map[x][y] >= 60){mine.bombs++; } else {mine.bombs--;} //修改雷数
    mine.showNums(mine.bombs, 'nn');
  }
  mine.showMapA(x, y);
}
mine.on2Click = function(x, y){
  for (i = 0; i < 9; i++) {
    if (mine.mids[i] == 0) { continue; }
    x = parseInt(mine.mids[i] / 100000);
    y = parseInt(mine.mids[i] / 1000) % 100;
    v = mine.mids[i] % 1000;
    mine.map[x][y] = v; //预览后还原状态
    mine.showMapA(x, y);
    mine.mids[i] = 0; //点过后,置0
  }
}
</script>
</body>
</html>

还有些小bug,整体玩着没什么问题

posted @ 2024-04-17 15:25  hankerstudio  阅读(59)  评论(0)    收藏  举报