我正在制作一个递归的井字游戏搜索播放器,并且有一个名为 board 的对象,每次我模拟一个新的棋盘时都会创建它。但是,原始板正在被未来的板覆盖。我不确定这是怎么发生的。
我有下面重要的代码。发生的事情是这样的: 我启动 ai 播放器并调用 getMove(),它应该返回一个数字。 在 getMove 中,我启动了一个递归 ai,它在启动时开始构建每个棋盘状态的数组。该列表是全局的,因此 build frontier 不会返回任何内容。它只说“如果这是一个结束状态,x/o/tie,然后将它添加到列表中。否则,制作一个新的 ai 播放器,这个位置已填充并检查结束状态,等等。没有在哪里它引用了 aiPlayer.board,但不知何故它被覆盖了。
当 ai.getMove 返回一个随机移动时,(找到棋盘上的所有点并从这些点中随机返回一个)线
print("the game board this sees")
self.board.printBoard()
打印我第一次调用 ai 播放器时输入的棋盘。如果我标记一个点,然后调用 ai 来决定下一个点,这个打印函数会在我标记一个点之后但在它被填充之前打印板。这很好。
然而,当使用实际算法时,同一行打印一个填充板。我从不触及 ai 的 self.board 对象来改变它,我只复制 c_board 对象并从中构建迭代。
我的问题是:虽然我从来没有碰过self.board,为什么它被覆盖了?我稍后制作的 c_boards 是指向同一个原始板的指针吗?到底是怎么回事? (PS. 很抱歉称它为 ai,这只是一次蛮力尝试。我试图在没有任何其他人方法教程的情况下了解我是如何思考和实施它的。{但我一直在寻找解决方案来解释代码的原因是越野车,我的第一个答案不是转向这个论坛。谢谢你的帮助!})
global overallFrontier
overallFrontier = []
class aiPlayer:
def __init__(self, field, currentTurn,firstPlayer,width,path,aiMode):
self.board = c_board(False,field,width,path,currentTurn) # <-- this board is getting overwritten
self.mode = aiMode
self.firstPlayer = firstPlayer
overallFrontier = []
def getMove(self):
self.bestTileNum = self.smartSearchGetMove()
print("getMove: ", self.bestTileNum)
print("the game board this sees")
self.board.printBoard() # <-- When I print this, I don't see the original board.
#instead, I see a hypothetical board created later
#return
return self.bestTileNum #int
def smartSearchGetMove(self):
recurse_buildFrontier(self.board.field,self.board.width,self.board.path,self.board.currentTurn,0)
return bestNumber #any number 1-9, the implementation isn't where my issue is
#Sorry about the weird indenting, I can't hit tab. The actual code # lines up properly
class recurse_buildFrontier:
def __init__(self,field,width,path,currentTurn,iteration):
print("RecurseAI: ", iteration)
self.board = c_board(False,field,width,path,currentTurn)
self.iteration = iteration
self.buildFrontier()
def buildFrontier(self):
print("================ BuildFrontierCall =====================")
print("iteration: ", self.iteration + 1)
print("frontier: ", overallFrontier)
for row in self.board.field:
for spot in row:
if(spot in range(1,11) and spot not in self.board.path):#the not path bit is implied, just for clarity its written
spotFrontierBoard = c_board(False,self.board.field,self.board.width,self.board.path,not self.board.currentTurn)
spotFrontierBoard.fillBoard(spot)
print("-- spot: ", spot)
print("! spotFrontierBoard print !")
spotFrontierBoard.printBoard()
print("! self board print !")
self.board.printBoard()
spotFrontierBoard.evaluatePosition()
evaluate = spotFrontierBoard.winState
evaluatePrinter = "X" if evaluate == c_board.winType["x"] else "O" if evaluate == c_board.winType["o"] else "TIE" if evaluate == c_board.winType["tie"] else "none" if evaluate == c_board.winType["none"] else "evaluate error"
print("evaluate: ",evaluatePrinter )
if (evaluate in [c_board.winType["x"],c_board.winType["o"],c_board.winType["tie"]]):
overallFrontier.append(spotFrontierBoard)
else:
recurse_buildFrontier(spotFrontierBoard.field,self.board.width,spotFrontierBoard.path,not self.board.currentTurn,self.iteration+1).buildFrontier()
print("++++++++++ END BuildFrontierCall +++++++++++")
c_board类的代码是:
from math import trunc
global boardWidth
boardWidth = 0
class c_board:
firstPlayer = True
winType = {
"x": 0,
"tie": 2,
"o": 4,
"none": -2,
}#first player win type
def __init__(self,isNewField,ifNewFieldThisIsEmptyListElseThisIsBoardField,widthZEROIfNewBoard,path,newTurnNumber):
self.path = path
self.currentTurn = newTurnNumber #not lastTurn
boardWidth = widthZEROIfNewBoard
if boardWidth == 0:
self.width = self._getBoardSize()
else:
self.width = boardWidth
if isNewField:
self.field = self.newField()
else:
self.field = ifNewFieldThisIsEmptyListElseThisIsBoardField
self.winState = self.evaluatePosition()
def printBoard(self):
spotNum = 1
for column in self.field:
line = ""
spotNum = 1
for val in column:
if(val == "x" or val == "o"):
val = val.upper()
line += str(val)
if(spotNum != 3):
line += " | "
spotNum +=1
print(" ",line)
print("path: ",self.path)
def evaluatePosition(self):
#returns either 0 1 or 2 depending on the winType(loose tie or win)
hitNum = False
for i in self.field:
for j in i:
for k in range(self.width * self.width):
if (str(j) == str(k)):
hitNum = True
# x1 x2 x3 o1 o2 o3
downXO = [[0, 0, 0], [0, 0, 0]]
for column in self.field:
#count accross
if(column.count("x") == self.width):
print("x, accross")
self.winState = self.winType["x"]
return self.winState
elif(column.count("o") == self.width):
print("o, accross")
self.winState = self.winType["o"]
return self.winState
for i in range(self.width):
if (column[i] == "x" or column[i] == "o"):
#count down
xo = 0 if (column[i] == "x") else 1
downXO[xo][i] += 1
#down win
if (downXO[xo][i] == self.width):
if(not bool(xo) == self.currentTurn): #In xo, 0 is x, whereas everywhere else True(num>0) is x
print("down x")
self.winState = self.winType["x"]
return self.winState
else:
print("down o")
self.winState = self.winType["o"]
return self.winState
#count diagonal tl to br
centerTile = self.field[trunc(self.width / 2)][trunc(self.width / 2)]
if (str(centerTile) not in [
"1", "2", "3", "4", "5", "6", "7", "8"
]): #TODO: make this a loop iterable generated by the boardsize
xo = 0 if (centerTile == "x") else 1
#this first i loop section does top left to bottom right, as x and y positions are the same number in this order
Lxo = [0, 0]
for i in range(self.width):
if (
Lxo[0] > 0 and Lxo[1] > 0
): #if the diagonal is already not pure, aka there are both x and o on the diagonal
break
if (self.field[i][i] == centerTile):
Lxo[xo] += 1
if (Lxo[xo] == self.width):
if(not bool(xo) == self.currentTurn):
print("tl acc x")
self.winState = self.winType["x"]
return self.winState
else:
print("tl acc o")
self.winState = self.winType["o"]
return self.winState
Rxo = [0, 0]
rlistCoords = list(zip(range(self.width), range(self.width - 1, -1, -1)))
for item in rlistCoords:
if (self.field[item[0]][item[1]] == centerTile):
Rxo[xo] += 1
if (Rxo[xo] == self.width):
if(not bool(xo) == self.currentTurn):
self.winState = self.winType["x"]
return self.winState
else:
self.winState = self.winType["o"]
return self.winState
if (hitNum == False):
self.winState = self.winType["tie"]
return self.winState
self.winState = self.winType["none"]
return self.winState
def fillBoard(self,tile):
i = 0
j = 0
for column in self.field:
for val in column:
if (val == tile):
self.field[i][j] = ("x" if (self.currentTurn == self.firstPlayer) else "o")
self.currentTurn = not self.currentTurn
self.path.append(tile)
return self.field
j += 1
j = 0
i += 1
return False
def resetBoard(self):
self.field = self.newField()
self.winState = self.winType["none"]
self.currentTurn = True
self.path = []
def newField(self):
self.field = [[int((1 + i + self.width * j)) for i in range(self.width)]
for j in range(self.width)]
return self.field
def _getBoardSize(self):
#in init, replace width from const to self.getBoardSize()
while (True):
try:
boardSize = int(input("board square size? 3 is best, odd only: "))
if (boardSize % 2 == 1):
break
except:
continue
#return boardSize #have some issues with larger boards, gotta fix and then this is possible
return boardSize
以防万一,这是 main.py
#V3 might be working better then v2. All changes are in the build frontier
import sys, os, time
from ai import *
from board import *
# minimax version
#features for the future:
# - use pygaem to display (might not work on person running code's computer)
# - have winning image sequence
# - when there are no possible moves that can win, tie
class TicTacToeGame:
validationInputs = {
"y": True,
"yes": True,
"sure": True,
"of course": True,
"definatly": True,
"affirmative": True,
"yup": True,
"ya": True,
"yuh": True,
"certainly": True,
"n": False,
"no": False,
"nope": False,
"nadda": False,
"never": False,
}
def __init__(self):
self.board = c_board(True,[],0,[],True)
print("press anything to start a game")
input("> ")
self.getPlayMode()
self.startGame()
def startGame(self):
#os.system("clear")
self.board.resetBoard()
self.playGame()
def playGame(self):
endGameStates = [c_board.winType["x"],c_board.winType["o"],c_board.winType["tie"]]
self.display()
if (self.board.winState in endGameStates):
valid = False
while (not valid):
try:
print(" continue on same game mode: 1")
print(" try playing a different gamemode: 2")
try:
answer = int(input(">"))
except:
raise Exception("not a number")
if (answer == 2):
valid = True
self.getPlayMode()
elif (answer == 1):
valid = True
else:
valid = False
raise Exception("answer is not 1 or 2")
except Exception as e:
print(e)
# os.system("clear")
self.startGame()
elif(self.board.winState == c_board.winType["none"]):
if (self.playAi == False):
self.playerMark()
else:
if (self.playerFirst == self.board.currentTurn):
print("player")
self.playerMark()
else:
print("ai")
aiMove = aiPlayer(self.board.field,self.board.currentTurn,self.playerFirst,self.board.width,[],self.aiMode).getMove()
print('aiMove is', aiMove)
self.board.fillBoard(aiMove)
self.board.winState = self.board.evaluatePosition()
print("wS: ",self.board.winState)
self.playGame()
def printWinner(self):
if (self.playAi):
if((self.playerFirst == self.board.currentTurn)):
print("you win")
else:
print("ai wins")
elif(not self.playAi):
if(self.board.currentTurn):
print("p1 wins")
else:
print("p2 wins")
def display(self):
#os.system("clear")
self.board.printBoard()
print(self.board.path)
if(self.board.winState == c_board.winType["tie"]):
print("tie")
elif(self.board.winState == c_board.winType["x"] or self.board.winState == c_board.winType["o"]):
self.board.currentTurn = not self.board.currentTurn
self.printWinner()
self.board.newField()
def playerMark(self): #either x or o
self.foundTile = False
valid = False
while(not valid):
try:
answer = input("tileNum: ")
if(answer == "e"):
self.resetScreen()
self.board.newField()
break
tileNum = int(answer)
for row in self.board.field:
for val in row:
if val == tileNum:
valid = True
if(not valid):
raise Exception("no tile found")
except Exception as e:
print(e)
self.board.fillBoard(tileNum)
def resetScreen(self):
while(True):
print("sit here to relax")
time.sleep(1)
print("we are zen")
time.sleep(1)
print("press enter to start a new game")
time.sleep(1)
print("but take your time")
input(">")
self.startGame()
def getPlayMode(self):
self.board.resetBoard()
allValid = False
while (allValid == False):
try:
print("play against AI? y/n")
answer = input("> ").lower().strip()
if (answer not in self.validationInputs):
raise Exception("not a valid input")
self.playAi = self.validationInputs[answer]
if (not self.playAi):
allValid = True
break
print("AI Mode: 1) next!")
print(" 2) random!")
print(" 3) Smart")
answer = input(">").strip()
if(answer not in ["1","2","3"]):
raise Exception("not a valid input")
self.aiMode = int(answer)
print("play first? y/n")
answer = input("> ").lower().strip()
if (answer not in self.validationInputs):
raise Exception("not a valid input")
self.playerFirst = self.validationInputs[answer]
allValid = True
except Exception as e:
print(e)
def main():
global game
game = TicTacToeGame()
main()
并且(可能是不必要的)这是整个计算机播放器代码,而不是我选择显示的内容:
from board import *
import random
global overallFrontier
overallFrontier = []
global listOfAiPlayers
listOfAiPlayers = []
class aiPlayer:
def __init__(self, field, currentTurn,firstPlayer,width,path,aiMode):
self.board = c_board(False,field,width,path,currentTurn)
self.mode = aiMode
self.firstPlayer = firstPlayer
overallFrontier = []
def getFirstOpenSpotFromFieldForBasicCompPlayer(self):
for row in self.board.field:
for item in row:
if(item not in ["x","o"]):
return item
def playRandomMove(self):
listOfSpaces = []
for row in self.board.field:
for item in row:
if(item not in ["x","o"]):
listOfSpaces.append(item)
if(len(listOfSpaces)>0):
return random.choice(listOfSpaces)
else:
return
def getMove(self):
if(self.mode == 1):
self.bestTileNum = self.getFirstOpenSpotFromFieldForBasicCompPlayer()
if(self.mode == 2):
self.bestTileNum = self.playRandomMove()
if(self.mode == 3):
self.bestTileNum = self.smartSearchGetMove()
print("getMove: ", self.bestTileNum)
print("the game board this sees")
self.board.printBoard()
#return
return self.bestTileNum #int
#TODO: after the best case is found, return the first number that leads down that case
def smartSearchGetMove(self):
recurse_buildFrontier(self.board.field,self.board.width,self.board.path,self.board.currentTurn,0)
print(" ***** ")
print(" ")
print(" ")
for board in overallFrontier:
print(board)
print(" ")
print(" ")
print(" ")
print(" ***** ")
return self.playRandomMove()
"""for ai in listOfAiPlayers:
print("ai address: ", ai)
print("Final frontier: ")
for board in overallFrontier:
board.printBoard()
if(not self.board.currentTurn):
print("Optimize for X")
else:
print("Optimize for O")
"""
#TODO: optimizedBoard.path
class recurse_buildFrontier:
def __init__(self,field,width,path,currentTurn,iteration):
print("RecurseAI: ", iteration)
self.board = c_board(False,field,width,path,currentTurn)
self.iteration = iteration
self.buildFrontier()
def buildFrontier(self):
print("================ BuildFrontierCall =====================")
print("iteration: ", self.iteration + 1)
print("frontier: ", overallFrontier)
for row in self.board.field:
for spot in row:
if(spot in range(self.board.width * self.board.width) and spot not in self.board.path):#the not path bit is implied, just for clarity its written
spotFrontierBoard = c_board(False,self.board.field,self.board.width,self.board.path,not self.board.currentTurn)
spotFrontierBoard.fillBoard(spot)
print("-- spot: ", spot)
print("! spotFrontierBoard print !")
spotFrontierBoard.printBoard()
print("! self board print !")
self.board.printBoard()
spotFrontierBoard.evaluatePosition()
evaluate = spotFrontierBoard.winState
evaluatePrinter = "X" if evaluate == c_board.winType["x"] else "O" if evaluate == c_board.winType["o"] else "TIE" if evaluate == c_board.winType["tie"] else "none" if evaluate == c_board.winType["none"] else "evaluate error"
print("evaluate: ",evaluatePrinter )
if (evaluate in [c_board.winType["x"],c_board.winType["o"],c_board.winType["tie"]]):
overallFrontier.append(spotFrontierBoard)
else:
recurse_buildFrontier(spotFrontierBoard.field,self.board.width,spotFrontierBoard.path,not self.board.currentTurn,self.iteration+1).buildFrontier()
print("++++++++++ END BuildFrontierCall +++++++++++")
中的问题
recurse_buildFrontier类中的这段代码显示:
self.board 应该看起来像迭代板之前的移动,因为迭代板正在探索 self.board 中的每个开放点。迭代板取一个点并填充它,但两者都打印出相同的东西。
调用并进行此更改的 fillBoard 函数仅引用 self.field
def fillBoard(self,tile):
i = 0
j = 0
for column in self.field:
for val in column:
if (val == tile):
self.field[i][j] = ("x" if (self.currentTurn == self.firstPlayer) else "o")
self.currentTurn = not self.currentTurn
self.path.append(tile)
return self.field
j += 1
j = 0
i += 1
return False