折叠何宝莹

你也可以用 JS 写个扫雷

扫雷先要解决怎样计算周围的雷的数量,这篇文章主要介绍这个核心问题怎么解决。将这个问题细化一下,就是:

array 是一个「包含了『只包含了 0 9 的 array』的 array」
返回一个标记过的 array
** 注意, 使用一个新数组来存储结果, 不要直接修改老数组
范例如下, 这是 array
[
[0, 9, 0, 0],
[0, 0, 9, 0],
[9, 0, 9, 0],
[0, 9, 0, 0],
]
这是标记后的结果
[
[1, 9, 2, 1],
[2, 4, 9, 2],
[9, 4, 9, 2],
[2, 9, 2, 1],
]
规则是, 0 会被设置为四周 8 个元素中 9 的数量

直接亮代码了,那时候花了不少时间写呢。不到100行代码。基本思路就是首先横排一行一行标记好数量,然后翻转二维数组,然后还是横排标记雷(数字9)的数量,然后再翻转二维数组。原理讲起来很简单。写起来花了点时间。

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
var log = console.log.bind(console)

var markedLine = function(array) {
var line = array.slice(0)
// log('line', line)
for (var i = 0; i < line.length; i++) {
// log('line2' , line)
var n = line[i]
var left = line[i - 1]
var right = line[i + 1]
if (n == 9) {
if ((left != 9) && (left != null)) {
line[i - 1] = left + 1
// log(left)
}
if ((right != 9) && (right != null)) {
line[i + 1] = right + 1
}
}
}
return line
}

var markedMultiLine = function(array) {
var result = []
for (var i = 0; i < array.length; i++) {
var newLine = markedLine(array[i])
result.push(newLine)
}
// log(result)
return result
}

var max = function(array) {
var max = array[0]
for (var i = 0; i < array.length; i++) {
var n = array[i]
if (n > max) {
max = n
}
}
return max
}

// 实现 markAround 函数,对于每一个 square[i][j] 这样的元素都按照规则 +1分 4 个顶角、4 条边和剩下的元素这几种情形
// 思路一: 横行为单位, 实现 markedline, 然后竖行 为单位, 实现 markedline, 关键点就是把一个竖行变成一个数组
// 写个函数, 转置矩阵数组, 其实就是位置挪移, [i][j]变成[j][i]

var transpose = function(array) {
var result = []
var lens = []
// 首先计算这个矩阵的长高 m, n
// 计算最长字符串长度
for (var i = 0; i < array.length; i++) {
var n = array[i]
// 将所有字符串的长度放到 lens 里
var len = n.length
lens.push(len)
}
// 得到数组矩阵的长 m 和高 n
var m = max(lens)
// log('max-len', m)
var n = array.length
// log('长和高', m, n)

// 开始真正转换了
for (var j = 0; j < m; j++) {
var newLine = []
for (var k = 0; k < n; k++) {
// log(array[k][j])
newLine.push(array[k][j])
}
result.push(newLine)
}
return result
}

var markedSquare = function(array) {
// +1
var array1 = markedMultiLine(array)

// 得到转置的数组
array1 = transpose(array1)

// +1
var array2 = markedMultiLine(array1)

array2 = transpose(array2)
// log('result', array2)
return array2
}
var array = [
[0, 9, 0],
[9, 0, 9],
]
log(markedSquare(array))

看到这里也挺不容易,还有另一个实现的方法,没有上一个那么炫酷,不过代码短很多,也挺实在的,也就是一个一个雷去数。
同样亮代码:

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
var log = console.log.bind(console)

var clonedSquare = function(array) {
var l = []
for (var i = 0; i < array.length; i++) {
var line = array[i]
var e = line.slice(0)
l.push(e)
}
return l
}
var plus1 = function(array, x, y) {
// 1. array[x][y] 不能为 9
// 2. x 和 y 不能越界
var n = array.length
var validX = x >= 0 && x < n
var validY = y >= 0 && y < n
if (validX && validY) {
if (array[x][y] != 9) {
array[x][y] += 1
}
}
}
var markAround = function(array, x, y) {
if (array[x][y] == 9) {
// 标记周围 8 个
// 标记的时候要判断是不是可以标记

// 先标记左边 3 个
plus1(array, x - 1, y - 1)
plus1(array, x - 1, y)
plus1(array, x - 1, y + 1)

// 再标记上下两个
plus1(array, x, y - 1)
plus1(array, x, y + 1)

// 最后标记右边 3 个
plus1(array, x + 1, y - 1)
plus1(array, x + 1, y)
plus1(array, x + 1, y + 1)
}
}

var markedSquare = function(array) {
var square = clonedSquare(array)
for (var i = 0; i < square.length; i++) {
var line = square[i]
for (var j = 0; j < line.length; j++) {
markAround(square, i, j)
}
}
return square
}
var array = [
[0, 9, 0],
[9, 0, 9],
]
log(markedSquare(array))

这个方法的步骤:

1. 先定义一个 clonedSquare 函数,把 array 的内容复制到一个新数组中
2. 调用 clonedSquare 函数,得到 square
3. 遍历 square,每次遍历的元素为 line
4. 遍历 line,调用一个 markAround 函数,传入 square, i, j
5. 实现 markAround 函数,对于每一个 `square[i][j]` 这样的元素都按照规则 +1
    分 4 个顶角、4 条边和剩下的元素这几种情形
6. 两重遍历结束后,square 就是需要的结果,`return square` 即可。