试图利用Vue3+Vuex+TS利用一维数组来实现前端的简单扫雷。在判断地雷周围雷区拥有的地雷数量时,发现本来应该是计算后输出number数组的结果里面居然出现了string。
callMatrix(72,9)
预期:[62, 63, 71, 80, 81]
结果:[62, 63, 71, 728, '729']
VUE部分 设置游戏
<template>
<div>
雷区格数:{{mineField.fieldHeight*mineField.fieldWeight}}
<p>雷区长度:<input placeholder="请输入雷区长宽,默认为4" v-model="mineField.fieldHeight"/></p>
<p>雷区宽度:<input placeholder="请输入雷区长宽,默认为4" v-model="mineField.fieldWeight"/></p>
<p>地雷数量:<input placeholder="请输入地雷数量,默认为1" v-model="mineField.mineNumber"/></p>
<button @click="setDefaultValue">默认值</button><button @click="setStoreInfo">确认</button>
</div>
</template>
<script lang="ts">
import {useStore} from "vuex"
import {ref} from 'vue'
export default {
name: "gameSetting",
setup(){
const store=useStore()
//雷区属性
const mineField=ref({
fieldHeight:10,
fieldWeight:10,
mineNumber:1
})
function setDefaultValue() {
mineField.value.fieldHeight=4
mineField.value.fieldWeight=4
mineField.value.mineNumber=1
}
function setStoreInfo() {
if (Number(mineField.value.fieldHeight)){
if (Number(mineField.value.fieldWeight)){
if (Number(mineField.value.mineNumber)&&Number(mineField.value.mineNumber)){
store.commit('setFieldHeight',mineField.value.fieldHeight)
store.commit("setFieldWeight",mineField.value.fieldWeight)
store.commit('setMineNumber',mineField.value.mineNumber)
store.commit('setIs',!store.state.is)
}else {
alert(`无效雷数,合理范围[1-${Number(mineField.value.fieldHeight)*Number(mineField.value.fieldWeight)-1}]`)
}
}else {
alert('无效宽度')
}
}else {
alert('无效长度')
}
}
return{
store,mineField,setDefaultValue,setStoreInfo
}
}
}
</script>
VUE部分 游戏区域
<template>
<div class="top">
玩家名:{{store.state.playerName}}
</div>
<div class="center">
<p>雷区地块:{{divNumber}}</p>
<p>翻开区块:{{openedDivNumber}}</p>
<p>预测地雷:{{minePrediction}}</p>
<div class="chessboardCanvas">
<div
v-for="item in divNumber"
:key="item"
:id="item"
class="noCheckBox"
@mousedown="clickThings"
>
{{item}}
</div>
</div>
</div>
<div class="footer">
<button @click="resetThisGame">重置本局</button>
<button>重新设置</button>
</div>
</template>
<script lang="ts">
import {useStore} from "vuex"
import {watch,computed,ref,nextTick} from 'vue'
export default {
name: "gameArea",
setup(){
const store=useStore()
//存放雷区地块数量
const divNumber=ref<number>(0)
//翻开区块数量
let openedDivNumber=ref<number>(0)
//存放雷区属性
let fieldHeight:number
let fieldWeight:number
//存放地雷数组ID
let saveMines:number[]=[]
//存放有周围地雷数量的DIV块
let saveHaveMineNumber:{id:number,haveMath:number}[]=[]
//暂时存放DIV的element类
let element:HTMLDivElement
//记录预测地雷数量
let minePrediction=ref<number>(0)
//画布的还原以及初始化样式
function fatherStyle() {
element=document.getElementsByClassName('chessboardCanvas')[0] as HTMLDivElement
element.style.display='grid'
element.style.width=`${40*fieldWeight+fieldWeight+1}`
element.style.height=`${40*fieldHeight+fieldHeight+1}`
element.style.gridTemplateColumns=`repeat(${fieldWeight},40px)`
element.style.gridTemplateRows=`repeat(${fieldHeight},40px)`
element.style.paddingBottom='2px'
}
function mineInitialization(mineNumber:number) {
openedDivNumber.value=0
//父元素设置样式
fatherStyle()
//存放地雷数组置为空
saveMines=[]
//临时存放minesID
let mineSet=new Set()
while(mineSet.size<mineNumber){
mineSet.add(Math.floor(Math.random()*divNumber.value+1))
}
saveMines=Array.from(mineSet) as number[]
havemineMath(fieldWeight)
console.log('地雷数组')
console.log(saveMines)
}
//监测用户是否确定改动
watch(computed(()=>{return store.state.is}),()=>{
divNumber.value=store.getters.mineFieldSize
fieldWeight=store.state.fieldWeight
fieldHeight=store.state.fieldHeight
mineInitialization(store.state.mineNumber)
// console.log(store.state.fieldWeight)
// console.log(`测试代码${callMatrix(saveMines[0],Number(store.state.fieldWeight))}`)
})
//重置本局
function resetThisGame() {
mineInitialization(store.state.mineNumber)
for (let item of document.getElementsByClassName('noCheckBox')){
(item as HTMLDivElement).style.backgroundColor='#999999',
(item as HTMLDivElement).textContent=item.id,
(item as HTMLDivElement).style.pointerEvents='auto'
}
alreadyMath=saveMines
}
//不同点击来触发事件
function clickThings(event:any) {
element=event.path[0] as HTMLDivElement
//禁用全局菜单
document.oncontextmenu=()=>{
return false
}
switch (event.button) {
case 0:
if (saveMines.includes(Number(element.id))){
element.innerText=""
//触发地雷 红色->等待1s->弹出提示框,进入下一轮
element.style.backgroundColor='red'
setTimeout(function () {
alert('你触发了地雷,游戏结束,即将回退')
resetThisGame()
},100)
}else {
recursizeLookup(Number(element.id),fieldWeight)
setTimeout(function () {
isWin()
},200)
}
break
case 1:
if (element.style.backgroundColor==="rgb(153, 255, 204)"){
element.style.backgroundColor='#999999'
element.innerText=element.id
alreadyMath=alreadyMath.filter(item=>{
return item!==Number(element.id)
})
minePrediction.value--
}else{
element.style.backgroundColor='#99FFCC'
element.innerText='预测地雷'
alreadyMath.push(Number(element.id))
minePrediction.value++
}
break
default:
break
}
}
//点击遍历周围是否存在地雷并赋值
//3*3规律
// y-(x+1) y-x y-(x-1)
// y-(x-2) y y+(x-2)
// y+(X-1) y+x y+x+1
function callMatrix(id:number,size:number):number[]{
//对数组边界的判断来决定返回值
if((id-1)%size===0){
//DIV左边界
return [id-size,id-size+1,id+1,id+size,id+size+1]
}else if (id%size===0){
//DIV右边界
return [id-size-1,id-size,id-1,id+size-1,id+size]
}else {
//非DIV左右边界
return [id-size-1,id-size,id-size+1,id-1,id+1,id+size-1,id+size,id+size+1]
}
}
//存放地雷周边区域DIV相关信息
function havemineMath(size:number) {
saveHaveMineNumber=[]
for (let i of saveMines){
let matrix=callMatrix(i,fieldWeight)
//**
//**应该输出的是number[] 但是在数组中输出的有string
//**
console.log(matrix)
console.log(typeof matrix[matrix.length-1])
//**
//**
//**
for (let j of matrix){
if (j>0&&j<=divNumber.value&&!saveMines.includes(j)){
saveHaveMineNumber.push({
id:j,
haveMath:1
})
}
}
}
// 如果存在相同元素则只留下唯一一个,同时改变haveMath,过滤操作
for (let i = 0;i<saveHaveMineNumber.length;i++){
let count=1
for (let j = i+1;j<saveHaveMineNumber.length;j++){
if (saveHaveMineNumber[i].id===saveHaveMineNumber[j].id){
saveHaveMineNumber[j].id=0
count++
break
}
}
saveHaveMineNumber[i].haveMath=count
}
saveHaveMineNumber=saveHaveMineNumber.filter((item)=>{
return item.id!=0
})
console.log(saveHaveMineNumber)
}
//以点击处为中心,递归寻找3*3,翻找到有地雷数量的区域就停止
//存储已经递归的ID以及改变过颜色的ID,避免重复递归
let alreadyMath:number[]=saveMines
function recursizeLookup(checkID:number,size:number) {
element.style.backgroundColor='#FFFFFF'
element.style.pointerEvents='none'
if (checkID>0&&checkID<=divNumber.value&&!alreadyMath.includes(checkID)){
let controlMath=1
element=document.getElementById(checkID.toString()) as HTMLDivElement
for (let i of saveHaveMineNumber){
if (checkID===i.id){
controlMath--
element.innerText=i.haveMath.toString()
alreadyMath.push(checkID)
openedDivNumber.value++
}
}
if (controlMath){
element.innerText=""
let divArr=[]
alreadyMath.push(checkID)
divArr=callMatrix(checkID,size)
for (let i of divArr){
recursizeLookup(i,size)
}
openedDivNumber.value++
}
}
}
function isWin() {
if (openedDivNumber.value+saveMines.length===divNumber.value){
alert('恭喜你胜利啦!即将重新开始!')
resetThisGame()
}
}
return{
minePrediction,store,saveMines,divNumber,resetThisGame,clickThings,saveHaveMineNumber,openedDivNumber
}
}
}
</script>
<style scoped>
.noCheckBox{
background: #999999;
width: 40px;
height: 40px;
border: 1px solid black;
}
</style>
VUEX
```typescript
import { createStore } from 'vuex'
export default createStore({
state: {
playerName:undefined,
//雷区属性
fieldHeight:0,
fieldWeight:0,
mineNumber:0,
//确定是否初始化
is:false
},
getters: {
//雷区大小
mineFieldSize:function (state):number{
return state.fieldHeight*state.fieldWeight
}
},
mutations: {
setPlayerName:function (state:any,newName:string):void {
state.playerName=newName
},
setFieldHeight:function (state:any,newHeight:number):void {
state.fieldHeight=newHeight
},
setFieldWeight:function (state:any,newWeight:number):void {
state.fieldWeight=newWeight
},
setMineNumber:function (state:any,newNumber:number):void {
state.mineNumber=newNumber
},
setIs:function (state,newBoolean:boolean):void {
state.is=newBoolean
}
},
})
```