ruby 连接4对角胜利检查

mm9b1k5b  于 2023-04-29  发布在  Ruby
关注(0)|答案(4)|浏览(79)

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

grid_array = [
["B", ".", ".", ".", ".", ".", ".", "."],
[".", "B", ".", ".", ".", ".", ".", "."],
[".", ".", "B", ".", ".", ".", ".", "."],
[".", ".", ".", "B", ".", ".", ".", "."],
[".", ".", ".", "X", "M", ".", ".", "."],
[".", ".", ".", ".", "X", "M", ".", "."],
[".", ".", ".", ".", ".", "X", "M", "."],
[".", ".", ".", ".", ".", ".", "X", "M"]
]

这个方法的内部循环可以正确地工作(正确地识别'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

编辑添加:一个“赢家”在连接4套4个相邻的作品(水平,垂直,或对角线)。在我的游戏中,这由'X''0'表示。片段从网格中某列的顶部“放下”,并落到该列中最底部的可用空间。棋子可以堆叠在一列中,但不能“浮动”在板的中间。对角线可以从左上到右下或从右上到左下。只有棋子在格子内不间断(没有环绕),才算赢。
想象一个更大版本的井字游戏,其中必须首先在最下面的一行进行移动,然后可以在上面的几行进行移动,像盒子一样堆叠。四个在一排(水平,垂直,或对角线\ /)获胜。
根据Steve的回答建议,如下:

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
kmpatx3s

kmpatx3s1#

假设我们有

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,但解决方案没有什么不同。
首先,由于数组很小,我们不必担心计算效率,因此我们可以专注于代码效率。
让我们首先检查是否有四个在一行中的每一行。

检查行

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 = grid,我们发现没有一行包含四个'b''w'

four_in_a_row_by_row(grid)
  #=> nil

请注意,此方法不要求arr.size == grid.sizearr的所有元素具有相同的大小。它只是检查一行中是否有四个'w'或四个'b'。这将在以后有意义。
例如,传递给块的arr的最后一个元素如下所示。

row =  ["b", "w", "b", "b", "w", "b"]

然后我们计算

enum0 = row.each_cons(4)
  #=> #<Enumerator: ["b", "w", "b", "b", "w", "b"]:each_cons(4)>

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

enum1可以被认为是一个 compound 枚举器,尽管Ruby没有这样定义。请参见Enumerable#each_cons和Enumerable#find。
我们可以将这个枚举数转换为数组,以查看将传递给块的元素。

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,这表明一行中没有四个'w'或最后一行中的'b'
我们就快完成了!
“等等”,你说,我们只检查了行!还有那些柱子和对角线!请继续收看。..

请勾选栏目

这一个是死容易。

four_in_a_row_by_row(grid.transpose)
  #=> nil

给你

grid.transpose
   #=> [[".", ".", ".", "b", "w", "b"],
   #    [".", ".", ".", ".", "w", "w"],
   #    [".", ".", ".", "w", ".", "b"],
   #    [".", "w", "w", ".", "w", "b"],
   #    [".", "w", "b", "b", "b", "w"],
   #    [".", ".", ".", ".", "b", "b"]]

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

在这里,我们需要做的就是构造包含对角线的数组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]]

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

[grid[3][0], grid[4][1], grid[5][2]] 
[grid[4][0], grid[5][1]] 
[grid[5][0]]

我们可以得到前三条对角线如下。

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

类似地,标识包括第一行中除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]]

包含第一行中元素的其余对角线(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", "."]]

因此,我们可以得到一个所有对角线的数组如下。

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
arr = diagonals(grid)
  #=> [[".", ".", ".", ".", "b", "b"],
  #    [".", ".", "w", "w", "w"],
  #    [".", ".", ".", "b"],
  #    [".", ".", "w", "b", "b"],
  #    [".", "w", "b", "."]]

我们看到没有对角线包含四行。

four_in_a_row_by_row(arr)
  #=> nil

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

我们可以通过与计算对角线相同的推理,但由于计算效率在这里并不重要,因此有一种更简单的方法:计算通过“旋转”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
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"

把一切都放在一起

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"

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
def four_in_a_row(row)
  (0..row.size-5).find { |j| row[j,4].uniq.size == 1 && row[j] != '.' }
end
four_in_a_row_by_row(grid)
  #=> nil

如果首选,row.nil? ? nil : four_in_a_row(row)可替换为

four_in_a_row(row) unless row.nil?
t98cgbkg

t98cgbkg2#

如果存在获胜对角线,则开始位置必须在row [0-3]和col [0-3]的范围内。任何从左上方方框外开始的对角线都没有足够的右和下位置来使其连续四个。
所以实际上你需要一个while row < 4while col < 4嵌套循环。
对于每个行列组合,假设值不是“”。“然后你可以设置你的piece_count为1,然后做一个计数器1到3,检查playing_board[row + counter][col + counter]等于playing_board[row][col]的值,如果它是递增你的piece_count。
在计数器1到3循环之外,如果piece_count为4,则您有赢家。

q43xntqr

q43xntqr3#

我想出了一个解决方案,似乎在测试中工作。它从右下角到左上角,从我的8x8网格中的第7行第7列开始。我还创建了从左下角移动到右上角的前对角线。
我已经测试了最后一小段时间,并没有发现一个错误,但很想知道是否有人戳它的漏洞。
我非常感谢你们所有人--你们的解决方案和建议让我走到了这一步!

def nw_diagonal_win (playing_board, player_piece)
    row = 7
    column = 7
    piece_count = 0
    while row < playing_board.size && row >= 0 && column < playing_board[row].size && column >= 0
      if playing_board[row][column] == player_piece
        piece_count += 1
        column -= 1
        row -= 1
      else
        piece_count = 0
        column -= 1
        if column < 3
          row -= 1
          column = 7
        end
      end
        if piece_count == 4
          puts "Player #{player_piece} is the winner in a diagonal!"
        end
    end
end
wd2eg0qa

wd2eg0qa4#

我正在玩一个类似的网格,为一个填字游戏,并出来了一种方法,以获得对角线。我改变了一点,以适应连接4。这不是一个完整的答案,但我希望它能有所帮助。
首先,我想Map网格以获得坐标:

grid_map = grid_array.map.with_index{ |line, y| line.map.map.with_index { |e, x| [e, x, y] } }

然后有几种方法来恢复矩阵的所有对角线(无论如何,应该有更好的解决方案)。
第一个只取一个方向上对角线的一半:

def half_diagonals(matrix)
  max_y = matrix[0].size
  max_x = matrix.size
  diagonals = []
  (0...max_x).each do |x_start|
    x = x_start
    y = 0
    diagonal = [matrix[x][y]]
    while  x < max_x - 1 and y < max_y - 1 do
      x += 1
      y += 1
      diagonal << matrix[x][y]
    end
    diagonals << diagonal
  end
  # only in diagonals with at least four spots
  return diagonals.select{ |d| d.size >= 4 }
end

这调用半对角(matrix)对矩阵的变换以得到所有对角:

def diagonals(matrix)
  half_diagonals(matrix) +
  half_diagonals(matrix.transpose)[1..-1] +
  half_diagonals(matrix.reverse) +
  half_diagonals(matrix.reverse.transpose)[1..-1]
end

# diagonals(grid_map).each { |e| p e } # to print the diagonals

现在,需要检查每个对角线的赢家,因此定义一个方法来执行此操作:

def check_diagonal(diagonal, empty_spot = ".")
  check = diagonal.chunk_while { |s1, s2| s1[0] == s2[0] }.map { |e| [e.count, e[0][0], e.map{ |ee| ee[1..-1]}] }
  return detected = check.detect { |e| e[0] == 4 and e[1] != empty_spot }
  # it returns the first four detected
end

现在调用grid_map上的方法,以获得连接的四个的计数,颜色和坐标:

diagonals(grid_map).map { |diagonal| check_diagonal(diagonal) }.compact
#=> [[4, "B", [[0, 0], [1, 1], [2, 2], [3, 3]]], [4, "X", [[3, 4], [4, 5], [5, 6], [6, 7]]]]

由于check.detect,四个"M"没有返回。

相关问题