我一直在軟件開發者訪談中使用這個問題的一個變種,所以我已經考慮了這個問題。這裏有一個更好的答案:它可以處理任意數量的玩家,任何平方井字遊戲網格和任何「遊程大小」。該方法非常簡單,提供了有關所有找到的序列的信息,並且是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