更新记录

1.0.0(2024-10-23) 下载此版本


平台兼容性

Vue2 Vue3
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
× × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × ×

1. 效果图

输入图片说明

2. 游戏规则

扫雷的规则很简单。盘面上有许多方格,方格中随机分布着一些雷。你的目标是避开雷,打开其他所有格子。一个非雷格中的数字表示其相邻 8 格子中的雷数,你可以利用这个信息推导出安全格和雷的位置。你可以用右键在你认为是雷的地方插旗(称为标雷)。你可以用左键打开安全的地方,左键打开雷将被判定为失败。

3. 实现思路

  1. 创建 row 行 col 列的二维数组,注意行和列创建时都要生成他的唯一 key;
  2. 根据选择的难度,将对应难度的雷的个数随机埋入 row * col 个格子中;
  3. 统计每个格子周边八个格子中雷的个数;
  4. 每个格子的状态:bomb 是否是存在雷的格子,bombCount 当前格子周边格子的雷的数量,flag 当前格子是否被插旗,opened 当前格子是否被翻开;
  5. 根据每个格子的状态,渲染对应的图片;
  6. 点击每个格子,执行对应的操作,比如插旗,翻开,是雷就爆炸等。

4. 创建背景

4.1 HTML 结构

<view class="rui-flex-cc">
    <view class="rui-minesweeper-content">
        <view class="rui-minesweeper-header-content">
            <view class="rui-header-counter">{{ bombCount.toString().padStart(3, '0') }}</view>
            <view class="rui-header-btn" @click="start">
                <view v-if="isGameOver">
                    <image v-if="isSuccess" :src="icon.iconFaceSuccess" class="rui-btn-icon"></image>
                    <image v-else :src="icon.iconFaceFail" class="rui-btn-icon"></image>
                </view>
                <image v-else :src="icon.iconFaceNormal" class="rui-btn-icon"></image>
            </view>
            <view class="rui-header-counter">{{ sec.toString().padStart(3, '0') }}</view>
        </view>
        <!-- 雷区 -->
        <view class="rui-main-content">

        </view>
    </view>
</view>

4.2 样式

$primary: #C0C0C0;
$light: #EEEEEE;
$dark: #969696;
.rui-minesweeper-content{
    background-color: $primary;
    width: 250px;
    height: 300px;
    margin: 20px auto;
    padding: 8px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    cursor: default;
    user-select: none;
    .rui-minesweeper-header-content{
        width: 100%;
        display: flex;
        justify-content: space-between;
        align-items: center;
        border: 3px inset $light;
        box-sizing: border-box;
        padding: 5px 10px;
        .rui-header-counter {
            background-color: black;
            color: red;
            font-family: Impact;
            min-width: 2em;
            text-align: center;
        }
        .rui-header-btn {
            border: 2px outset #eee;
            width: 25px;
            height: 25px;
            display: flex;
            align-items: center;
            justify-content: center;
            .rui-btn-icon{
                width: 21px;
                height: 21px;
                border-radius: 50%;
                display: block;
            }
        }
    }
}

4.3 实现结果

输入图片说明

5. 初始化格子

5.1 代码分析

  1. 获取当前难度等级:通过 find 方法从 navbars 数组中找到 ischeck 属性为 true 的第一个元素,即当前选中的难度等级。
  2. 提取难度等级的属性:使用解构赋值从 curGrade 对象中提取出 bombCount、row 和 col 属性,分别代表地雷数量、行数和列数。
  3. 初始化游戏状态:初始化游戏的各种状态变量,包括计时器(sec)、游戏结束标志(isGameOver)、游戏成功标志(isSuccess)、最大行数(maxrow)、最大列数(maxcol)、地雷数量(bombCount)和空白格子数量(blankCount)。
  4. 初始化网格:调用 initCells 方法来初始化游戏网格,该方法根据传入的行数和列数创建一个二维数组,用于表示游戏的网格。
  5. 随机放置地雷:调用 fixUpMinesweepers 方法在游戏网格中随机放置地雷。
  6. 计算当前位置周边雷的个数:调用 findCurrentPointAroundMinesweeperCount 方法来计算每个格子周围的地雷数量,这个信息通常用于在游戏中显示给玩家。

5.2 初始化代码实现

// 开始重置
start(){
    let curGrade = this.navbars.find(item => item.ischeck);
    let { bombCount, row, col } = curGrade;
    this.sec = 0;
    this.isGameOver = false;
    this.isSuccess = false;
    this.maxrow = row;
    this.maxcol = col;
    this.bombCount = bombCount;
    this.blankCount = row * col - bombCount;
    // 初始化网格
    this.initCells(row, col);
    // 随机放置地雷
    this.fixUpMinesweepers();
    // 计算当前位置周边雷的个数
    this.findCurrentPointAroundMinesweeperCount();
}

5.3 初始化网格

  1. 初始化外层数组:使用 Array.from 方法创建一个长度为 row 的新数组,并使用一个映射函数来初始化每个元素。映射函数的参数 r 代表当前正在处理的行,而 ridx 代表该行的索引。返回一个对象,该对象包含两个属性:
    • keyId: 一个随机生成的字符串,用于标识该行。
    • list: 一个新的数组,使用 Array.from 方法创建,长度为 col。
  2. 初始化内层数组:数组的每个元素也是一个对象,包含以下属性:
    • bomb: 一个布尔值,表示该单元格是否包含地雷,初始值为 false。
    • bombCount: 一个数字,表示该单元格周围的地雷数量,初始值为 0。
    • flag: 一个布尔值,表示该单元格是否被标记为地雷,初始值为 false。
    • opened: 一个布尔值,表示该单元格是否被翻开,初始值为 false。

5.4 网格代码实现

// 初始化网格
initCells(row, col){
    this.cells = Array.from({ length: row }, (r, ridx) => {
        return {
            keyId: `id-${randomString()}`,
            list: Array.from({length: col}, (c, cidx) => {
                return {
                    keyId: `id-${randomString()}`,
                    bomb: false, 
                    bombCount: 0, 
                    flag: false, 
                    opened: false
                }
            })
        }
    })
}

6. 随机放置地雷

6.1 代码分析

  1. 初始化:初始化一个局部变量 cells,它引用了组件实例的 cells 属性,即二维数组,表示游戏网格。
  2. 放置地雷的循环:使用 for 循环,它将执行 this.bombCount 次,即要放置的地雷数量。在每次循环中,它生成两个随机数 row 和 col,分别代表地雷在棋盘上的行和列索引。
  3. 检查并放置地雷:检查生成的随机位置 cells[row].list[col] 是否已经包含了地雷。如果没有,它将该位置标记为地雷(bomb 属性设置为 true)。如果该位置已经有地雷,它将 i 减一,以便在下一次循环中重新尝试放置地雷。

6.2 代码实现

fixUpMinesweepers(){
    let cells = this.cells;
    for(let i = 0; i < this.bombCount; i++){
        let row = Math.floor(Math.random() * this.maxrow);
        let col = Math.floor(Math.random() * this.maxcol);
        if(!cells[row].list[col].bomb){
            cells[row].list[col].bomb = true;
        } else {
            i--;
        }
    }
}

7. 计算当前位置周边雷的个数

7.1 代码分析

  1. 初始化:初始化一个局部变量 cells,它引用了组件实例的 cells 属性,即二维数组,表示游戏棋盘。
  2. 两层嵌套循环遍历单元格:使用两层嵌套循环遍历游戏棋盘上的每个单元格。外层循环遍历每一行,内层循环遍历每一列。对于每个单元格,它调用 handleAroundPoints 函数来处理该单元格周围的点。
  3. 调用 handleAroundPoints 函数:
    • cells:二维数组,表示游戏棋盘。
    • row:当前单元格的行索引。
    • col:当前单元格的列索引。
    • maxrow:游戏棋盘的最大行数。
    • maxcol:游戏棋盘的最大列数。
    • callback:一个回调函数,用于对每个相邻单元格执行操作。在这个回调函数中,它将当前单元格的 bombCount 属性增加 1,如果相邻单元格是地雷;否则,不增加。

7.2 代码实现

findCurrentPointAroundMinesweeperCount(){
    let cells = this.cells;
    for(let row = 0; row < this.maxrow; row++) {
        for(let col = 0; col < this.maxcol; col++) {
            handleAroundPoints({
                cells, 
                row, 
                col, 
                maxrow: this.maxrow,
                maxcol: this.maxcol,
                callback: cCell => cells[row].list[col].bombCount += cCell.bomb ? 1 : 0
            })
        }
    }
}

7.3 遍历当前位置周边代码分析

  1. 函数的参数包括:
    • cells:一个二维数组,表示单元格的网格。
    • row:当前单元格的行索引。
    • col:当前单元格的列索引。
    • maxrow:网格的最大行数。
    • maxcol:网格的最大列数。
    • callback:一个回调函数,用于对每个相邻单元格执行操作。
  2. 函数内部使用了两个嵌套的 for 循环来遍历相邻单元格。外层循环遍历行,内层循环遍历列。通过 Math.max 和 Math.min 函数确保循环的边界不会超出网格的范围。同时,使用 continue 语句跳过当前单元格本身,只处理相邻单元格。
  3. 通过回调函数 callback 来对每个相邻单元格执行操作,传入的参数包括单元格的值、行索引和列索引。

7.4 遍历当前位置周边代码实现

function handleAroundPoints({cells, row, col, maxrow, maxcol, callback}) {
    for(let srow = Math.max(row - 1, 0); srow < Math.min(row + 2, maxrow); srow++) {
        for(let scol = Math.max(col - 1, 0); scol < Math.min(col + 2, maxcol); scol++) {
            if (srow === row && scol === col) continue;
            callback(cells[srow].list[scol], srow, scol)
        }
    }
}

7.5 实现结果

输入图片说明

8. 点击事件

8.1 逻辑分析

  1. 初始化:接受两个参数:row 和 col,分别代表点击的单元格的行和列索引。方法内部首先检查是否存在一个计时器(timer),如果不存在,则创建一个计时器,每1000毫秒(即1秒)调用一次 onTick 方法。
  2. 点击事件:当前选中的点击类型(curTypeItem.type)是0,即左键【翻开】点击,代码会检查点击的单元格是否被标记为旗子(flag)。如果是,则不做任何操作;如果不是,它会进一步检查单元格是否包含地雷(bomb)。如果是地雷,单元格将被标记为已打开(opened),并且调用 onExplode 方法表示游戏失败;如果不是地雷,它将调用 onOpen 方法来打开单元格。
  3. 插旗点击事件:当前选中的点击类型是1,即右键【插旗】点击,代码将调用 onFlag 方法来标记或取消标记单元格为旗子。

8.2 代码实现

onClick(row, col) {
    let curTypeItem = this.chooseClickTypes.find(item => item.ischeck);
    if(!this.timer){
        this.timer = setInterval(this.onTick, 1000);
    }
    if(curTypeItem.type == 0){
        const cell = this.cells[row].list[col];
        if (cell.flag){
            return false;
        }
        if(cell.bomb){
            cell.opened = true;
            this.onExplode();
        } else {
            this.onOpen(row, col);
        }
    } else if(curTypeItem.type == 1){
        this.onFlag(row, col);
    }
}

9. 翻开逻辑

9.1 逻辑分析

  1. 初始化:接受两个参数:row 和 col,分别代表点击的单元格的行和列索引。方法内部首先获取当前单元格的引用,然后将其标记为已打开(opened),并清除其标记为旗子的状态(flag)。接着,它减少空白单元格计数器(blankCount)的值,表示已经打开了一个单元格。
  2. 检查游戏胜利条件:检查 blankCount 是否小于1,如果是,则表示所有非地雷单元格都已被打开,游戏胜利,因此调用 onSuccess 方法。
  3. 检查是否需要开启周围单元格:当前单元格的地雷数量(bombCount)为0,则表示其周围没有地雷,需要开启周围的单元格。因此,调用 openAround 方法来开启当前单元格周围的单元格。

9.2 代码实现

// 开启当前位置
onOpen(row, col) {
    const cell = this.cells[row].list[col];
    cell.opened = true;
    cell.flag = false;
    this.blankCount--;
    if (this.blankCount < 1) {
        this.onSuccess()
    } else if (cell.bombCount === 0) {
        this.openAround(row, col)
    }
}

10. 翻开当前位置的周边没有雷区和没有开启的位置

10.1 逻辑分析

  1. 获取了当前的单元格数组 cells,然后调用了一个名为 handleAroundPoints 的函数。
  2. 函数接收一个对象作为参数,该对象包含以下属性:
    • cells:当前的单元格数组。
    • row:当前单元格的行索引。
    • maxrow:网格的最大行数。
    • maxcol:网格的最大列数。
    • callback:一个回调函数,该函数接收三个参数:cCell(当前处理的单元格),crow(当前处理的单元格的行索引),ccol(当前处理的单元格的列索引)。回调函数的作用是检查当前单元格是否满足翻开条件,如果满足,则翻开该单元格。
  3. 检查当前单元格是否已经被打开、是否是炸弹或者是否被标记为旗子。如果这些条件都不满足,则调用 this.onOpen(crow, ccol) 方法来打开当前单元格。

10.2 代码实现

// 开启当前位置的周边没有雷区和没有开启的位置
openAround(row, col) {
    let cells = this.cells;
    handleAroundPoints({
        cells, 
        row, 
        col, 
        maxrow: this.maxrow,
        maxcol: this.maxcol,
        callback: (cCell, crow, ccol) => {
            if (!cCell.opened && !cCell.bomb && !cCell.flag){
                this.onOpen(crow, ccol);
            }
        }
    })
}

11. 增加难度选择

11.1 代码实现

// 切换难度
changeGrade(idx){
    let navbars = this.navbars;
    navbars.forEach(item => item.ischeck = false);
    navbars[idx].ischeck = true;
    this.start();
}

11.2 实现效果

11.2.1 初级

输入图片说明

11.2.1 中级

输入图片说明

12. 翻开或插旗切换

12.1 代码实现

// 切换点击事件类型
changeClickType(idx){
    let chooseClickTypes = this.chooseClickTypes;
    chooseClickTypes.forEach(item => item.ischeck = false);
    chooseClickTypes[idx].ischeck = true;
}

12.2 实现效果

输入图片说明

13. 全部代码

<template>
<view>
    <view class="rui-flex-cc">
        <view class="rui-flex-ac">
            <view :class="{
                'rui-navbar-li': true,
                'rui-active': nav.ischeck
            }" 
            @click="changeGrade(idx)"
            v-for="(nav,idx) in navbars" :key="nav.keyId">
                {{nav.name}}
            </view>
        </view>
    </view>
    <view class="rui-flex-cc">
        <view class="rui-minesweeper-content">
            <view class="rui-minesweeper-header-content">
                <view class="rui-header-counter">{{ bombCount.toString().padStart(3, '0') }}</view>
                <view class="rui-header-btn" @click="start">
                    <view v-if="isGameOver">
                        <image v-if="isSuccess" :src="icon.iconFaceSuccess" class="rui-btn-icon"></image>
                        <image v-else :src="icon.iconFaceFail" class="rui-btn-icon"></image>
                    </view>
                    <image v-else :src="icon.iconFaceNormal" class="rui-btn-icon"></image>
                </view>
                <view class="rui-header-counter">{{ sec.toString().padStart(3, '0') }}</view>
            </view>
            <!-- 雷区 -->
            <view class="rui-main-content">
                <view class="rui-row" v-for="(row,ridx) in cells" :key="row.keyId">
                    <view class="rui-col" v-for="(col,cidx) in  row.list"
                    @click="onClick(ridx, cidx)"
                    @contextmenu.prevent="onFlag(ridx, cidx)"
                    :key="col.keyId">
                        <view v-if="isGameOver">
                            <image v-if="!col.bomb" :src="icon[`icon${col.bombCount}`]" class="rui-icon"></image>
                            <view v-else>
                                <image v-if="col.opened" :src="icon.iconBlood" class="rui-icon"></image>
                                <image v-else :src="icon.iconMine" class="rui-icon"></image>
                            </view>
                        </view>
                        <view v-else>
                            <image v-if="col.flag" :src="icon.iconFlag" class="rui-icon"></image>
                            <image v-else-if="col.opened" :src="icon[`icon${col.bombCount}`]" class="rui-icon"></image>
                            <image v-else-if="!col.opened" :src="icon.iconBlank" class="rui-icon"></image>
                        </view>
                    </view>
                </view>
            </view>
        </view>
    </view>
    <!-- 切换点击事件类型 -->
    <view class="rui-flex-cc">
        <view class="rui-flex-ac">
            <view :class="{
                'rui-navbar-li': true,
                'rui-active': typeItem.ischeck
            }" 
            @click="changeClickType(idx)"
            v-for="(typeItem,idx) in chooseClickTypes" :key="typeItem.keyId">
                {{typeItem.name}}
            </view>
        </view>
    </view>
</view>
</template>

<script>
    import icon from './icon';
    import { randomString, handleAroundPoints } from './utils';
    export default {
        name:"RuiMinesweeper",
        data() {
            return {
                icon,
                sec: 0,
                bombCount: 0,
                blankCount: 0,
                maxrow: 0,
                maxcol: 0,
                isGameOver: false, 
                isSuccess: false,
                cells: [],
                chooseClickTypes: [{
                    name: '翻开',
                    type: 0,
                    keyId: `id-${randomString()}`,
                    ischeck: true
                },{
                    name: '插旗',
                    type: 1,
                    keyId: `id-${randomString()}`,
                    ischeck: false
                }],
                navbars: [{
                    name: '初级',
                    row: 9,
                    col: 9,
                    bombCount: 10,
                    keyId: `id-${randomString()}`,
                    ischeck: true
                },{
                    name: '中级',
                    row: 16,
                    col: 16,
                    bombCount: 40,
                    keyId: `id-${randomString()}`,
                    ischeck: false
                },{
                    name: '高级',
                    row: 20,
                    col: 20,
                    bombCount: 99,
                    keyId: `id-${randomString()}`,
                    ischeck: false
                },{
                    name: '自定义',
                    row: 30,
                    col: 30,
                    bombCount: 150,
                    keyId: `id-${randomString()}`,
                    ischeck: false
                }],
            };
        },
        created(){
            this.start()
        },
        methods: {
            // 切换难度
            changeGrade(idx){
                let navbars = this.navbars;
                navbars.forEach(item => item.ischeck = false);
                navbars[idx].ischeck = true;
                this.start();
            },
            // 切换点击事件类型
            changeClickType(idx){
                let chooseClickTypes = this.chooseClickTypes;
                chooseClickTypes.forEach(item => item.ischeck = false);
                chooseClickTypes[idx].ischeck = true;
            },
            // 开始重置
            start(){
                let curGrade = this.navbars.find(item => item.ischeck);
                let { bombCount, row, col } = curGrade;
                this.sec = 0;
                this.isGameOver = false;
                this.isSuccess = false;
                this.maxrow = row;
                this.maxcol = col;
                this.bombCount = bombCount;
                this.blankCount = row * col - bombCount;
                // 初始化网格
                this.initCells(row, col);
                // 随机放置地雷
                this.fixUpMinesweepers();
                // 计算当前位置周边雷的个数
                this.findCurrentPointAroundMinesweeperCount();
            },
            // 初始化网格
            initCells(row, col){
                this.cells = Array.from({ length: row }, (r, ridx) => {
                    return {
                        keyId: `id-${randomString()}`,
                        list: Array.from({length: col}, (c, cidx) => {
                            return {
                                keyId: `id-${randomString()}`,
                                bomb: false, 
                                bombCount: 0, 
                                flag: false, 
                                opened: false
                            }
                        })
                    }
                })
            },
            // 随机放置地雷
            fixUpMinesweepers(){
                let cells = this.cells;
                for(let i = 0; i < this.bombCount; i++){
                    let row = Math.floor(Math.random() * this.maxrow);
                    let col = Math.floor(Math.random() * this.maxcol);
                    if(!cells[row].list[col].bomb){
                        cells[row].list[col].bomb = true;
                    } else {
                        i--;
                    }
                }
            },
            // 计算当前位置周边雷的个数
            findCurrentPointAroundMinesweeperCount(){
                let cells = this.cells;
                for(let row = 0; row < this.maxrow; row++) {
                    for(let col = 0; col < this.maxcol; col++) {
                        handleAroundPoints({
                            cells, 
                            row, 
                            col, 
                            maxrow: this.maxrow,
                            maxcol: this.maxcol,
                            callback: cCell => cells[row].list[col].bombCount += cCell.bomb ? 1 : 0
                        })
                    }
                }
            },
            // 计时器一千秒
            onTick() {
                if (this.sec < 999){
                    this.sec++;
                } else {
                    // 时间完成,结束游戏
                    this.onStop();
                }
            },
            // 结束游戏
            onStop() {
                this.isGameOver = true;
                if(this.timer) {
                    clearInterval(this.timer);
                    this.timer = null;
                }
            },
            // 爆炸
            onExplode(){
                this.isSuccess = false;
                this.onStop();
            },
            // 成功
            onSuccess(){
                this.isSuccess = true;
                this.onStop();
            },
            // 插旗操作
            onFlag(row, col){
                const cell = this.cells[row].list[col];
                cell.flag = !cell.flag
                cell.flag ? this.bombCount-- : this.bombCount++; 
            },
            // 点击事件
            onClick(row, col) {
                let curTypeItem = this.chooseClickTypes.find(item => item.ischeck);
                if(!this.timer){
                    this.timer = setInterval(this.onTick, 1000);
                }
                if(curTypeItem.type == 0){
                    const cell = this.cells[row].list[col];
                    if (cell.flag){
                        return false;
                    }
                    if(cell.bomb){
                        cell.opened = true;
                        this.onExplode();
                    } else {
                        this.onOpen(row, col);
                    }
                } else if(curTypeItem.type == 1){
                    this.onFlag(row, col);
                }
            },
            // 开启当前位置
            onOpen(row, col) {
                const cell = this.cells[row].list[col];
                cell.opened = true;
                cell.flag = false;
                this.blankCount--;
                if (this.blankCount < 1) {
                    this.onSuccess()
                } else if (cell.bombCount === 0) {
                    this.openAround(row, col)
                }
            },
            // 开启当前位置的周边没有雷区和没有开启的位置
            openAround(row, col) {
                let cells = this.cells;
                handleAroundPoints({
                    cells, 
                    row, 
                    col, 
                    maxrow: this.maxrow,
                    maxcol: this.maxcol,
                    callback: (cCell, crow, ccol) => {
                        if (!cCell.opened && !cCell.bomb && !cCell.flag){
                            this.onOpen(crow, ccol);
                        }
                    }
                })
            }
        }
    }
</script>

<style lang="scss" scoped>
    $primary: #C0C0C0;
    $light: #EEEEEE;
    $dark: #969696;
    .rui-flex-cc{
        display: flex;
        justify-content: center;
        align-items: center;
    }
    .rui-flex-ac{
        display: flex;
        align-items: center;
    }
    .rui-icon{
        width: 25px;
        height: 25px;
        display: block;
    }
    .rui-navbar-li{
        color: #23527c;
        margin: 20px;
    }
    .rui-navbar-li.rui-active{
        color: #444;
        font-weight: 700;
    }
    .rui-minesweeper-content{
        background-color: $primary;
        margin: 20px auto;
        padding: 8px;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        cursor: default;
        user-select: none;
        .rui-minesweeper-header-content{
            width: 100%;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border: 3px inset $light;
            box-sizing: border-box;
            padding: 5px 10px;
            .rui-header-counter {
                background-color: black;
                color: red;
                font-family: Impact;
                min-width: 2em;
                text-align: center;
            }
            .rui-header-btn {
                border: 2px outset #eee;
                width: 25px;
                height: 25px;
                display: flex;
                align-items: center;
                justify-content: center;
                .rui-btn-icon{
                    width: 21px;
                    height: 21px;
                    border-radius: 50%;
                    display: block;
                }
            }
        }
        .rui-main-content{
            width: 100%;
            height: 100%;
            border: 3px inset #eee;
            box-sizing: border-box;
        }
        .rui-row{
            display: flex;
            align-items: center;
        }
        .rui-col {
            flex: none;
            width: 25px;
            height: 25px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .rui-col.no-event {
          pointer-events: none;
        }
    }
</style>

14. 工具函数

export function randomString(e) {
  e = e || 32;
  const t = "abcdefghijklmnopqrstwxyz1234567890";
  const a = t.length;
  let n = "";
  for (let i = 0; i < e; i++) {
    n += t.charAt(Math.floor(Math.random() * a));
  }
  return n;
}

export function handleAroundPoints({cells, row, col, maxrow, maxcol, callback}) {
    for(let srow = Math.max(row - 1, 0); srow < Math.min(row + 2, maxrow); srow++) {
        for(let scol = Math.max(col - 1, 0); scol < Math.min(col + 2, maxcol); scol++) {
            if (srow === row && scol === col) continue;
            callback(cells[srow].list[scol], srow, scol)
        }
    }
}

15. 总结

  1. 逻辑实现其实不是很难,最主要的是需要理清逻辑,然后按部就班的实现就可以了,写代码实现很重要,不要永远在思考的层面。
  2. 由于这个是按照 PC 实现的界面,所以在移动端游戏界面会超出视图,因此可以在此基础,将难度在一个页面选择,然后动态计算每个难度格子的大小。
  3. 基本的逻辑已完善,如果需要引流等代码,需要对饮的自己添加,由于使用的都是图片,换UI界面也比较容易,设计一套新的UI,替换图片就可以。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问