如果您认为玩家轮流进行,当有人获胜时游戏停止,则可能的棋盘数将少于 X、O 和空白单元格的最大组合所建议的数量。您也不能计算填充了所有 X 或所有 Os 或任何 X 和 Os 数量之差大于 1 的组合的板。
您可以通过递归模拟从 X 开始到 O 开始的移动来获得该数字:
axes = [(0,1,2),(3,4,5),(6,7,8),(0,3,6),(1,4,7),(2,5,8),(0,4,8),(2,4,6)]
def isWin(board):
return any("".join(board[p] for p in axis) in ["XXX","OOO"] for axis in axes)
def validBoards(board="."*9,player=None):
if player == None:
yield board # count the empty board
for b in validBoards(board,player="X"): yield b # X goes 1st
for b in validBoards(board,player="O"): yield b # O goes 1st
return
opponent = "XO"[player=="X"]
for pos,cell in enumerate(board):
if cell != ".": continue
played = board[:pos]+player+board[pos+1:] # simulate move
yield played # return the new state
if isWin(played): continue # stop game upon winning
for nextBoard in validBoards(played,opponent):
yield nextBoard # return boards for subsequent moves
输出:
distinctBoards = set(validBoards()) # only look at distinct board states
allStates = len(distinctBoards)
print(allStates) # 8533 counting all intermediate states
winningStates = sum(isWin(b) for b in distinctBoards)
print(winningStates) # 1884 (so 942 for a specific starting player)
filledStates = sum(("." not in b) for b in distinctBoards)
print(filledStates) # 156 states where all cells are filled
finalStates = sum(isWin(b) or ("." not in b) for b in distinctBoards)
print(finalStates) # 1916 end of game states (win or draw)
earlyWins = sum(isWin(b) and ("." in b) for b in distinctBoards)
print(earlyWins) # 1760 wins before filling the board
draws = finalStates - winningStates
print(draws) # 32 ways to end up in a draw
lastWins = filledStates-draws
print(lastWins) # 124 wins on the 9th move (i.e filling the board)
fastWins = sum( isWin(b) and b.count(".") == 4 for b in distinctBoards)
print(fastWins) # 240 fastest wins by 1st player (in 3 moves)
fastCounters = sum( isWin(b) and b.count(".") == 3 for b in distinctBoards)
print(fastCounters) # 296 fastest wins by 2nd player (in 3 moves)
如果您需要更快的实现,这里有一个优化版本的函数,它只返回不同的状态并利用它来跳过移动序列树的整个分支:
def validBoards(board="."*9,player=None,states=None):
if player == None:
result = {board} # count the empty board
result |= validBoards(board,player="X",states=set()) # X goes 1st
result |= validBoards(board,player="O",states=set()) # O goes 1st
return result
opponent = "XO"[player=="X"]
for pos,cell in enumerate(board):
if cell != ".": continue
played = board[:pos]+player+board[pos+1:] # simulate move
if played in states : continue # skip duplicate states
states.add(played) # return the new state
if isWin(played): continue # stop game upon winning
validBoards(played,opponent,states) # add subsequent moves
return states