我正在使用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- 乔纳森
答案 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
对角线换位背后的想法是使用lft
和rgt
左右填充来移动行。例如,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}