假设我们有
grid = [
%w| . . . . . . |,
%w| . . . w w . |,
%w| . . . w b . |,
%w| b . w . b . |,
%w| w w . w b b |,
%w| b w b b w b |
]
#=> [[".", ".", ".", ".", ".", "."],
# [".", ".", ".", "w", "w", "."],
# [".", ".", ".", "w", "b", "."],
# ["b", ".", "w", ".", "b", "."],
# ["w", "w", ".", "w", "b", "b"],
# ["b", "w", "b", "b", "w", "b"]]
没错,这只是6x6,但解决方案也不例外 .
首先,由于数组较小,我们不必担心计算效率,因此我们可以专注于代码效率 .
让我们首先检查每行中是否有四个 .
Check the rows
def four_in_a_row_by_row(arr)
arr.each do |row|
a = row.each_cons(4).find { |a| a.uniq.size == 1 && a.first != '.' }
return a.first unless a.nil?
end
nil
end
如果连续有四个 w ,则此方法返回 w ,如果连续有四个 b ,则返回 b ,否则返回 nil .
请注意,此方法不要求 arr.size == grid.size 或 arr 的所有元素具有相同的大小 . 它只是检查是否有任何元素连续有四个 'w' 或四个 'b' . 这将具有重要意义 .
例如,传递给块的 arr 的最后一个元素如下 .
row = ["b", "w", "b", "b", "w", "b"]
然后我们计算
enum0 = row.each_cons(4)
#=> #
和
enum1 = enum0.find
#=> #:find>
enum1 可以被认为是复合枚举器,尽管Ruby没有这样定义它 .
我们可以将此枚举器转换为数组,以查看将传递给块的元素 .
enum1.to_a
#=> [["b", "w", "b", "b"], ["w", "b", "b", "w"], ["b", "b", "w", "b"]]
第一个元素传递给块并进行以下计算 .
a = enum1.next
u = a.uniq
u.size == 1
因此,我们不需要计算 a.first != '.' . 将 enum1 的剩余两个元素传递给块,并为每个元素计算 nil ,表示最后一行中_111110_或 'b' 中没有四个元素 .
我们差不多完了!
“等等”,你说,我们只检查了行!还有柱子和所有对角线!敬请关注...
Check the columns
这个很容易 .
four_in_a_row_by_row(grid.transpose)
#=> nil
Check the diagonals (top left to bottom right)
这里我们需要做的是构造为包含对角线的数组 arr 然后应用 four_in_a_row(arr) . 首先确定包含第一列中长度为 4 或更大的元素的对角线:
(0..grid.size-4).map { |i| (0..grid.size-1-i).map { |j| grid[i+j][j] } }
#=> [[".", ".", ".", ".", "b", "b"], [".", ".", "w", "w", "w"], [".", ".", ".", "b"]]
同样,识别包含第一行中元素的对角线,其他 [0, 0] ,其长度为 4 或更大:
(1..grid.first.size-4).map { |j| (0..grid.size-j-1).map { |i| grid[i][j+i] } }
#=> [[".", ".", "w", "b", "b"], [".", "w", "b", "."]]
def diagonals(grid)
(0..grid.size-4).map { |i| (0..grid.size-1-i).map { |j| grid[i+j][j] } }.
concat((1..grid.first.size-4).map { |j| (0..grid.size-j-1).map { |i| grid[i][j+i] } })
end
arr = diagonals(grid)
#=> [[".", ".", ".", ".", "b", "b"], [".", ".", "w", "w", "w"], [".", ".", ".", "b"],
# [".", ".", "w", "b", "b"], [".", "w", "b", "."]]
four_in_a_row_by_row(arr)
#=> nil
Check the antediagonals (botton left to top right)
我们可以通过与计算对角线相同的推理,但由于计算效率在这里并不重要,因此有一种更简单的方法:计算由"rotating" grid 90度获得的数组的对角线 .
def rotate90(grid)
ncols = grid.first.size
grid.each_index.with_object([]) { |i,a| a << ncols.times.map { |j| grid[j][ncols-1-i] } }
end
arr = rotate90(grid)
#=> [[".", ".", ".", ".", "b", "b"],
# [".", "w", "b", "b", "b", "w"],
# [".", "w", "w", ".", "w", "b"],
# [".", ".", ".", "w", ".", "b"],
# [".", ".", ".", ".", "w", "w"],
# [".", ".", ".", "b", "w", "b"]]
arr1 = diagonals(arr)
#=> [[".", "w", "w", "w", "w", "b"], [".", "w", ".", ".", "w"],
# [".", ".", ".", "b"], [".", "b", ".", ".", "w"], [".", "b", "w", "b"]]
four_in_a_row_by_row(arr1)
#=> "w"
Putting it all together
def four_in_a_row(grid)
four_in_a_row_by_row(grid) ||
four_in_a_row_by_row(grid.transpose) ||
four_in_a_row_by_row(diagonals(grid)) ||
four_in_a_row_by_row(diagonals(rotate90(grid)))
end
four_in_a_row_by_row(grid)
#=> "w"