本帖最后由 Fullmoonbaka 于 2021-7-28 21:31 编辑
如题所示,
本人使用javascript实现了一个扫雷游戏,基本涵盖了windows7扫雷的所有基本功能
扫雷
游戏程序主要技术点:
1.【生成炸弹】[createBomb方法]
在第一次点击地板的时候,需要确保点击的地方不是炸弹(防止第一次点击就输),因此炸弹生成在第一次点击之后。
在第一次点击时先获取到点击落点周围9格的id,
在所有地板id的列表中排除这些id,
然后在剩下的id中随机抽取一定量的地板id生成炸弹
2.【揭开地板】[show方法](个人觉得是扫雷的核心规则)
在揭开地板时先判断是否为炸弹,为炸弹就直接判输
如果不为炸弹,获取落点地板周围9格的地板id逐个判断是否为炸弹,
如果有炸弹就将落点地板标上炸弹数且就此结束
如果没有炸弹就递归遍历落点地板周围一圈8个地板重复执行此操作
(此操作会形成一揭一大片的情况,当周围的8个地板都没有炸弹时一直往外扩散遍历,直至检测到炸弹以后将地板填入数字后停止。)
这一步要注意将地板的状态及时更新成揭开状态,不然会无限递归导致卡死
3.【双击地板】
双击地板操作需要双击已经揭开、带有数字并且附近插有旗子的地板才有效。
当落点周围8格有雷且插上旗子数等于雷的数量的时候,会自动揭开其他格子。(据我多年扫雷经验,此规则应该和win7版扫雷的左键+右键功能一致)
如果插旗错误(对应的旗子没有插在雷上而是插在安全格子上)会直接失败,如果插旗每个都插在雷上,会自动揭开周围8格其他所有未揭开且未插旗的格子。
本操作规则遵守揭开地板规则,遇到空地板时候会递归揭开周围的格子。
想要运行的可以看这个 https://www.52pojie.cn/thread-1484299-1-1.html
本人编程时间尚短、技术力有限,本文章有错误望大佬一一指正。
[JavaScript] 纯文本查看 复制代码 class sweep{
constructor(item) {
this.item = document.querySelector(item)
this.bombNum = 99 // 炸弹数量(默认)
this.allBlock = [] // 全部地块DOM列表
this.blockList = [] // 无炸弹地块列表
this.bombList = [] // 有炸弹地块列表
this.showList = [] // 揭开地块的列表
this.blockObj = [] // 地块对象
this.flagList = new Set() // 旗子列表
this.begin = false // 开始
this.timer = null // 计时器
this.init()
}
// 启动器
init () {
this.conTextMenu()
this.createFloor()
this.clickBlock()
}
// 生成地形
createFloor () {
let frg = document.createDocumentFragment()
for(let j = 0; j < 450; j++){
let div = document.createElement('div')
this.blockList.push(j)
this.blockObj.push({
id: j,
x: j%30,
y: parseInt(j/30)
})
div.dataset.id = j
div.className = 'block'
this.allBlock.push(div)
frg.appendChild(div)
}
this.item.appendChild(frg)
}
// 生成炸弹
createBomb (arr) {
this.bombNum = parseInt(document.querySelector('#num').value)
arr.forEach((item) => {
this.blockList.splice(this.blockList.indexOf(item), 1)
})
for(let i = 0; i < this.bombNum; i++){
this.bombList.push(this.blockList.splice(parseInt(Math.random() * this.blockList.length), 1)[0])
}
arr.forEach((item) => {
this.blockList.push(item)
})
}
// 点击事件
clickBlock () {
// 单击
this.item.addEventListener('click', (e) => {
e = e || window.event
let target = e.target || e.srcElement
if(!this.begin){
this.begin = true
this.setTime()
let arr = this.getBlock(target.dataset.id - 0)
arr.push(target.dataset.id - 0)
this.createBomb(arr)
}
if(target.className === 'block'){
this.show(target)
if(!this.blockList.some((item) => {return this.allBlock[item].className !== 'show'})){
this.victory()
return
}
}
})
// 选中事件
this.item.addEventListener('selectstart', (e) => {
e = e || window.event
e.preventDefault()
})
// 双击事件
this.item.addEventListener('dblclick', (e) => {
e = e || window.event
let target = e.target || e.srcElement
if(target.className === 'show'){
let num = target.dataset.id - 0
let arr = this.getBlock(num)
if(arr.some((item) => { return this.bombList.indexOf(item) !== -1 && this.allBlock[item].className !== 'bomb' })){
if(target.innerText - 0 && arr.filter((item) => { return this.allBlock[item].className === 'bomb'}).length === target.innerText - 0){
this.fail()
}
} else {
arr.forEach((item) => {
if(this.allBlock[item].className === 'block'){
this.show(this.allBlock[item])
}
})
if(this.showList.length === this.blockList.length) {
this.victory()
}
}
}
})
}
// 判断方块四周
show (item) {
let sum = 0
let num = parseInt(item.dataset.id)
if(this.bombList.some((i) => { return i === parseInt(item.dataset.id) })){
this.fail()
} else {
this.showList.push(parseInt(item.dataset.id))
let arr = this.getBlock(num)
arr.forEach((one) => {
if(this.bombList.some((i) => {return i === one})){
sum ++
}
})
item.className = 'show'
if(sum){
item.innerText = sum
} else { // 追加 show
arr.forEach((num) => {
if(this.allBlock[num].className === 'block'){
this.show(this.allBlock[num])
}
})
}
}
}
// 获取到四周的方块
getBlock(num) {
let arr = []
if(num === 0){
arr = [num + 1, num + 30, num + 31]
} else if (num === 29){
arr = [num - 1, num + 30, num + 29]
} else if (num === 420){
arr = [num + 1, num - 30, num - 29]
} else if (num === 449){
arr = [num - 1, num - 30, num - 31]
} else if (num < 29){
arr = [num + 1, num - 1, num + 30, num + 31, num + 29]
} else if (num > 420){
arr = [num + 1, num - 1, num - 30, num - 29, num - 31]
} else if (num % 30 === 0){
arr = [num + 1, num - 30, num - 29, num + 30, num + 31]
} else if (num % 30 === 29){
arr = [num - 1, num - 30, num - 31, num + 30, num + 29]
} else {
arr = [num + 1, num - 1, num + 29, num + 30, num + 31, num - 30, num - 31, num - 29]
}
return arr
}
// 计时器
setTime(){
let t = document.getElementById('time')
let s = t.innerText - 0
this.timer = setInterval(() => {
s ++
t.innerText = s
}, 1000)
}
// 失败
fail (){
let bool = confirm('踩到雷,失败了!')
if(bool){
window.location.reload()
}
}
// 成功
victory (){
alert('成功了!成功了!带成功!')
clearInterval(this.timer)
alert('你的生命减少了 ' + document.querySelector('#time').innerText + ' 秒')
}
// 右键事件
conTextMenu () {
this.item.addEventListener('contextmenu', (e) => {
e = e || window.event
let target = e.target || e.srcElement
try {e.preventDefault()} catch (err) {e.returnValue = false}
if(target.className === 'block'){
target.className = 'bomb'
this.flagList.add(parseInt(target.dataset.id))
} else if (target.className === 'bomb'){
target.className = 'block'
this.flagList.delete(parseInt(target.dataset.id))
}
if(this.flagList.size === this.bombNum) {
let flagAllBomb = this.bombList.every(item => {
return this.flagList.has(item)
})
if(flagAllBomb) {
this.victory()
}
}
})
}
}
let first = new sweep('#box')
|