<template>
<div flex items-center justify-center h-100vh w-full>
<div class="wrap pb-20">
<div class="header p-4 flex items-center justify-between">
<div
class="h-50 border-rd-50% flex items-center justify-center cursor-pointer text-size-40 avatar"
@click="reset"
>
<span v-if="status === 0">😊</span>
<span v-if="status === 2">😭</span>
<span v-if="status === 1">✌</span>
</div>
<div class="ml-20">
<el-radio-group v-model="levelId" class="radio-grid">
<el-radio
v-for="(item, idx) in levels"
:value="item.id"
:label="item.name"
:key="item.id"
@change="changeLevel"
>{{ item.name }}</el-radio
>
</el-radio-group>
</div>
</div>
<div class="flex justify-center px-8">
<div>
<div
class="flex"
v-for="(i, idx) in array"
:key="idx"
:class="{ disabled: status === 2 || status === 1 }"
>
<div
class="block flex items-center justify-center cursor-pointer"
:style="{
backgroundColor: j.status ? j.value === 100 ? status === 1 ? '#59F117' : '#F56C6C' : '#efe' : '',
border: j.status ? '1px solid #BABABA' : '',
}"
v-for="(j, idx2) in i"
:key="idx"
@click="toClick(j, idx, idx2)"
@contextmenu.prevent="rightClick(j)"
>
<template v-if="j.status === 1">
<div class="mine" v-if="j.value === 100"></div>
<div v-if="j.value && j.value !== 100">{{ j.value }}</div>
</template>
<template v-if="j.status === 2">
<div class="flag">🚩</div>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, toRefs } from "vue";
import { ElNotification } from 'element-plus'
/**
* @param {width} 列数
* @param {height} 行数
* @param {mineCount} 地雷数量
* @param {array} 游戏区域数组
* @param {status} 0 正常状态,1 胜利,2 失败
*/
let WIDTH = 6;
let HEIGHT = 9;
let mineCount = 10;
const status = ref(0);
/**
* @param {value} 1-8 数字,100 地雷
* @param {status} 0-未点击,1-已点击,2-已右键点击
*/
const array = ref<any>([]);
// console.log("array", array);
function generateRandomNumbers() {
const randomNumbers = new Set();
while (randomNumbers.size < mineCount) {
const randomNum1 = Math.floor(Math.random() * HEIGHT);
const randomNum2 = Math.floor(Math.random() * WIDTH);
const point = [randomNum1, randomNum2];
if (!randomNumbers.has(JSON.stringify(point))) {
// 防止生成相同的点
randomNumbers.add(JSON.stringify(point));
}
}
return Array.from(randomNumbers).map((point: any) => JSON.parse(point));
}
const init = () => {
array.value = Array.from({ length: HEIGHT }, () =>
Array.from({ length: WIDTH }, () => {
return {
status: 0,
value: 0,
};
})
);
const minePoints = generateRandomNumbers();
console.log("minePoints", minePoints);
for (let i of minePoints) {
array.value[i[0]][i[1]].value = 100;
}
generateNumbers();
};
/**
* 根据地雷坐标生成数字
*/
/**
* 根据地雷坐标生成数字
*/
function generateNumbers() {
for (let i = 0; i < HEIGHT; i++) {
for (let j = 0; j < WIDTH; j++) {
if (array.value[i][j].value !== 100) {
let count = 0;
if (i > 0 && j > 0 && array.value[i - 1][j - 1].value === 100) count++;
if (i > 0 && array.value[i - 1][j].value === 100) count++;
if (i > 0 && j < WIDTH - 1 && array.value[i - 1][j + 1].value === 100)
count++;
if (j > 0 && array.value[i][j - 1].value === 100) count++;
if (j < WIDTH - 1 && array.value[i][j + 1].value === 100) count++;
if (i < HEIGHT - 1 && j > 0 && array.value[i + 1][j - 1].value === 100)
count++;
if (i < HEIGHT - 1 && array.value[i + 1][j].value === 100) count++;
if (
i < HEIGHT - 1 &&
j < WIDTH - 1 &&
array.value[i + 1][j + 1].value === 100
)
count++;
array.value[i][j].value = count;
}
}
}
}
interface cell {
value: number;
status: number;
}
const toClick = (j: cell, idx: number, idx2: number) => {
// 旗子🚩不能点击
if(j.status === 2) return
j.status = 1;
if (j.value === 0) {
adhere(j, idx, idx2);
} else if (j.value === 100) {
console.log('踩雷');
status.value = 2;
for (let i of array.value) {
for (let j of i) {
if(j.value === 100) {
j.status = 1;
}
}
}
return
}
if (isSuccess()) {
console.log('胜利');
ElNotification.success({
title: '胜利',
message: '恭喜你游戏胜利!',
offset: 100,
})
status.value = 1;
for (let i of array.value) {
for (let j of i) {
if (j.value === 100) {
j.status = 1;
}
}
}
}
}
/**
* @func adhere 点到空白处自动展开
* @func isSuccess 检测胜利
*/
const adhere = (j: cell, idx: number, idx2: number) => {
if (idx > 0) {
if (array.value[idx - 1][idx2].status === 0) {
array.value[idx - 1][idx2].status = 1;
if (array.value[idx - 1][idx2].value === 0) {
adhere(array.value[idx - 1][idx2], idx - 1, idx2);
}
}
}
if (idx < HEIGHT - 1) {
if (array.value[idx + 1][idx2].status === 0) {
array.value[idx + 1][idx2].status = 1;
if (array.value[idx + 1][idx2].value === 0) {
adhere(array.value[idx + 1][idx2], idx + 1, idx2);
}
}
}
if (idx2 > 0) {
if (array.value[idx][idx2 - 1].status === 0) {
array.value[idx][idx2 - 1].status = 1;
if (array.value[idx][idx2 - 1].value === 0) {
adhere(array.value[idx][idx2 - 1], idx, idx2 - 1);
}
}
}
if (idx2 < WIDTH - 1) {
if (array.value[idx][idx2 + 1].status === 0) {
array.value[idx][idx2 + 1].status = 1;
if (array.value[idx][idx2 + 1].value === 0) {
adhere(array.value[idx][idx2 + 1], idx, idx2 + 1);
}
}
}
// 四周
if (idx > 0 && idx2 > 0) {
if (array.value[idx - 1][idx2 - 1].status === 0) {
array.value[idx - 1][idx2 - 1].status = 1;
if (array.value[idx - 1][idx2 - 1].value === 0) {
adhere(array.value[idx - 1][idx2 - 1], idx - 1, idx2 - 1);
}
}
}
if (idx > 0 && idx2 < WIDTH - 1) {
if (array.value[idx - 1][idx2 + 1].status === 0) {
array.value[idx - 1][idx2 + 1].status = 1;
if (array.value[idx - 1][idx2 + 1].value === 0) {
adhere(array.value[idx - 1][idx2 + 1], idx - 1, idx2 + 1);
}
}
}
if (idx < HEIGHT - 1 && idx2 > 0) {
if (array.value[idx + 1][idx2 - 1].status === 0) {
array.value[idx + 1][idx2 - 1].status = 1;
if (array.value[idx + 1][idx2 - 1].value === 0) {
adhere(array.value[idx + 1][idx2 - 1], idx + 1, idx2 - 1);
}
}
}
if (idx < HEIGHT - 1 && idx2 < WIDTH - 1) {
if (array.value[idx + 1][idx2 + 1].status === 0) {
array.value[idx + 1][idx2 + 1].status = 1;
if (array.value[idx + 1][idx2 + 1].value === 0) {
adhere(array.value[idx + 1][idx2 + 1], idx + 1, idx2 + 1);
}
}
}
};
const isSuccess = () => {
// 已经点开的格子
let count = 0;
for (let i of array.value) {
for (let j of i) {
if (j.status === 1 && j.value !== 100) {
count++;
}
}
}
console.log('count', count);
console.log('WIDTH * HEIGHT - mineCount', WIDTH * HEIGHT - mineCount);
if (count === WIDTH * HEIGHT - mineCount) {
status.value = 1;
return true;
}
return false;
};
/**
* 重置游戏
*/
const reset = () => {
status.value = 0;
init();
};
init();
/**
* 右键点击
*/
const rightClick = (j: cell) => {
if(j.status === 1) return
if (j.status === 2) {
j.status = 0;
} else {
j.status = 2;
}
};
/**
* 难度
*/
const levelId = ref(1);
const levels = ref([
{ id: 1, name: "简单" },
{ id: 2, name: "中等" },
{ id: 3, name: "困难" },
{ id: 4, name: "极限" },
]);
const changeLevel = (level: number) => {
if (level === 1) {
WIDTH = 6;
HEIGHT = 9;
mineCount = 10;
} else if (level === 2) {
WIDTH = 9;
HEIGHT = 10;
mineCount = 19;
} else if (level === 3) {
WIDTH = 14;
HEIGHT = 16;
mineCount = 30;
} else if (level === 4) {
WIDTH = 20;
HEIGHT = 20;
mineCount = 60;
}
init();
};
</script>
<style lang="scss" scoped>
$width: 32px;
$row: 10;
$col: 10;
.block {
width: $width;
height: $width;
background-color: #e4e4e4;
margin: 1px;
&:hover {
background-color: #cfc8c8;
border: 1px solid #b3a5a5;
}
}
/**地雷样式 */
.mine {
width: 70%;
height: 70%;
background-color: rgb(0, 0, 0);
border-radius: 50%;
clip-path: polygon(
16.6% 16.6%,
33.3% 0%,
50% 16.6%,
66.6% 0%,
83.3% 16.6%,
100% 33%,
83.3% 50%,
100% 66.6%,
83.3% 83.3%,
66.6% 100%,
50% 83.3%,
33.3% 100%,
16.6% 83.3%,
0% 66.6%,
16.6% 50%,
0% 33%
);
// animation: mine 1s infinite;
}
@keyframes mine {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.border {
border: 1px solid #555555;
}
.disabled {
pointer-events: none;
cursor: not-allowed;
}
.radio-grid{
display: grid;
grid-template-columns: 1fr 1fr;
}
.header{
border-bottom: 1px solid #a5a5a5;
margin-bottom: 12px;
}
.wrap{
box-shadow: 0 5px 2px 4px rgba(0, 0, 0, 0.4);
border-radius: 6px;
border: 1px solid #a5a5a5;
overflow: hidden;
}
.avatar{
span{
transition: .2s;
&:hover{
transform: scale(1.08);
}
&:active{
filter: brightness(1.25);
}
}
}
</style>
vue3,扫雷
于 2024-09-02 18:05:32 首次发布