TicTacToe And Minimax
Solution 1:
Change this part, your implementation will return optimalMove
even if it doesn't go inside the if statement
, and optimalMove
will not be assigned at that point, so put the return
inside.
if score > sampleScore:
sampleScore = score
optimalMove = i
return optimalMove
Solution 2:
optimalMove = 0
in play()
and optimalMove = i
in findOptimalField()
are declaring two distinct variables, each of which is local to the function declaring it.
If you want multiple functions to have access to the same variable, you can use the global keyword, but that's generally considered a bad practice. It can make it hard to reason about the code (e.g. is var = x
creating a new local variable or overwriting the value of a global?) and it doesn't stop you from accidentally using a variable before it's declared.
Since you're coming from a Java background, you can turn this into a class to get behavior more like what you expect, eliminating the need for globals:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.score = 0
self.playerSymbol = None
self.aiSymbol = None
...
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
...
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
...
Each method now takes an explicit self
argument that refers to the current instance, and you can use this to access any variables that belong to the class instance instead of a particular method. If you don't include self.
before a variable, that variable will still be local to the method that declares it. In this case, the drawBoard()
method won't be able to access the answer
variable defined in choice()
.
You can create new self.
variables in any of the class's methods, but the best practice is to initialize all of them in the __init__
constructor method, using None
as a placeholder for variables that don't have a value yet.
Solution 3:
I am posting this as an answer, just in case somebody in the future stumbles upon the same problem :)
the main issue i encountered (besides my bad programming style) is that i forgot to update the contents the lists playerPosition and aiPosition. You can review the rest of the changes in the working code:
class TicTacToe:
def __init__(self):
self.board = [" ", " ", " ", " ", " ", " ", " ", " ", " "]
self.playerSymbol = ""
self.playerPosition = []
self.aiSymbol = ""
self.aiPosition = []
self.winner = None
self.scoreBoard = None
self.turn = 0
self.optimalMove = int()
def drawBoard(self):
print(self.board[0] + " | " + self.board[1] + " | " + self.board[2])
print("___" + "___" + "___")
print(self.board[3] + " | " + self.board[4] + " | " + self.board[5])
print("___" + "___" + "___")
print(self.board[6] + " | " + self.board[7] + " | " + self.board[8])
def choice(self):
answer = input("What do you want to play as? (type x or o) ")
if answer.upper() == "X":
self.playerSymbol = "X"
self.aiSymbol = "O"
else:
self.playerSymbol = "O"
self.aiSymbol = "X"
self.scoreBoard = {
self.playerSymbol: -1,
self.aiSymbol: 1,
"tie": 0
}
def availableMoves(self):
moves = []
for i in range(0, len(self.board)):
if self.board[i] == " ":
moves.append(i)
return moves
def won_print(self):
self.won()
if self.winner == self.aiSymbol:
print("AI wins :(")
exit(0)
elif self.winner == self.playerSymbol:
print("Player Wins :)")
exit(0)
elif self.winner == "tie":
print("Guess it's a draw")
exit(0)
def won(self):
winningPositions = [{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 4, 8}, {2, 4, 6}, {0, 3, 6},
{1, 4, 7}, {2, 5, 8}]
for position in winningPositions:
if position.issubset(self.playerPosition):
self.winner = self.playerSymbol
return True
elif position.issubset(self.aiPosition):
self.winner = self.aiSymbol
return True
if self.board.count(" ") == 0:
self.winner = "tie"
return True
self.winner = None
return False
def set_i_ai(self, i):
self.aiPosition.append(i)
self.board[i] = self.aiSymbol
def set_clear_for_ai(self, i):
self.aiPosition.remove(i)
self.board[i] = " "
def set_i_player(self, i):
self.playerPosition.append(i)
self.board[i] = self.playerSymbol
def set_clear_for_player(self, i):
self.playerPosition.remove(i)
self.board[i] = " "
def findOptimalPosition(self):
bestScore = float("-Infinity")
elements = {} # desperate times call for desperate measures
for i in self.availableMoves():
self.set_i_ai(i)
score = self.minimax(False)
if score > bestScore:
bestScore = score
elements[i] = bestScore
self.set_clear_for_ai(i)
if bestScore == 1:
print("you messed up larry")
elif bestScore == 0:
print("hm")
else:
print("whoops i made a prog. error")
return max(elements, key=lambda k: elements[k])
def minimax(self, isMaximizing):
if self.won():
return self.scoreBoard[self.winner]
if isMaximizing:
bestScore = float("-Infinity")
for i in self.availableMoves():
self.set_i_ai(i)
bestScore = max(self.minimax(False), bestScore)
self.set_clear_for_ai(i)
return bestScore
else:
bestScore = float("Infinity")
for i in self.availableMoves():
self.set_i_player(i)
bestScore = min(self.minimax(True), bestScore)
self.set_clear_for_player(i)
return bestScore
def play(self):
self.choice()
while not self.won_print():
if self.turn % 2 == 0:
pos = int(input("Where would you like to play? (0-8) "))
self.playerPosition.append(pos)
self.board[pos] = self.playerSymbol
self.turn += 1
self.drawBoard()
else:
aiTurn = self.findOptimalPosition()
self.aiPosition.append(aiTurn)
self.board[aiTurn] = self.aiSymbol
self.turn += 1
print("\n")
print("\n")
self.drawBoard()
else:
print("Thanks for playing :)")
if __name__ == '__main__':
tictactoe = TicTacToe()
tictactoe.play()
But as mentioned, the code may work, but there are MANY problems regarding the logic and structure, so do not straight-forward copy-paste it :))
Post a Comment for "TicTacToe And Minimax"