Reputation: 14845
Hi!
I'm trying to write a negamax search algorithm for my chess engine, but i can't seem to get it working. I'm using wikipedias pseudocode as an example, but somehow it doesn't produce the expected results. When i run it with a ply of 2, it changes my board data structure, although it shouldn't. After the function is finished running with ply 2, all of the white's (or black's. depends what player calls the function.) pawns are moved 2 spaces forward from starting position.
My make and unmake move functions are working perfectly , as i tested them with a non-recursive function that searches up to 5-ply. Then, it worked perfectly. There must be something wrong with my negamax implementation.
Thank you very much for your help!
def negaMax(self, board, rules, ply, player):
""" Implements a minimax algorithm. """
if ply == 0:
return self.positionEvaluation()
self.max_eval = float('-infinity')
self.move_list = board.generateMoves(rules, player)
for self.move in self.move_list:
board.makeMove(self.move, player)
self.eval = -self.negaMax(board, rules, ply - 1, board.getOtherPlayer(player))
board.unmakeMove(self.move, player)
if self.eval > self.max_eval:
self.max_eval = self.eval
return self.max_eval
Upvotes: 4
Views: 1832
Reputation: 178431
The main issue here is I believe the usage of object variables instead of local variable.
self.move
is an object variable, every time you change it - every level of the recursion "sees" the change, which is usually a bad thing for recursive algorithms.
Recursive algorithms should be self contained, and do minimal if any change on the calling environment - it makes it much easier to walk over the flow of the algorithm.
The two main issues I see in this code are:
self.move
should be a local variable (declare it as move
). When you later do: board.unmakeMove(self.move, player)
- I suspect that the board is undoing a different move, which was set deeper in the recursion tree, and not the one you intended. Using a local variable will eliminate this undesired behavior.self.max_eval = float('-infinity')
- so you constantly change it, though it is probably not what you want to do.The solution should be something like that:
def negaMax(self, board, rules, ply, player):
""" Implements a minimax algorithm. """
if ply == 0:
return self.positionEvaluation()
max_eval = float('-infinity')
move_list = board.generateMoves(rules, player)
for move in move_list:
board.makeMove(move, player)
currentEval = -self.negaMax(board, rules, ply - 1, board.getOtherPlayer(player))
board.unmakeMove(move, player)
if currentEval > max_eval:
max_eval = currentEval
return max_eval
I am not 100% certain it will indeed solve everything in the code (but it will solve some of it) but I am 100% certain avoiding object variables will make your code much easier to understand and debug.
Upvotes: 4