I'm attempting to develop an AI to play a 1-player board game optimally. I'm using a depth-first search to a few levels.
I've attempted to speed it up by multithreading the initial loop iterating over all moves and recursing into the game trees. My idea is that each thread will split-up the initial possible move boards into chunks and further evaluate these in a separate recursive function. All functions called are nogil
However, I'm encountering what I can only guess is a race condition because the multi-threaded solution gives different results, and I'm not sure how to go about fixing it.
cdef struct Move:
int x
int y
int score
cdef Move search( board_t& board, int prevClears, int maxDepth, int depth ) nogil:
cdef Move bestMove
cdef Move recursiveMove
cdef vector[ Move ] moves = generateMoves( board )
cdef board_t nextBoard
cdef int i, clears
bestMove.score = 0
# Split the initial possible move boards amongst threads
for i in prange( <int> moves.size(), nogil = True ):
# Applies move and calculates the move score
nextBoard = applyMove( board, moves[ i ], prevClears, maxDepth, depth )
# Recursively evaluate further moves
if maxDepth - depth > 0:
clears = countClears( nextBoard )
recursiveMove = recursiveSearch( nextBoard, moves[ i ], clears, maxDepth, depth + 1 )
moves[ i ].score += recursiveMove.score
# Update bestMove
if moves[ i ].score > bestMove.score:
bestMove = moves[ i ]
return bestMove