连接4个对角线赢支票

时间:2018-12-09 07:05:49

标签: arrays ruby multidimensional-array

我正在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

编辑后添加: 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

4 个答案:

答案 0 :(得分:3)

假设我们有

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,否则返回{{1 }}。

请注意,此方法不需要nilarr.size == grid.size的所有元素都具有相同的大小。它仅检查是否有任何元素连续有四个arr或四个'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> 可以被视为化合物枚举器,尽管Ruby对此没有定义。

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

enum1

请参见Enumerable#each_cons

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

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 ,然后应用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

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

我们可以进行与计算对角线相同的推理,但是由于此处的计算效率并不重要,因此有一种更简单的方法:计算通过“ (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 ”旋转90度获得的数组的对角线。

grid

将它们放在一起

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"

答案 1 :(得分:1)

如果有获胜对角线,则起始位置必须在第[0-3]行和第[0-3]行的范围内。从左上角框开始的任何对角线都没有足够的左右位置来使其连续排成四个。

所以实际上您需要一个while row < 4while col < 4嵌套循环。

对于每个行col组合,假定值不是“。”。然后,您可以将piece_count设置为1,然后将计数器1设置为3,并检查playing_board[row + counter][col + counter]是否等于playing_board[row][col]的值,以及是否增加了piece_count。

在计数器1到3的循环之外,如果piece_count为4,则您有一个获胜者。

答案 2 :(得分:1)

我想出了一个似乎可以在测试中起作用的解决方案。它从右下角到左上角,从我的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

答案 3 :(得分:0)

我当时正在用类似的网格玩填字游戏,并提出了一种获取对角线的方法。我做了些改动以适应连接4。 这不是一个完整的答案,但我希望它能有所帮助。

首先,我想将网格映射为也具有坐标:

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

这将对矩阵的转换调用half_diagonals(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]]]]

由于"M",未返回四个check.detect