This is my C implementation of the Minimax Algorithm to play tic-tac-toe. I consider myself a C beginner so any feedback on style, best practices, or efficiency is more than welcome.
# define HUMAN_PLAYER 'X'
# define AI_PLAYER 'O'
# define NONE '\0'
typedef struct Point {
int x, y;
} Point;
typedef struct Board {
Point location;
Point boundary;
int size;
int current_player;
int last_player;
int nmoves_played;
int ai_mode;
char last_cell_symbol;
char **cells;
} Board;
typedef struct MiniMaxMove {
int score;
int x;
int y;
} MiniMaxMove;
int has_won(Point last_destination, char player, Board *board) {
// columns
for (int i = 0; i < board->size; i++) {
if (board->cells[last_destination.x][i] != player)
break;
if (i == board->size - 1) {
return 1;
}
}
// rows
for (int i = 0; i < board->size; i++) {
if (board->cells[i][last_destination.y] != player)
break;
if (i == board->size - 1) {
return 1;
}
}
// diagonal
if (last_destination.x == last_destination.y) {
for (int i = 0; i < board->size; i++) {
if (board->cells[i][i] != player)
break;
if (i == board->size - 1) {
return 1;
}
}
}
// anti diagonal
if (last_destination.x + last_destination.y == board->size - 1) {
for (int i = 0; i < board->size; i++) {
if (board->cells[i][(board->size-1)-i] != player)
break;
if (i == board->size - 1){
return 1;
}
}
}
return 0;
}
int is_a_draw(Board *board) {
if (board->nmoves_played == (pow(board->size, 2) - 1)) {
return 1;
}
return 0;
}
MiniMaxMove minimax(Point last_destination, char player, Board *board) {
if (has_won(last_destination, HUMAN_PLAYER, board)) {
return (MiniMaxMove) { .score = -10 };
} else if (has_won(last_destination, AI_PLAYER, board)) {
return (MiniMaxMove) { .score = 10 };
} else if (is_a_draw(board)) {
return (MiniMaxMove) { .score = 0 };
}
MiniMaxMove *moves = malloc(sizeof(MiniMaxMove *) * board->size * board->size);
size_t moves_size = 0;
for (int i = 0; i < board->size; i++) {
for (int j = 0; j < board->size; j++) {
if (board->cells[i][j] == '\0') {
MiniMaxMove move;
move.x = i;
move.y = j;
board->cells[i][j] = player;
// caclulate the score for the opponent
if (player == AI_PLAYER) {
MiniMaxMove mm_move = minimax(last_destination, HUMAN_PLAYER, board);
move.score = mm_move.score;
} else if (player == HUMAN_PLAYER) {
MiniMaxMove mm_move = minimax(last_destination, AI_PLAYER, board);
move.score = mm_move.score;
}
// reset the board to what it was
board->cells[i][j] = '\0';
moves[moves_size++] = move;
}
}
}
int best_move_idx;
if (player == AI_PLAYER) {
int best_score = -10000;
for (unsigned int i = 0; i < moves_size; i++) {
if (moves[i].score > best_score) {
best_score = moves[i].score;
best_move_idx = i;
}
}
} else {
int best_score = 10000;
for (unsigned int i = 0; i < moves_size; i++) {
if (moves[i].score < best_score) {
best_score = moves[i].score;
best_move_idx = i;
}
}
}
return moves[best_move_idx];
}
MiniMaxMove *moves = malloc( sizeof(MiniMaxMove *) * board->size * board->size);
is suspicious. I'd expectsizeof(MiniMaxMove) * board->size * board->size
or bettersizeof *moves * board->size * board->size
\$\endgroup\$