如何实现检查4个图块的水平、垂直和对角线的功能?

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

我正在编写一个四子棋游戏,您可以在其中选择棋盘的大小。该游戏适用于大多数棋盘尺寸,但当棋盘比宽高时就会出现问题。我不断收到索引超出范围的错误,我不确定我做错了什么。

这就是我现在的检查功能,因为它是唯一给我带来问题的部分。

def checkOWin(board):
    
    boardHeight = len(board)
    boardWidth = len(board[0])
    tile = 'O'
    # check horizontal spaces
    for y in range(boardHeight):
        for x in range(boardWidth - 3):
            if board[x][y] == tile and board[x+1][y] == tile and board[x+2][y] == tile and board[x+3][y] == tile:
                return True
    
    # check vertical spaces
    for x in range(boardWidth):
        for y in range(boardHeight - 3):
            if board[x][y] == tile and board[x][y+1] == tile and board[x][y+2] == tile and board[x][y+3] == tile:
                return True
    
    # check / diagonal spaces
    for x in range(boardWidth - 3):
        for y in range(3, boardHeight):
            if board[x][y] == tile and board[x+1][y-1] == tile and board[x+2][y-2] == tile and board[x+3][y-3] == tile:
                return True
    
    # check \ diagonal spaces
    for x in range(boardWidth - 3):
        for y in range(boardHeight - 3):
            if board[x][y] == tile and board[x+1][y+1] == tile and board[x+2][y+2] == tile and board[x+3][y+3] == tile:
                return True
    
    return False
python
3个回答
39
投票

虽然连续嵌套的 for 循环是胜利检测的明显解决方案,但在 Python 等语言中,这是一种相当慢的方法。这个问题实际上可以类比为 Connect 4 板二维上的卷积运算,卷积核设计用于匹配 4 个图块的水平、垂直和对角线。

因此,更快的方法是:

  1. 创建用于水平、垂直和对角线胜利检测的内核。

    horizontal_kernel = np.array([[ 1, 1, 1, 1]])
    vertical_kernel = np.transpose(horizontal_kernel)
    diag1_kernel = np.eye(4, dtype=np.uint8)
    diag2_kernel = np.fliplr(diag1_kernel)
    detection_kernels = [horizontal_kernel, vertical_kernel, diag1_kernel, diag2_kernel]
    
  2. 从您的棋盘创建一个 2D 数组,其中玩家的所有图块都设置为 1,所有空/对手图块都设置为 0。

  3. 使用 SciPy 高度优化的 convolve2d 函数通过卷积运算运行电路板。

  4. 在卷积输出形成的数组中,任何“4”都表示棋盘中有4个相连的图块。

    from scipy.signal import convolve2d
    
    def winning_move(board, player):
        for kernel in detection_kernels:
            if (convolve2d(board == player, kernel, mode="valid") == 4).any():
                return True
        return False
    

这可以大大加快获胜条件的检测速度,这在游戏树上实现类似树搜索的算法时至关重要。我还发现这个解决方案更加优雅和可读。


7
投票

您刚刚混淆了尺寸,您应该这样设置它们:

def checkOWin(board):
    boardHeight = len(board[0])
    boardWidth = len(board)

因为当你引用 board[x] 时,它是在计算板中列表的数量,而当你引用 board[x][y] 时 那只是指板中某一特定行的长度。

if board[x][y] == tile and board[x+1][y] == tile and board[x+2][y] == tile and board[x+3][y] == tile:

当我翻转这些值时,函数运行没有错误。


4
投票
我认为问题出在条件上。

这是一个示例输入,应该与 OP 使用的类似:

board = [['_','_','_','_','_','_','_'], ['_','_','_','_','_','_','_'], ['_','_','_','X','_','_','_'], ['_','_','_','O','_','_','_'], ['_','X','X','O','O','O','O'], ['X','X','X','O','O','X','O']]
我将条件重新修改为:

for x in range(boardWidth): for y in range(boardHeight): try: if board[y][x] == tile and board[y][x+1] == tile and board[y][x+2] == tile and board[y][x+3] == tile: return True except IndexError: next
我们在看板列表中存储了 6 个不同的列表。访问面板时,y 首先出现,因为它首先告诉我们需要使用这 6 个列表中的哪一个列表,从而在面板列表中向上或向下移动。现在 x 索引将我们移动到我们访问过的任何 y 列表中。

尝试和例外允许您删除

for x in range(boardWidth - 3)

,因为如果收到索引错误,我们就知道我们已经到达游戏板的边缘并且无法满足获胜条件。所以我们继续进行下一点测试。

© www.soinside.com 2019 - 2024. All rights reserved.