I already have my brain broken with ai for tic-tac-toe type board game.
Problem is slow ai performance on high levels (even low levels has not so quick).
AI uses recursive method to find best move from number of available moves.
Here is some code:
@impelementation AIClass
- (NSMutableArray *)findLegalMoves
{
// Here is code that finds available legal moves
// Loop over board array
}
- (float)scoreOpponentsTurn:(float)min max:(float)max depth:(int)depth
{
moveType t; // moveType is struct defined in .h file
// typedef struct { int x, y; } moveType
NSMutableArray *moves = [self findLegalMoves];
for ( NSValue *val in moves ) {
[val getValue:&it]
float score = [self scoreMove:it min:min max:max depth:depth];
if ( score > min ) {
min = score;
}
if ( score > max ) {
min = 1000000000;
}
}
return min;
}
- (float)scoreMove:(moveType)m min:(float)min max:(float)max depth:(int)depth
{
NSMutableArray *changes = [[NSMutableArray alloc] init];
NSMutableArray *undo = [[NSMutableArray alloc] init];
float score;
[self.board evaluateChangesOnCol:m.x Row:m.y];
if ( [self.board checkForWin:&changes undo:&undo] ) {
score = 1000000000 + [self calcH]; //calcH - evals heuristic like sum of params
} else if ( depth > 0 ) {
score = - [self scoreOpponentsTurn:-1000000000 max:-min depth:depth - 1];
} else {
score = [self calcH];
}
[self.board applyChanges:undo];
}
- (moveType)findBestMove
{
NSMutableArray *legalMoves = [self findLegalMoves];
NSMutableArray *bestMoves = [[NSMutableArray alloc] init];
int min = -1000000000;
int max = 1000000000;
moveType move;
for ( NSValue *moveIt in legalMoves ) {
[moveIt getValue:&move];
float score = [self scoreMove:move min:min max:max depth:depth];
// Here i have some conditions to decide current move is best or not
}
// and pick random move from best moves and assign it to move variable
return move;
}
@end
And if number of legal moves like 3 and more (over recursive search it grows) this algorithm
works very slow.
It’s my first objective-c experience.
Here is my guesses about how to improve performance:
- Remove recursion (but I don’t see another solution)
- Use multithreading (how?)
- May be use some ai library?
Sorry for my english.
Throwing away recursion in an algorithm that is a natural fit for recursion is not a good idea. Rather, you need to memoize your recursive solution. This common trick speeds up recursive solutions with common subproblems by orders of magnitude.
Consider these two sequences of moves:
and
The sequences are different, but they arrive at the same final state:
Here is the root cause of the slowness: when your program arrives at a repeated state for the second time, it evaluates the position exactly as if it’s the first time that it has seen it. This is wasteful: identical positions with three-move look-aheads will be evaluated four times; with four-move look-aheads, they would be evaluated eight times, and so on. This causes slowness proportional to
2^N, whereNis the depth of your look-ahead.Fixing this requires an addition of a lookup table. Given a state of the game, this table would give you the score for you or for the opponent, if such score has been calculated before. Your recursive functions would build a position key, and try a lookup in the score table. If the answer is there, it would be returned immediately. Otherwise, your function would construct the answer, and store it at the position key. Next time the same position occurs through a different series of moves, the answer would be reused.