I am creating an AI for a board game known as Reversi / Othello. I have created a minimax function that plays n moves forward for every possible move for each player and chooses the best possible outcome. However, I find that when playing against a player he often makes stupid mistakes (even after including the heuristic) and loses quite often. Is there a problem with logic in my program?

As a note, all the functions included within the minimax function work perfectly fine, I have thoroughly tested them.

```
float minimax(char **board, int n, int row, int col, char colour, int depth, float alpha, float beta, bool maxPlayer, char *computerColourPtr, char *playColourPtr) {
//printf("%dn", depth);
char compMoves(50) = {0};
char oppMoves(50) = {0};
char colour2;
if (maxPlayer) {
colour = *computerColourPtr;
colour2 = *playColourPtr;
}
else {
colour2 = *computerColourPtr;
colour = *playColourPtr;
}
char **oldBoard = (char **)malloc(sizeof(char *) * n);
for (int i = 0; i < n; i++) {
oldBoard(i) = (char *)malloc(n);
}
copyBoard(board, oldBoard, n);
char **flippedBoard = (char **)malloc(sizeof(char *) * n);
for (int i = 0; i < n; i++) {
flippedBoard(i) = (char *)malloc(n);
}
copyBoard(oldBoard, flippedBoard, n);
//printf("Depth: %d. %c at %c%cn", depth, colour, row + 97, col + 97);
int maxMoves = availableMoves(n, oldBoard, *computerColourPtr, compMoves, false);
int minMoves = availableMoves(n, oldBoard, *playColourPtr, oppMoves, false);
//printf("%d %dn", maxMoves, minMoves);
if (depth == 0 || (maxMoves + minMoves) == 0) {
int numOfMax = countColour(n, flippedBoard, *computerColourPtr);
int numOfMin = countColour(n, flippedBoard, *playColourPtr);
double timeD = ((numOfMax + numOfMin)/(float)(n*n));
float summation = 0;
summation += 25.0*timeD*(float)numOfMax/(float)(numOfMax+numOfMin);
//printf("Avail: %lfn", 0.5*(float)availableMoves(n, flippedBoard, *computerColourPtr, compMoves, false));
summation += (float)availableMoves(n, flippedBoard, *computerColourPtr, compMoves, false);
summation -= (float)availableMoves(n, flippedBoard, *playColourPtr, oppMoves, false);
summation += maxFlipped(n, flippedBoard, *computerColourPtr);
summation -= maxFlipped(n, flippedBoard, *playColourPtr);
//summation += timeD*(float)flip(flippedBoard, colour, row, col, n, false);
//printf("%fn", summation);
//summation += 1.0*timeD*edgePlaces(row, col, n, colour);
//printf("%fn", summation);
//summation += timeD*2.0*(float)((numOfMax+numOfMin)-(n*n)/4)*(numOfMax/(numOfMax+numOfMin));
//printf("%fn", summation);
//printf("Corner move: %lfn", 3.0*timeD*(float)location(row, col, n, *computerColourPtr, flippedBoard));
//summation += 3.0*location(row, col, n, *computerColourPtr, flippedBoard);
//printf("Depth of blegh. %c at %c%c: %f %c: %d %c: %dn", colour, row+97,col+97, summation, colour, numOfMax, colour2, numOfMin);
//printBoard(flippedBoard, n);
return summation;
}
if (maxPlayer) {
float maxEval = -10000;
float eval1;
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
if (checkLegalInDirection(oldBoard, n, row, col, colour, deltaRow, deltaCol)) {
copyBoard(oldBoard, flippedBoard, n);
flip(flippedBoard, colour, row+97, col+97, n, true);
flippedBoard(row)(col) = colour;
//printf("Depth of %d. %c at %c%c: %fn", depth, colour, row+97,col+97, maxEval);
//printBoard(flippedBoard, n);
eval1 = minimax(flippedBoard, n, row, col, colour, depth - 1, alpha, beta, false, computerColourPtr, playColourPtr);
maxEval = max(maxEval, eval1);
alpha = max(alpha, eval1);
//printf("Max: %fn", maxEval);
//printBoard(flippedBoard, n);
if (beta <= alpha) {
row = col = deltaRow = deltaCol = n;
break;
}
}
}
}
}
}
//printf("MAXIMUM: %lfn", maxEval);
return maxEval;
}
else {
float minEval = 10000;
float eval2;
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
for (int deltaRow = -1; deltaRow <= 1; deltaRow++) {
for (int deltaCol = -1; deltaCol <= 1; deltaCol++) {
if (checkLegalInDirection(oldBoard, n, row, col, colour, deltaRow, deltaCol)) {
copyBoard(oldBoard, flippedBoard, n);
flip(flippedBoard, colour, row+97, col+97, n, true);
flippedBoard(row)(col) = colour;
//printf("Depth of %d. %c at %c%c: %fn", depth, colour, row+97,col+97, minEval);
//printBoard(flippedBoard, n);
//printBoard(flippedBoard, n);
eval2 = minimax(flippedBoard, n, row, col, colour, depth - 1, alpha, beta, true, computerColourPtr, playColourPtr);
minEval = min(minEval, eval2);
beta = min(beta, eval2);
//printf("Min: %fn", minEval);
//printf("Depth of %d. %c at %c%c: %fn", depth, colour, row+97,col+97, minEval);
//printBoard(flippedBoard, n);
if (beta <= alpha) {
row = col = deltaRow = deltaCol = n;
break;
}
}
}
}
}
}
//printf("MINIMUM: %lfn", minEval);
return minEval;
}
}
```

The function returns a float to a function that plays a movement depending on the movement for which minimax returns the highest value. The oldBoard and flippedBoard variables are dynamic matrices, and each recursion has a player (can be white or black depending on who is the turn) who plays a move on flippedBoard, the oldBoard variable is used to copy the previous board to the next board without losing information for every possible move.

If this explanation is not clear, please let me know, I will try my best to explain.

Note: I currently have the search function at a depth of 5.