连接 4 对角获胜检查

kat*_*987 1 ruby arrays multidimensional-array

我正在 Ruby Connect 4 游戏中测试对角获胜。我一直在使用硬编码的二维数组进行测试:

grid_array = [
["B", ".", ".", ".", ".", ".", ".", "."],
[".", "B", ".", ".", ".", ".", ".", "."],
[".", ".", "B", ".", ".", ".", ".", "."],
[".", ".", ".", "B", ".", ".", ".", "."],
[".", ".", ".", "X", "M", ".", ".", "."],
[".", ".", ".", ".", "X", "M", ".", "."],
[".", ".", ".", ".", ".", "X", "M", "."],
[".", ".", ".", ".", ".", ".", "X", "M"]
]
Run Code Online (Sandbox Code Playgroud)

该方法的内部循环工作正常(正确识别'M'或单独为获胜者,但例如,'B'当尝试将对角线检查跨列或向上移动到外循环以获取获胜值时,我陷入困境。'X'

def nw_diagonal_win (playing_board)
  row = 7
  while row < playing_board.size && row >= 0

    row = 7
    column = 7
    piece_count = 0

    while (row < playing_board.size && column < playing_board[row].size && column >= 0)

      if playing_board[row][column] == 'M'
        piece_count += 1

        if piece_count == 4
          puts "Player is the winner in a diagonal!"
        end

        puts piece_count.inspect

      else
        piece_count = 0
        puts "No winner."
      end

      row += 1
      column += 1
    end

    row -= 1
  end
end
Run Code Online (Sandbox Code Playgroud)

编辑添加:Connect 4 中的“获胜者”设置 4 个相邻的块(水平、垂直或对角线)。'X'在我的游戏中,这由和表示'0'。棋子从网格中一列的顶部“掉落”,并落到该列中最底部的可用空间。棋子可以堆叠成一列,但不能“漂浮”在棋盘中间。对角线可以从左上到右下或从右上到左下。仅当棋子在网格内不间断(没有环绕)时才会获胜。

想象一下井字游戏的更大版本,其中必须首先在底行进行移动,然后可以在上面的行中进行移动,就像盒子一样堆叠。连续四次(水平、垂直或对角\/)获胜。

为了回应史蒂夫的回答建议,如下:

def top_left_diagonal (playing_board, player_piece)
  row = 0
  while row < playing_board.size - 3
    piece_count = 0
    column = 0
    while column < playing_board[row].size - 3 && playing_board[row][column] == player_piece
      if (playing_board[row][column] == playing_board[row + piece_count][column + piece_count])
        piece_count += 1
      else
        piece_count = 0
      end
      column += 1
    end
    if piece_count == 4
      puts "Diagonal winner!"
    end
    row += 1
  end
end
Run Code Online (Sandbox Code Playgroud)

Car*_*and 5

假设我们有

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"]]
Run Code Online (Sandbox Code Playgroud)

确实,这只是 6x6,但解决方案没有什么不同。

首先,由于数组很小,我们不必担心计算效率,因此我们可以专注于代码效率。

我们首先检查每行是否有四个连续。

检查行数

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
Run Code Online (Sandbox Code Playgroud)

如果连续w有四个,则此方法返回,如果连续有四个,则返回。wbbnil

因为arr = grid我们发现没有一行连续包含四个'b'' 或'w'' 。

four_in_a_row_by_row(grid)
  #=> nil
Run Code Online (Sandbox Code Playgroud)

请注意,此方法不要求arr.size == grid.size或 的所有元素arr具有相同的大小。它只是检查是否有任何元素有四个'w'或四个'b'连续。这在以后会有重要意义。

例如,传递到块的最后一个元素arr如下。

row =  ["b", "w", "b", "b", "w", "b"]
Run Code Online (Sandbox Code Playgroud)

然后我们计算

enum0 = row.each_cons(4)
  #=> #<Enumerator: ["b", "w", "b", "b", "w", "b"]:each_cons(4)>
Run Code Online (Sandbox Code Playgroud)

enum1 = enum0.find
  #=> #<Enumerator: #<Enumerator: ["b", "w", "b", "b", "w", "b"]:each_cons(4)>:find>
Run Code Online (Sandbox Code Playgroud)

enum1可以被认为是一个复合枚举器,尽管 Ruby 并没有这样定义。请参阅Enumerable#each_consEnumerable#find

我们可以将此枚举器转换为数组以查看将传递到块的元素。

enum1.to_a
  #=> [["b", "w", "b", "b"],
  #    ["w", "b", "b", "w"],
  #    ["b", "b", "w", "b"]]
Run Code Online (Sandbox Code Playgroud)

第一个元素被传递到块并进行以下计算。

    a = enum1.next
    u = a.uniq
    u.size == 1
Run Code Online (Sandbox Code Playgroud)

因此我们不需要计算a.first != '.'. 的剩余两个元素enum1被传递到块并为每个元素进行计算,表明一行中或最后一行nil中没有四个元素。'w''b'

我们快完成了!

“等等”,你说,我们只检查了行!仍然有柱子和所有的对角线!敬请关注...

检查列

这个非常简单。

four_in_a_row_by_row(grid.transpose)
  #=> nil
Run Code Online (Sandbox Code Playgroud)

这里

 grid.transpose
   #=> [[".", ".", ".", "b", "w", "b"],
   #    [".", ".", ".", ".", "w", "w"],
   #    [".", ".", ".", "w", ".", "b"],
   #    [".", "w", "w", ".", "w", "b"],
   #    [".", "w", "b", "b", "b", "w"],
   #    [".", ".", ".", ".", "b", "b"]]
Run Code Online (Sandbox Code Playgroud)

检查对角线(从左上到右下)

这里我们需要做的就是构造一个arr包含对角线的数组,然后应用four_in_a_row(arr)。首先确定包含第一列中长度或更长的元素的对角线4。包括以下对角线

[grid[0][0], grid[1][1], grid[2][2], grid[3][3], grid[4][4],grid[5][5]] 
[grid[1][0], grid[2][1], grid[3][2], grid[4][3], grid[5][4]] 
[grid[2][0], grid[3][1], grid[4][2], grid[5][3]] 
Run Code Online (Sandbox Code Playgroud)

没有必要考虑包含第一列中的元素的剩余对角线,因为它们包含的4元素少于:

[grid[3][0], grid[4][1], grid[5][2]] 
[grid[4][0], grid[5][1]] 
[grid[5][0]] 
Run Code Online (Sandbox Code Playgroud)

我们可以如下获得前三个对角线。

(0..grid.size-4).map { |i| (0..grid.size-1-i).map { |j| grid[i+j][j] } }
  #=> [[".", ".", ".", ".", "b", "b"],
  #    [".", ".", "w", "w", "w"],
  #    [".", ".", ".", "b"]]
Run Code Online (Sandbox Code Playgroud)

同样,确定包含第一行中的元素(除 之外)的对角线grid[0][0],这些元素的长度4或更大。这些是对角线

[grid[0][1], grid[1][2], grid[2][3], grid[3][4], grid[4][5]] 
[grid[0][2], grid[1][3], grid[2][4], grid[3][5]] 
Run Code Online (Sandbox Code Playgroud)

包含第一行中的元素(除 之外grid[0][0])的其余对角线包含少于4元素。我们按如下方式获得这些对角线。

(1..grid.first.size-4).map do |j|
  (0..grid.size-j-1).map { |i| grid[i][j+i] }
end
  #=> [[".", ".", "w", "b", "b"],
  #    [".", "w", "b", "."]]
Run Code Online (Sandbox Code Playgroud)

因此,我们可以获得所有对角线的数组,如下所示。

def diagonals(grid)
  (0..grid.size-4).map do |i|
    (0..grid.size-1-i).map { |j| grid[i+j][j] }
  end.concat((1..grid.first.size-4).map do |j|
    (0..grid.size-j-1).map { |i| grid[i][j+i] }
  end)
end
Run Code Online (Sandbox Code Playgroud)
arr = diagonals(grid)
  #=> [[".", ".", ".", ".", "b", "b"],
  #    [".", ".", "w", "w", "w"],
  #    [".", ".", ".", "b"],
  #    [".", ".", "w", "b", "b"],
  #    [".", "w", "b", "."]]
Run Code Online (Sandbox Code Playgroud)

我们看到没有一条对角线包含连续的四个对角线。

four_in_a_row_by_row(arr)
  #=> nil
Run Code Online (Sandbox Code Playgroud)

检查对角线(左下角到右上角)

grid我们可以进行与计算对角线相同的推理,但由于这里计算效率并不重要,因此有一种更简单的方法:计算“旋转” 90度获得的数组的对角线。

def rotate90(grid)
  ncols = grid.first.size
  grid.each_index.with_object([]) do |i,a|
    a << ncols.times.map { |j| grid[j][ncols-1-i] }
  end
end
Run Code Online (Sandbox Code Playgroud)
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"]]
Run Code Online (Sandbox Code Playgroud)

我们看到没有任何对角线包含连续的四个对角线。

four_in_a_row_by_row(arr1)
  #=> "w"
Run Code Online (Sandbox Code Playgroud)

把它们放在一起

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"
Run Code Online (Sandbox Code Playgroud)

的替代计算four_in_a_row_by_row

或者可以写four_in_a_row_by_row如下。

def four_in_a_row_by_row(arr)
  row = arr.find { |row| four_in_a_row(row) }
  row.nil? ? nil : four_in_a_row(row)
end
Run Code Online (Sandbox Code Playgroud)
def four_in_a_row(row)
  (0..row.size-5).find { |j| row[j,4].uniq.size == 1 && row[j] != '.' }
end
Run Code Online (Sandbox Code Playgroud)
four_in_a_row_by_row(grid)
  #=> nil
Run Code Online (Sandbox Code Playgroud)

如果愿意,row.nil? ? nil : four_in_a_row(row)可以替换为

four_in_a_row(row) unless row.nil?
Run Code Online (Sandbox Code Playgroud)