我一直在软件开发者访谈中使用这个问题的一个变种,所以我已经考虑了这个问题。这里有一个更好的答案:它可以处理任意数量的玩家,任何平方井字游戏网格和任何“游程大小”。该方法非常简单,提供了有关所有找到的序列的信息,并且是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}
检查所有组合可以是矫枉过正,但它仍然是在O(M×N个)可行的,因为任何给定的正方形是不超过恒定数量的可能的一员赢得的弦乐。此外,这种检查很可能会花费足够长的时间才能被用户察觉。话虽如此,你可以打败这是你注意的举动,如特奥多所言。 – Brian 2010-07-22 16:55:42
@Brian,Teodore。同意,在传统的tic tac toe游戏中,只需要检查选定的方块可能取得的胜利就会更有意义。我实际上使用这个来试图确定Xs(no Os)的最大数量,它可以在没有胜利的情况下在板上。我正在迭代填充板(MxN位二进制数)并确定它是否成功。我认为井字问题会给我一些算法思想,而不必解释太多。 – Jonathan 2010-07-22 17:04:36
在这种情况下,Teodor的建议将会奏效,尽管您可以通过跳过八个方向中的三个方向的测试来稍微提高速度。顺便说一下,我的直觉是,通过从一个完整的xs板开始并尽可能少地移除,可能更容易解决这个问题。 – Brian 2010-07-22 17:26:31