在二维数组中查找相邻元素(类似扫雷的矩阵)

问题描述 投票:0回答:2

我有一个像这样的二维数组。

[
  ["+", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "+"], 
  ["|", "*", "*", " ", " ", "*", " ", " ", " ", " ", "*", "*", "*", "*", "*", "*", "*", "*", " ", "*", " ", "|"], 
  ["|", " ", "*", "*", "*", "*", " ", " ", " ", " ", "*", "*", " ", " ", "*", "*", " ", "*", " ", "*", " ", "|"], 
  ["|", "*", " ", "*", " ", " ", " ", " ", "*", " ", "*", " ", "*", "*", "*", " ", "*", "*", "*", " ", "*", "|"], 
  ["|", "*", "*", " ", " ", "*", " ", " ", "*", "*", "*", " ", "*", " ", "*", "*", " ", "*", "*", " ", " ", "|"], 
  ["|", "*", " ", " ", " ", "*", " ", "*", " ", " ", " ", "*", "*", "*", "*", "*", " ", " ", " ", "*", "*", "|"], 
  ["|", " ", " ", " ", " ", " ", "*", " ", "*", " ", " ", " ", "*", " ", " ", " ", " ", " ", "*", " ", " ", "|"], 
  ["|", "*", "*", " ", "*", "*", "*", " ", "*", " ", " ", "*", "*", "*", "*", " ", "*", " ", " ", "*", " ", "|"], 
  ["|", " ", "*", " ", "*", "*", "*", "*", " ", " ", " ", "*", "*", " ", " ", " ", " ", "*", " ", "*", "*", "|"], 
  ["|", " ", "*", " ", " ", "*", " ", " ", "*", " ", "*", "*", "*", " ", "*", " ", "*", " ", " ", " ", "*", "|"], 
  ["|", " ", "*", "*", "*", "*", " ", " ", " ", " ", "*", "*", " ", "*", "*", "*", " ", " ", " ", "*", "*", "|"], 
  ["|", " ", " ", "*", "*", " ", "*", "*", "*", " ", " ", " ", " ", " ", " ", "*", "*", " ", " ", " ", " ", "|"], 
  ["|", "*", " ", "*", "*", " ", "*", " ", "*", "*", " ", " ", " ", "*", " ", "*", "*", "*", " ", " ", " ", "|"], 
  ["|", " ", " ", " ", "*", " ", "*", " ", "*", " ", "*", " ", " ", " ", " ", " ", " ", "*", "*", " ", " ", "|"], 
  ["|", "*", " ", "*", " ", "*", " ", "*", " ", " ", " ", "*", " ", "*", "*", " ", "*", "*", "*", "*", "*", "|"], 
  ["|", " ", " ", " ", "*", " ", "*", "*", "*", "*", "*", "*", " ", " ", "*", " ", " ", "*", "*", " ", "*", "|"], 
  ["|", "*", "*", "*", "*", "*", " ", " ", " ", "*", " ", "*", " ", "*", "*", " ", "*", "*", " ", " ", "*", "|"], 
  ["|", "*", " ", " ", " ", "*", "*", "*", "*", " ", " ", "*", " ", "*", " ", "*", " ", "*", " ", "*", "*", "|"], 
  ["|", "*", " ", "*", "*", "*", "*", "*", "*", " ", "*", "*", "*", "*", " ", " ", " ", "*", " ", " ", " ", "|"], 
  ["|", " ", " ", "*", " ", " ", "*", "*", " ", " ", " ", "*", " ", "*", "*", " ", " ", " ", " ", " ", "*", "|"], 
  ["|", " ", "*", " ", " ", " ", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", " ", " ", " ", "*", "|"], 
  ["+", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "+"]
]

我需要找到每个“*”附近的炸弹数量,如下图所示。

我这样做是为了获取索引,但我不知道如何获取相邻索引。

@array_map.each_index do |i|
subarray = @array_map[i]
 subarray.each_index do |x|
    puts String(i) << " " << String(x) << "... " << @array_map[i][x]
 end
end
ruby matrix minesweeper
2个回答
4
投票
minefield = [
  ["+", "-", "-", "-", "-", "-", "-", "-"],
  ["|", "*", "*", " ", " ", "*", " ", " "],
  ["|", " ", "*", "*", "*", "*", " ", " "],
  ["|", "*", " ", "*", " ", " ", " ", " "],
  ["|", "*", "*", " ", " ", "*", " ", " "],
  ["|", "*", " ", " ", " ", "*", " ", "*"],
  ["|", " ", " ", " ", " ", " ", "*", " "]
]
last_row = minefield.size-1
  #=> 6
last_col = minefield.first.size-1
  #=> 7
adj_cols = (0..last_col).each_with_object({}) do |j,h|
  h[j] = ([j-1, 0].max..[j+1, last_col].min).to_a
end
  #=> {0=>[0, 1], 1=>[0, 1, 2], 2=>[1, 2, 3], 3=>[2, 3, 4], 4=>[3, 4, 5],
  #    5=>[4, 5, 6], 6=>[5, 6, 7], 7=>[6, 7]}
arr = (0..last_row).each_with_object(minefield.dup.map(&:dup)) do |i,a|
  adj_rows = ([i-1, 0].max..[i+1, last_row].min).to_a
  (0..last_col).each do |j|
    next unless a[i][j] == ' '
    a[i][j] = adj_rows.product(adj_cols[j]).count do |r,c|
      minefield[r][c] == '*'
    end.to_s
  end
end
arr.each { |row| p row }

显示

["+", "-", "-", "-", "-", "-", "-", "-"]
["|", "*", "*", "4", "4", "*", "2", "0"]
["|", "4", "*", "*", "*", "*", "2", "0"]
["|", "*", "6", "*", "5", "3", "2", "0"]
["|", "*", "*", "2", "3", "*", "3", "1"]
["|", "*", "3", "1", "2", "*", "4", "*"]
["|", "1", "1", "0", "1", "2", "*", "2"]

参见数组#产品

adj_cols
是一个哈希,它提供列索引数组来标识每个列索引的相邻位置。在开始时执行一次此操作是有意义的,而不是对
minefield
的每个元素(行)重复计算。

要返回的数组被初始化为

minefield.dup.map(&:dup)  

这样

minefield
就不会被突变(修改)。

这是一个计算示例。

i = 3
adj_rows = ([i-1, 0].max..[i+1, last_row].min).to_a
  #=> ([2, 0].max..[4, 6].min).to_a
  #=> (2..4).to_a
  #=> [2, 3, 4]   
j = 4
a = adj_cols[j]
  #=> [3, 4, 5]
b = adj_rows.product(a)
  #=> [[2, 3], [2, 4], [2, 5],
  #    [3, 3], [3, 4], [3, 5],
  #    [4, 3], [4, 4], [4, 5]] 
b.count { |r,c| minefield[r][c] == '*' }
  #=> 5

请注意,

b
包括
[3, 4]
,我们知道它等于
' '
。不过,将其留在
count
操作中并没有什么坏处,如
minefield[r][c] == '*' #=> false
)。我们也可以写:

b = adj_rows.product(a) - [[i, j]]
  #=> [[2, 3], [2, 4], [2, 5],
  #    [3, 3],         [3, 5],
  #    [4, 3], [4, 4], [4, 5]] 

这是第二个示例(如果

minefield[0][4] #=> ' '
则适用)。

i = 0
j = 4
adj_rows = ([i-1, 0].max..[i+1, last_row].min).to_a
  #=> [0, 1]   
j = 4
a = adj_cols[j]
  #=> [3, 4, 5]
b = adj_rows.product(a)
  #=> [[0, 3], [0, 4], [0, 5],
  #    [1, 3], [1, 4], [1, 5]] 

对于问题中给出的

minefield
数组,返回值如下。

["+", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "+"]
["|", "*", "*", "4", "4", "*", "2", "0", "0", "2", "*", "*", "*", "*", "*", "*", "*", "*", "4", "*", "2", "|"]
["|", "4", "*", "*", "*", "*", "2", "1", "1", "4", "*", "*", "6", "7", "*", "*", "7", "*", "6", "*", "3", "|"]
["|", "*", "6", "*", "5", "3", "2", "2", "*", "6", "*", "6", "*", "*", "*", "6", "*", "*", "*", "4", "*", "|"]
["|", "*", "*", "2", "3", "*", "3", "3", "*", "*", "*", "6", "*", "8", "*", "*", "5", "*", "*", "5", "3", "|"]
["|", "*", "3", "1", "2", "*", "4", "*", "4", "4", "3", "*", "*", "*", "*", "*", "3", "3", "4", "*", "*", "|"]
["|", "3", "3", "2", "3", "5", "*", "5", "*", "2", "2", "5", "*", "7", "5", "4", "2", "2", "*", "4", "3", "|"]
["|", "*", "*", "4", "*", "*", "*", "6", "*", "2", "2", "*", "*", "*", "*", "2", "*", "3", "4", "*", "3", "|"]
["|", "4", "*", "5", "*", "*", "*", "*", "3", "3", "4", "*", "*", "6", "3", "4", "3", "*", "3", "*", "*", "|"]
["|", "3", "*", "6", "6", "*", "5", "3", "*", "3", "*", "*", "*", "5", "*", "4", "*", "2", "3", "5", "*", "|"]
["|", "2", "*", "*", "*", "*", "4", "4", "3", "4", "*", "*", "4", "*", "*", "*", "4", "2", "1", "*", "*", "|"]
["|", "2", "5", "*", "*", "6", "*", "*", "*", "4", "3", "2", "3", "3", "6", "*", "*", "3", "2", "2", "2", "|"]
["|", "*", "3", "*", "*", "6", "*", "7", "*", "*", "2", "1", "1", "*", "3", "*", "*", "*", "3", "1", "0", "|"]
["|", "2", "4", "4", "*", "5", "*", "5", "*", "4", "*", "2", "3", "3", "4", "4", "6", "*", "*", "4", "2", "|"]
["|", "*", "2", "*", "4", "*", "5", "*", "5", "5", "5", "*", "3", "*", "*", "3", "*", "*", "*", "*", "*", "|"]
["|", "3", "5", "5", "*", "5", "*", "*", "*", "*", "*", "*", "5", "5", "*", "5", "5", "*", "*", "6", "*", "|"]
["|", "*", "*", "*", "*", "*", "6", "6", "6", "*", "6", "*", "5", "*", "*", "4", "*", "*", "5", "5", "*", "|"]
["|", "*", "6", "5", "7", "*", "*", "*", "*", "4", "5", "*", "7", "*", "5", "*", "5", "*", "4", "*", "*", "|"]
["|", "*", "4", "*", "*", "*", "*", "*", "*", "3", "*", "*", "*", "*", "5", "2", "3", "*", "3", "3", "3", "|"]
["|", "2", "4", "*", "4", "5", "*", "*", "6", "5", "6", "*", "8", "*", "*", "4", "3", "2", "1", "2", "*", "|"]
["|", "1", "*", "2", "1", "2", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "*", "1", "0", "2", "*", "|"]
["+", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "+"]

0
投票

这会起作用:

minefield = [
  ["+", "-", "-", "-", "-", "-", "+"],
  ["|", "*", "*", "*", "*", "*", "|"],
  ["|", "*", " ", "*", "*", " ", "|"],
  ["|", " ", " ", " ", "*", " ", "|"],
  ["|", "*", " ", "*", "*", "*", "|"],
  ["|", "*", "*", "*", " ", " ", "|"],
  ["+", "-", "-", "-", "-", "-", "+"]
]

minefield.each_with_index do |row, y|
  row.each_with_index do |item, x|
    next unless item == '*'

    (y - 1).upto(y + 1) do |j|
      (x - 1).upto(x + 1) do |i|
        next if minefield[j][i] =~ /[*|+-]/

        minefield[j][i] = minefield[j][i].to_i.succ.to_s
      end
    end
  end
end

它穿过雷区阵列,跳过除炸弹之外的所有东西(

next unless item == '*'
)。一旦遇到炸弹,我们就会遍历它的周围环境,跳过任何特殊字段,例如
*
|
+
-
。 (这也会跳过炸弹本身)

由于炸弹不会出现在雷区边界,我们可以省略边界检查。

对于其余的周围字段(即空格和数字字符串),我们将该字段转换为整数(因此

" "
变为
0
),添加 1(通过
succ
),将其转换回字符串并重新分配到字段:(
succ
也适用于字符串,但无论如何我们都必须处理
" "

                   #  blank  # numeric
minefield[j][i]    #=> " "   #   "1"
  .to_i            #=> 0     #   1
  .succ            #=> 1     #   2
  .to_s            #=> "1"   #   "2"

最后,空白字段已被炸弹计数替换:

[
  ["+", "-", "-", "-", "-", "-", "+"],
  ["|", "*", "*", "*", "*", "*", "|"],
  ["|", "*", "5", "*", "*", "4", "|"],
  ["|", "2", "4", "5", "*", "4", "|"],
  ["|", "*", "5", "*", "*", "*", "|"],
  ["|", "*", "*", "*", "4", "2", "|"],
  ["+", "-", "-", "-", "-", "-", "+"]
]
© www.soinside.com 2019 - 2024. All rights reserved.