javaScript扫雷


一、准备工作

1.图片

需要找三张图片 旗子的图片 炸弹的图片 爆炸的图片

2.html

html文件夹新建一个html文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>扫雷游戏</title>
  <link rel="stylesheet" href="../css/index.css">
</head>
<body>
  <!-- 游戏最外层容器 -->
  <div class="container">
    <!-- 游戏标题 -->
    <h1>扫雷</h1>
    <!-- 游戏难度 -->
    <div class="level">
      <button class="active">初级</button>
      <button>中级</button>
      <button>高级</button>
    </div>
    <!-- 总雷数和插旗树 -->
    <div class="info">总雷数:<span class="mineNum">10</span></div>
    <div class="info">插旗数:<span class="flagNum">0</span></div>
    <!-- 扫雷区域  通过js动态添加-->
    <div class="mineArea"></div>
  </div>
   <script src="../js/config.js"></script>
  <script src="../js/index.js"></script>
</body>
</html>

在这里插入图片描述
目前写好html后的样式

2.css

css文件夹下新建一个css文件

  1. 初始化
    • 内外边框为0
    • 改变盒子模型为边框盒子
    *{
          
          
      /* 初始化内外边距 */
      margin: 0;
      padding: 0;
      /* 改成边框盒 会把padding什么的算进去 */
      box-sizing: border-box;
    }
    
  2. 标题:
    • 文字居中
    • 字体大小46px
    • 粗100
    • 上下边距30 居中 左右20 居中
    /* 标题 */
    h1{
          
          
      text-align: center;
      font-size:46px;
      font-weight: 100;
      margin:30px auto 20px auto
    }
    
  3. 游戏难度选择
    • 字体居中
    • 下边距10px
    /* 游戏难度选择 */
    .level{
          
          
      text-align: center;
      margin-bottom: 10px;
    }
    
  4. 游戏难度选择按钮:
    • 内边距上下5px 左右15px
    • 背景颜色#02a4ad
    • 字体颜色白色
    • 取消轮廓(outline)
    • 取消外边框(border)
    • 圆角3px
    • 上下边距0 左右边距5px
    • 移动到按钮时鼠标改为手指
    .level button{
          
          
      padding: 5px 15px;
      background: #02a4ad;
      color: white;
      outline: none;
      border:none;
      border-radius: 3px;
      cursor:pointer
    }
    
  5. 当前按钮样式
    • 背景颜色 #00abff
    .level button.active{
          
          
      background-color: #00abff;
    }
    
  6. 提示信息相关样式
    • 文字居中
    • 粗200
    • 上下10px 左右居中
  7. 完成后的样式截图:
    在这里插入图片描述

3.js

新建一个js文件夹

  1. 创建index.js 写逻辑
  2. 创建config.js写配置

二、初始化数据

1. 配置文件

由于整个游戏是可以选择难度的,所以我们需要一些配置信息,我们单独创建了一个config.js,代码如下

// 游戏配置文件

//不同级别的配置
var config = {
    
    
  easy: {
    
    
    row: 10,
    col: 10,
    mineNum: 10,
  }, //简单模式
  noraml: {
    
    
    row: 15,
    col: 15,
    mineNum: 30,
  }, //普通模式
  hard: {
    
    
    row: 20,
    col: 20,
    mineNum: 60,
  }, //困难模式
  custom: {
    
    
    row: '',
    col: '',
    mineNum: '',
  }, //自定义模式
}
// 当前游戏难度 一开始默认为简单模式
var curLevel=config.easy

2.工具文件

新建一个tool.js

// 工具函数

// 1. 获取元素
function $(selector){
    
    
  return document.querySelector(selector)
}
//2.获取所有元素
function $$(selector){
    
    
  return document.querySelectorAll(selector)
}

3.逻辑文件

1.main函数

// 游戏主要逻辑

// 主函数
function main(){
    
    
  //1.进行游戏的初始化
  init()
  //2.绑定事件
  bindEvent()
}
main()

// 初始化
function init(){
    
    
}
//绑定事件
function bindEvent(){
    
    
}

2.init函数

  1. 思考:初始化要做什么事情?
    - 随机生成所选配置对应数量的雷
//index。js最上面的地方写
//用于存储生成的地雷的数组
var mineArray = null
//雷区的容器
var mineArea = $(".mineArea")
// 初始化
function init() {
    
    
  //1.随机生成所选配置对应的数量的雷
  mineArray = initMine()
  //2.根据雷的数量,生成对应的表格
  var table = document.createElement('table')
  //初始化格子的下标
  var index = 0

  for (var i = 0; i < curLevel.row; i++) {
    
    
    var tr = document.createElement('tr')
    for (var j = 0; j < curLevel.col; j++) {
    
    
      var td = document.createElement('td')
      tr.appendChild(td)
    }
    table.appendChild(tr)
  }
  mineArea.appendChild(table);
}

1.随机生成雷

// 随机生成所选配置对应的数量的雷
function initMine() {
    
    
  //1.创建一个数组,用于存储生成的地雷,生成对应长度的数组
  var arr = new Array(curLevel.row * curLevel.col)
  //2.往这个数组里面填充值
  for (var i = 0; i < arr.length; i++) {
    
    
    arr[i] = i
  }
  //3.接下来打乱这个数组
  arr.sort(() => 0.5 - Math.random())
  //4.只保留对应雷数量的数组长度
  return arr.slice(0, curLevel.mineNum)
}

2.css添加

/* 表格相关样式 */
table{
    
    
  background:#929196;
  margin: 0 auto;
  border-spacing:1px;
}
table td{
    
    
  width: 24px;
  height: 24px;
  background:#ccc;
  border: 2px solid;
  border-color: #fff #a1a1a1 #a1a1a1 #fff;
  text-align:center ;
  line-height: 24px;
  font-weight: bold;
  cursor:pointer
}

三、完整代码

1.html


<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>扫雷游戏</title>
  <link rel="stylesheet" href="../css/index.css">
</head>

<body>
  <!-- 游戏最外层容器 -->
  <div class="container">
    <!-- 游戏标题 -->
    <h1>扫雷</h1>
    <!-- 游戏难度 -->
    <div class="level">
      <button class="active">初级</button>
      <button>中级</button>
      <button>高级</button>
      <button>自定义</button>
    </div>
    <!-- 总雷数和插旗树 -->
    <div class="info">总雷数:<span class="mineNum">10</span></div>
    <div class="info">插旗数:<span class="flagNum">0</span></div>
    <!-- 扫雷区域  通过js动态添加-->
    <div class="mineArea"></div>
  </div>
  <script src="../js/tool.js"></script>
  <script src="../js/config.js"></script>
  <script src="../js/index.js"></script>
</body>

</html>

2.js

config.js

// 游戏配置文件

//不同级别的配置
var config = {
    
    
  easy: {
    
    
    row: 10,
    col: 10,
    mineNum: 10,
  }, //简单模式
  normal: {
    
    
    row: 15,
    col: 15,
    mineNum: 30,
  }, //普通模式
  hard: {
    
    
    row: 20,
    col: 20,
    mineNum: 60,
  }, //困难模式
  custom: {
    
    
    row: '',
    col: '',
    mineNum: '',
  }, //自定义模式
}
// 当前游戏难度 一开始默认为简单模式
var curLevel=config.easy

index.js

// 游戏主要逻辑
//用于存储生成的地雷的数组
var mineArray = null
//雷区的容器
var mineArea = $('.mineArea')
//用于存储整张地图每个格子额外的一些信息
var tableData = []
//用于存储用户插旗的DOM元素
var flagArray = []
var mineNumber = $('.mineNum')
// 主函数
var btns = document.getElementsByTagName('button')
function main() {
    
    
  //1.进行游戏的初始化
  init()
  //2.绑定事件
  bindEvent()
}
main()
function clearScene(){
    
    
  mineArea.innerHTML = ''
  flagArray=[];
  $('.flagNum').innerHTML=0;//重置插旗
   mineNumber.innerHTML=curLevel.mineNum
}
// 初始化
function init() {
    
    
  //清空场景,或者叫做重置信息
  clearScene();
  //1.随机生成所选配置对应的数量的雷
  mineArray = initMine()
  // console.log(mineArray)
  //2.根据雷的数量,生成对应的表格
  var table = document.createElement('table')
  //初始化格子的下标
  var index = 0

  for (var i = 0; i < curLevel.row; i++) {
    
    
    var tr = document.createElement('tr')
    tableData[i] = []
    for (var j = 0; j < curLevel.col; j++) {
    
    
      var td = document.createElement('td')
      var div = document.createElement('div')
      //每一个小格子都会对应一个js对象
      //该对象存储了额外的一些信息
      tableData[i][j] = {
    
    
        row: i, //该格子的行
        col: j, //该格子的列
        type: 'number', //格子的属性 数字number  雷mine
        value: 0, //周围雷的数量
        index, //格子的下标
        checked: false, //是否被检验过,后面会用到
      }
      //为每一个div添加一个下标,方便用户点击的时候获取相应格子的下标
      div.dataset.id = index
      //当前格子是可以插旗的
      // div.classList.add('canFlag')
      div.setAttribute('class', 'canFlag')
      //查看当前格子的下标是否在雷的数组里面
      if (mineArray.includes(tableData[i][j].index)) {
    
    
        tableData[i][j].type = 'mine'
        div.classList.add('mine')
      }
      td.appendChild(div)
      tr.appendChild(td)
      //下标自增
      index++
    }
    table.appendChild(tr)
  }
  mineArea.appendChild(table)
  // console.log(table)
  // console.log(tableData)
  mineArea.onmousedown = function (e) {
    
    
    if (e.button === 0) {
    
    
      //左键
      //进行区域搜索操作
      searchArea(e.target)
    }
    if (e.button === 2) {
    
    
      //右键
      //插旗子
      flag(e.target)
    }
  }
}

// 随机生成所选配置对应的数量的雷
function initMine() {
    
    
  //1.创建一个数组,用于存储生成的地雷,生成对应长度的数组
  var arr = new Array(curLevel.row * curLevel.col)
  //2.往这个数组里面填充值
  for (var i = 0; i < arr.length; i++) {
    
    
    arr[i] = i
  }
  //3.接下来打乱这个数组
  arr.sort(() => 0.5 - Math.random())
  //4.只保留对应雷数量的数组长度
  return arr.slice(0, curLevel.mineNum)
}
//显示答案
function showAnswer() {
    
    
  var isAllRight = true
  //1.显示所有的雷
  //2.有些雷可能插了旗子 需要判断插旗是否正确
  //正确添加绿色背景
  //错误添加红色背景
  var mineArr = $$('td>div.mine')
  for (var i = 0; i < mineArr.length; i++) {
    
    
    mineArr[i].style.opacity = 1
  }
  //遍历用户的插旗
  for (var i = 0; i < flagArray.length; i++) {
    
    
    if (flagArray[i].classList.contains('mine')) {
    
    
      //说明插旗插队了
      flagArray[i].classList.add('right')
    } else {
    
    
      flagArray[i].classList.add('error')
      isAllRight = false
    }
  }
  if (!isAllRight || flagArray.length !== curLevel.mineNum) {
    
    
    gameOver(false)
  }
  //取消事件
  mineArea.onmousedown = null
}
//找到对应DOM在tableData里面的js对象
function getTableItem(cell) {
    
    
  var index = cell.dataset.id
  // console.log(index)
  // console.log(tableData)
  var flatTableData = tableData.flat()
  return flatTableData.filter((item) => item.index == index)[0]
  // console.log(i)
}
//返回该对象对应四周的边界

function getBound(obj) {
    
    
  // console.log(obj)
  //确定上下边界
  var rowTop = obj.row - 1 < 0 ? 0 : obj.row - 1
  var rowBottom = obj.row + 1 === curLevel.row ? curLevel.row - 1 : obj.row + 1
  //确定左右边界
  var colLeft = obj.col - 1 < 0 ? 0 : obj.col - 1
  var colRight = obj.col + 1 === curLevel.col ? curLevel.col - 1 : obj.col + 1
  return {
    
    
    rowTop,
    rowBottom,
    colLeft,
    colRight,
  }
}
//返回周围一圈雷的数量
function findMineNum(obj) {
    
    
  var count = 0 //地雷计数器
  var {
    
     rowTop, rowBottom, colLeft, colRight } = getBound(obj)
  // console.log(getBound(obj))
  for (var i = rowTop; i <= rowBottom; i++) {
    
    
    for (var j = colLeft; j <= colRight; j++) {
    
    
      if (tableData[i][j].type === 'mine') {
    
    
        count++
      }
    }
  }
  return count
}
//根据tableData种的js对象返回对应的div
function getDOM(obj) {
    
    
  var divArray = $$('td>div')
  //返回对应下标的div
  return divArray[obj.index]
}
//搜索该单元格周围的九宫格区域
function getAround(cell) {
    
    
  if (!cell.classList.contains('flag')) {
    
    
    //当前的单元格没有被插旗,我们才进行以下的操作
    //搜索该单元格周围的九宫格区域
    cell.parentNode.style.border = 'none'

    if (cell.getAttribute('class') !== 'mineArea') {
    
    
      cell.parentNode.style.background = '#d9d9d9'
    }
    cell.classList.remove('canFlag')
    //1.获取到该DOM元素在tableData里面所对应的对象
    var tableItem = getTableItem(cell)
    if (!tableItem) {
    
    
      return
    }
    //代表当前单元格已经被核对过了
    tableItem.checked = true
    //得到了对应DOM对象所对应的js对象
    //那我们就可以查看周围一圈是否有雷
    var mineNum = findMineNum(tableItem)
    // console.log(mineNum)
    if (!mineNum) {
    
    
      //进入此if周围没有雷
      //需要继续搜素
      var {
    
     rowTop, rowBottom, colLeft, colRight } = getBound(tableItem)
      for (var i = rowTop; i <= rowBottom; i++) {
    
    
        for (var j = colLeft; j <= colRight; j++) {
    
    
          if (!tableData[i][j].checked) {
    
    
            getAround(getDOM(tableData[i][j]))
          }
        }
      }
    } else {
    
    
      //当前的格子要显示对应雷的数量
      var cl = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']
      cell.classList.add(cl[mineNum])
      cell.innerHTML = mineNum
    }
  }
}
//鼠标左键点击
function searchArea(cell) {
    
    
  //1.当前单元格子是雷 游戏结束
  if (cell.classList.contains('mine')) {
    
    
    //游戏结束
    cell.classList.add('boom')
    showAnswer() //显示答案
    return
    // gameOver()
  }
  //2.当前单元格不是雷
  //2.1旁段周围一圈有没有雷 如果有雷,显示雷的数量
  //2.2旁段周围一圈没有雷,显示空白,然后继续搜索周围一圈
  getAround(cell)
}
//判断玩家插旗是否全部正确
function isWin() {
    
    
  for (var i = 0; i < flagArray.length; i++) {
    
    
    if (!flagArray[i].classList.contains('mine')) {
    
    
      return false
    }
  }
  return true
}
//判断是否获胜
function gameOver(isWin) {
    
    
  //分为两种情况 胜利和失败
  var mess = ''
  if (isWin) {
    
    
    mess = '游戏胜利,小呆找出了所有雷~'
  } else {
    
    
    mess = '粗心了哦小呆!'
  }
  window.alert(mess)
}
//插旗
function flag(cell) {
    
    
  if (cell.classList.contains('canFlag')) {
    
    
    //只有点击的dom元素包含canFlag这个类名才可以添加旗子
    if (!flagArray.includes(cell)) {
    
    
      //进行插旗操作
      flagArray.push(cell)
      cell.classList.add('flag')
      //还需要进行插旗数的判断
      if (flagArray.length === curLevel.mineNum) {
    
    
        //判断玩家是否胜利
        if (isWin()) {
    
    
          gameOver(true)
        }
        //无论游戏胜利还是失败我们都应该进入showAnswer 显示最终答案
        showAnswer()
      }
    } else {
    
    
      //进行取消插旗操作
      var index = flagArray.indexOf(cell)
      flagArray.splice(index, 1)
      cell.classList.remove('flag')
    }
    $('.flagNum').innerHTML = flagArray.length
  }
}
//绑定事件
function bindEvent() {
    
    
  //阻止默认的鼠标右键行为
  mineArea.oncontextmenu = function (e) {
    
    
    e.preventDefault()
  }
  //1.点击格子插旗子

  //游戏难度选择
  $('.level').onclick = function (e) {
    
    
    for (var i = 0; i < btns.length; i++) {
    
    
      btns[i].classList.remove('active')
    }
    e.target.classList.add('active')
    switch (e.target.innerHTML) {
    
    
      case '初级': {
    
    
        curLevel = config.easy
        break
      }
      case '中级': {
    
    
        curLevel = config.normal
        break
      }
      case '高级': {
    
    
        curLevel = config.hard
        break
      }
      case '自定义': {
    
    
      }
    }
    init()
  }
}

tool.js


// 工具函数

// 1. 获取元素
function $(selector) {
    
    
  return document.querySelector(selector)
}
//2.获取所有元素
function $$(selector) {
    
    
  return document.querySelectorAll(selector)
}

3.css

*{
    
    
  /* 初始化内外边距 */
  margin: 0;
  padding: 0;
  /* 改成边框盒 会把padding什么的算进去 */
  box-sizing: border-box;
}
/* .container{
  border: #f5b0d0 solid ;
} */
/* 标题 */
h1{
    
    
  text-align: center;
  font-size:46px;
  font-weight: 100;
  margin:30px auto 20px auto
}
/* 游戏难度选择 */
.level{
    
    
  text-align: center;
  margin-bottom: 10px;
}
.level button{
    
    
  padding: 5px 15px;
  background: #02a4ad;
  color: white;
  outline: none;
  border:none;
  border-radius: 3px;
  cursor:pointer
}
.level button.active{
    
    
  background-color: #00abff;
}
.info{
    
    
  text-align: center;
  margin:10px auto;
  font-weight: 200;
}

/* 表格相关样式 */
table{
    
    
  background:#929196;
  margin: 0 auto;
  border-spacing:1px;
}
table td{
    
    
  width: 24px;
  height: 24px;
  background:rgb(245, 213, 228);
  border: 2px solid;
  border-color: rgb(252, 215, 233) #f5b0d0 #f5b0d0 rgb(252, 215, 233);
  text-align:center ;
  line-height: 24px;
  font-weight: bold;
  cursor:pointer
}
td>div{
    
    
  height: 100%;
  width: 100%;
}
/* 显示地雷 */
td>div.mine{
    
    
  background: rgb(245, 213, 228) url("../img/炸弹.png") center/cover no-repeat;
  opacity: 0;
}
/* 显示小旗子 */
td>div.flag{
    
    
  background: rgb(245, 213, 228) url("../img/旗子.png") center/cover no-repeat;
  opacity: 1;
}
td>div.error{
    
    
  background-color:red
  /* background: rgb(245, 213, 228) url("../img/爆炸.png") center/cover no-repeat; */
}
td>div.boom{
    
    
  background:  url("../img/爆炸.png") center/cover no-repeat;
}
td>div.right{
    
    
  background-color: green;
}
td>div.zero{
    
    
  border-color:#d9d9d9;
  background-color:#d9d9d9;
}
td>div.one{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:rgb(136, 173, 236);
}
td>div.two{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:#9BCD9B;
}
td>div.three{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:#ee8797;
}
td>div.four{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:red;
}
td>div.five{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:orange;
}
td>div.six{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:yellow;
}
td>div.seven{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:gray;
}
td>div.eight{
    
    
  border-color:#d9d9d9;
  background:#d9d9d9;
  color:black;
}

猜你喜欢

转载自blog.csdn.net/m0_58065010/article/details/128078494