封装扫雷相关类
为更快、更简单实现扫雷的基础功能,这里先封装一个扫雷相关类,类中实现扫雷的基础数据和操作。
# 定义格子
定义格子属性:坐标x、y,当前格子值(类、空白、数字),是否已揭露,是否标记
interface ICell {
x: number;
y: number;
value: number;
isRevealed: boolean;
isFlagged: boolean;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 定义扫雷类
因为棋子是复杂类型,所以棋盘数组使用reactive创建响应式保证Cell内部属性修改触发渲染更新。同样为保证棋盘能再次更新我在reactive创建时包裹了一层**{ list: }**,我们使用时只需更新board.list即可。
export class MineSweeper {
private board: Reactive<{ list: ICell[][] }>;
private gameOver: boolean;
private size: number;
private mineList: number[];
// 其它方法属性
// ....
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 初始化雷区
- 按传入的
size生成size * size的二维数组ICell - 从
0到size * size中生成随机数,以mineList记录保证不重复,将上面记录的随机数转换成坐标填入到board.list中,即生成地雷 - 根据生成的地雷
mineList列表,我们累计计算周围的数字并填入到board.list中,即生成地雷周围数字 - 以上步骤即生成完整的雷区,需注意:随机数为一维,使用时需转换成二维坐标 实现代码如下:
private createBoard(): ICell[][] {
const board = Array.from({ length: this.size }, (_, x) =>
Array.from({ length: this.size }, (_, y) => ({
x,
y,
value: 0,
isRevealed: false,
isFlagged: false,
}))
);
// 添加雷
for(let i = 0; i < this.size; ) {
const randomIndex = Math.floor(Math.random() * this.size * this.size);
if (!this.mineList.includes(randomIndex)) {
this.mineList.push(randomIndex);
board[Math.floor(randomIndex / this.size)][randomIndex % this.size].value = -1;
i++
}
}
// 添加数字
for (const mineIndex of this.mineList) {
const mineX = Math.floor(mineIndex / this.size);
const mineY = mineIndex % this.size;
board[mineX][mineY].value = -1;
for (let x = Math.max(0, mineX - 1); x <= Math.min(this.size - 1, mineX + 1); x++) {
for (let y = Math.max(0, mineY - 1); y <= Math.min(this.size - 1, mineY + 1); y++) {
if (board[x][y].value !== -1) {
board[x][y].value++;
}
}
}
}
return board;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 展开格子算法
如果点中的格子为空白时需展开相邻所有的空白和数字格子,实现代码如下:
/**
* 递归揭示格子
* @param x
* @param y
* @returns
*/
private recursionRevealCell(x: number, y: number) {
if (this.gameOver || this.board.list[y][x].isRevealed) return;
this.board.list[y][x].isRevealed = true;
// 递归处理
if (this.board.list[y][x].value === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
if (i === 0 && j === 0) continue;
const newX = x + i;
const newY = y + j;
if (newX >= 0 && newX < this.size && newY >= 0 && newY < this.size) {
this.recursionRevealCell(newX, newY);
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 完整代码
import type { Reactive } from "vue";
import { reactive } from "vue";
interface ICell {
x: number;
y: number;
value: number;
isRevealed: boolean;
isFlagged: boolean;
}
export class MineSweeper {
private board: Reactive<{ list: ICell[][] }>;
private gameOver: boolean;
private size: number;
private mineList: number[];
get boardList(): Reactive<ICell[][]> {
return this.board.list;
}
constructor(size: number) {
this.size = size;
this.mineList = [];
this.board = reactive({ list: this.createBoard()});
this.gameOver = false;
}
/**
* 创建游戏棋盘
* @returns 棋盘
*/
private createBoard(): ICell[][] {
const board = Array.from({ length: this.size }, (_, x) =>
Array.from({ length: this.size }, (_, y) => ({
x,
y,
value: 0,
isRevealed: false,
isFlagged: false,
}))
);
// 添加雷
for(let i = 0; i < this.size; ) {
const randomIndex = Math.floor(Math.random() * this.size * this.size);
if (!this.mineList.includes(randomIndex)) {
this.mineList.push(randomIndex);
board[Math.floor(randomIndex / this.size)][randomIndex % this.size].value = -1;
i++
}
}
// 添加数字
for (const mineIndex of this.mineList) {
const mineX = Math.floor(mineIndex / this.size);
const mineY = mineIndex % this.size;
board[mineX][mineY].value = -1;
for (let x = Math.max(0, mineX - 1); x <= Math.min(this.size - 1, mineX + 1); x++) {
for (let y = Math.max(0, mineY - 1); y <= Math.min(this.size - 1, mineY + 1); y++) {
if (board[x][y].value !== -1) {
board[x][y].value++;
}
}
}
}
return board;
}
/**
* 揭示格子
* @param x
* @param y
* @returns
*/
public revealCell(x: number, y: number): void {
if (this.gameOver || this.board.list[y][x].isRevealed) return;
if (this.board.list[y][x].value === -1) {
this.gameOver = true;
for(let i = 0; i < this.mineList.length; i++) {
const mineX = Math.floor(this.mineList[i] / this.size);
const mineY = this.mineList[i] % this.size;
this.board.list[mineX][mineY].isRevealed = true;
}
this.gameOver = true;
setTimeout(() => {
alert('游戏失败!!!!')
}, 200)
return;
}
this.recursionRevealCell(x, y);
if (this.checkWin()) {
this.gameOver = true
setTimeout(() => {
alert('游戏胜利!!!!')
}, 200)
}
}
/**
* 递归揭示格子
* @param x
* @param y
* @returns
*/
private recursionRevealCell(x: number, y: number) {
if (this.gameOver || this.board.list[y][x].isRevealed) return;
this.board.list[y][x].isRevealed = true;
// 递归处理
if (this.board.list[y][x].value === 0) {
for (let i = -1; i <= 1; i++) {
for (let j = -1; j <= 1; j++) {
if (i === 0 && j === 0) continue;
const newX = x + i;
const newY = y + j;
if (newX >= 0 && newX < this.size && newY >= 0 && newY < this.size) {
this.recursionRevealCell(newX, newY);
}
}
}
}
}
private checkWin() {
return this.boardList.reduce((res, row) => {
res.push(...row.filter(cell => !cell.isRevealed));
return res;
}, []).length === this.mineList.length;
}
public flagCell(x: number, y: number): void {
if (this.gameOver || this.board.list[y][x].isRevealed) return;
this.board.list[y][x].isFlagged = !this.board.list[y][x].isFlagged;
}
public resetGame(): void {
this.mineList = [];
this.board.list = this.createBoard();
this.gameOver = false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
上次更新: 2025/09/05, 8:09:00