vue3,扫雷

<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>


  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值