在Python 2d数组中连续确定三个

时间:2010-07-22 16:31:55

标签: python arrays

我正在使用Python中的M x N板进行井字游戏。我正试图找到一种有效的方法来确定一个玩家是否赢了(连续三个方向是垂直,水平或对角线方向。)大多数3x3的游戏实现只是在每个回合后检查所有可能的获胜组合。对于庞大的董事会来说,这似乎有点极端。

4x4示例:(使用1和2而不是X和Os)

board = ([1,0,2,1], [0,0,0,1], [2,2,0,0], [1,0,0,1])
for row in board:
    print row

Thanks- 乔纳森

5 个答案:

答案 0 :(得分:3)

您可以查看玩家的移动是否关闭游戏(查看该行,该列和2个对角线,如果它们连续检查),它的o(x)复杂度。让我们说你正在寻找那一行,看看他是否赢了。向左看,有多少连续检查是在右边。如果他们的总和超过x他赢了。您将在列和对角线上执行相同的操作。

答案 1 :(得分:3)

虽然这种方法具有一定的吸引力,但可能并不是特别快。

# A bogus game with wins in several directions.
board = (
    [1,1,2,1],
    [0,2,1,1],
    [2,2,2,1],
    [1,0,0,1],
)

# A few convenience variables.    
n_rows = len(board)    
lft = [ [0] * i for i in range(n_rows) ]  # [[], [0], [0, 0], [0, 0, 0]]
rgt = list(reversed(lft))

# Create transpositions of the board to check for wins in various directions.
transpositions = {
    'horizontal' : board,
    'vertical'   : zip(*board),
    'diag_forw'  : zip(* [lft[i] + board[i] + rgt[i] for i in range(n_rows)] ),
    'diag_back'  : zip(* [rgt[i] + board[i] + lft[i] for i in range(n_rows)] ),
}

# Apply Jonathan's horizontal-win check to all of the transpositions.
for direction, transp in transpositions.iteritems():
    for row in transp:
        s = ''.join( map(str, row) )
        for player in range(1,3):
            if s.find(str(player) * 3) >= 0:
                print 'player={0} direction={1}'.format(player, direction)

输出:

player=1 direction=diag_back
player=2 direction=diag_forw
player=2 direction=horizontal
player=1 direction=vertical

对角线换位背后的想法是使用lftrgt左右填充来移动行。例如,diag_forw列表在添加填充后看起来像这样(填充字符显示为句点,即使在实际代码中使用了零)。

1 1 2 1 . . .
. 0 2 1 1 . .
. . 2 2 2 1 .
. . . 1 0 0 1 

然后我们使用zip(*foo)转换该数组,这样我们就可以使用Jonathan的好主意来寻找横向胜利。

答案 2 :(得分:1)

检查横向胜利

for row in board:
    rowString = ''.join(row)
    if(rowString.count('111') > 2 or rowString.count('222') > 2):
        print "Somebody won"

检查垂直获胜

for col in xrange(len(board[0])):
    colString = ""
    for row in board:
        colString = colString.append(row[col])
    if(colString.count('111') > 2 or colString.count('222') > 2):
        print "Somebody won"

仍然难以对角线......

答案 3 :(得分:1)

如果您的电路板设置如下:

board = 
([1,0,2,0],
 [0,1,2,0],
 [0,0,0,0],
 [0,0,0,0])

您可以将它想象为x和y坐标,从左上角开始,向下移动为正y,向右移动为正x。任何一名球员在board[3][3]的移动都将是一场胜利之举。使用Teodor Pripoae工艺,我们可以围绕最后一步移动构建水平,垂直和对角线。水平情况很容易。

def horizontal(board, y_coord):
    return board[y_coord]

垂直情况要求我们从每一行中选择x_coord:

def vertical(board, x_coord):
    return [row[x_coord] for row in board]

对角线的情况有点棘手。对于第一个函数,它计算从上到下的从左到右的对角线。当y等于零时,距离基本上表示从零开始的水平距离。

def diagonal1(board, x_coord, y_coord):
    length = len(board[0])
    distance = x_coord - y_coord
    if distance >= 0:
        return [y[x] for x, y in enumerate(board) if distance + x <= length]
    else:
        return [y[x] for x, y in enumerate(board) if x - distance >= 0 and x - distance <= length]

第二个函数计算从上到下从右到左的对角线。在此函数中,距离表示从零开始的垂直距离,因为水平距离为零。

def diagonal2(board, x_coord, y_coord):
    length = len(board[0])
    distance = y_coord + x_coord
    return [y[distance - x] for x, y in enumerate(board) if distance - x <= length]

一旦你定义了这些,你只需要一种方法来检查玩家是否赢了。这样的事情可能会这样:

def game_over(direction, number_to_win, player_number):
    count = 0
    for i in direction:
        if i == player_number:
            count += 1
            if count = number_to_win:
                return True
        else:
            count = 0
    return False

写完所有这些之后,似乎这有点矫枉过正,除非你有相当大的M和N.虽然它可能比检查每个胜利条件更有效,但它确实构建了整个水平,垂直和对角线方向,而不是不仅仅是那些围绕最后一步的坐标,它还没有那么高效。

也许这很有帮助,但看起来Brian建议简单地删除x可能会更好。

答案 4 :(得分:0)

我一直在软件开发者访谈中使用这个问题的变体,所以我已经考虑了这个问题。这是一个更好的答案:它可以处理任意数量的玩家,任何方形的tic-tac-toe网格和任何“跑步大小”。方法相当简单,提供了所有序列的信息,并且是O(N),其中N是细胞数。

# Given a square tic-tac-toe grid of any size, with any number of players, find
# all sequences (horizontal, vertical, diagonal) of some minimum size.

def main():
    raw_grid = [
        [1, 1, 2, 1, 0],  # Zero means open spot.
        [0, 2, 1, 1, 1],
        [2, 2, 2, 1, 2],
        [1, 0, 1, 1, 2],
        [1, 0, 0, 0, 2],
    ]
    for run in get_runs(raw_grid, 3):
        print run

def get_runs(raw_grid, run_size):
    # Offsets to find the previous cell in all four directions.
    offsets = {
        'h' : ( 0, -1), # _
        'v' : (-1,  0), # |
        'f' : (-1,  1), # /
        'b' : (-1, -1), # \
    }

    # Helpers to check for valid array bounds and to return a new cell dict.
    size      = len(raw_grid)
    in_bounds = lambda r, c: r >= 0 and c >= 0 and r < size and c < size
    new_cell  = lambda i, j, p: dict(h=1, v=1, f=1, b=1, i=i, j=j, player=p)

    # Use the raw grid to create a grid of cell dicts.
    grid = []
    for i, row in enumerate(raw_grid):
        grid.append([])
        for j, player in enumerate(row):
            # Add a cell dict to the grid (or None for empty spots).
            cell = new_cell(i, j, player) if player else None
            grid[i].append(cell)
            if not cell: continue

            # For each direction, look to the previous cell. If it matches the
            # current player, we can extend the run in that direction.
            for d, offset in offsets.iteritems():
                r, c = (i + offset[0], j + offset[1])
                if in_bounds(r, c):
                    prev = grid[r][c]
                    if prev and prev['player'] == cell['player']:
                        # We have a match, so the run size is one bigger,
                        # and we will track that run in the current cell,
                        # not the previous one.
                        cell[d] = prev[d] + 1
                        prev[d] = None

    # For all non-None cells, yield run info for any runs that are big enough.
    for cell in (c for row in grid for c in row if c):
        for d in offsets:
            if cell[d] and cell[d] >= run_size:
                yield dict(
                    player    = cell['player'],
                    endpoint  = (cell['i'], cell['j']),
                    direction = d,
                    run_size  = cell[d],
                )

main()

输出:

{'player': 1, 'direction': 'h', 'endpoint': (1, 4), 'run_size': 3}
{'player': 2, 'direction': 'f', 'endpoint': (2, 0), 'run_size': 3}
{'player': 2, 'direction': 'h', 'endpoint': (2, 2), 'run_size': 3}
{'player': 1, 'direction': 'b', 'endpoint': (2, 3), 'run_size': 3}
{'player': 1, 'direction': 'f', 'endpoint': (3, 2), 'run_size': 3}
{'player': 1, 'direction': 'v', 'endpoint': (3, 3), 'run_size': 4}
{'player': 2, 'direction': 'v', 'endpoint': (4, 4), 'run_size': 3}