|
@@ -5,6 +5,16 @@
|
|
|
|
|
|
|
|
// Psst! Most of this file was written with Copilot
|
|
// Psst! Most of this file was written with Copilot
|
|
|
|
|
|
|
|
|
|
+const int weights[BOARD_SIZE][BOARD_SIZE] = {
|
|
|
|
|
+ {100, -10, 10, 10, 10, 10, -10, 100},
|
|
|
|
|
+ {-10, -20, -5, -5, -5, -5, -20, -10},
|
|
|
|
|
+ {10, -5, 5, 1, 1, 5, -5, 10},
|
|
|
|
|
+ {10, -5, 1, 0, 0, 1, -5, 10},
|
|
|
|
|
+ {10, -5, 1, 0, 0, 1, -5, 10},
|
|
|
|
|
+ {10, -5, 5, 1, 1, 5, -5, 10},
|
|
|
|
|
+ {-10, -20, -5, -5, -5, -5, -20, -10},
|
|
|
|
|
+ {100, -10, 10, 10, 10, 10, -10, 100}};
|
|
|
|
|
+
|
|
|
// Check if the move is legal by checking if it results in any opponent pieces being captured
|
|
// Check if the move is legal by checking if it results in any opponent pieces being captured
|
|
|
bool is_legal_move(int8_t board[BOARD_SIZE][BOARD_SIZE], int row, int col, int player) {
|
|
bool is_legal_move(int8_t board[BOARD_SIZE][BOARD_SIZE], int row, int col, int player) {
|
|
|
if(board[row][col] != 0) return false;
|
|
if(board[row][col] != 0) return false;
|
|
@@ -53,52 +63,56 @@ bool has_legal_moves(int8_t board[BOARD_SIZE][BOARD_SIZE], int8_t player_color)
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Calculate the heuristic value of the current board. This function can
|
|
|
|
|
-// be replaced with a more complex evaluation function that takes into
|
|
|
|
|
-// account factors such as mobility, piece square tables, etc.
|
|
|
|
|
-int heuristic(int8_t board[BOARD_SIZE][BOARD_SIZE]) {
|
|
|
|
|
- int white = 0, black = 0;
|
|
|
|
|
|
|
+int evaluate_board(int8_t board[BOARD_SIZE][BOARD_SIZE], int player) {
|
|
|
|
|
+ int score = 0;
|
|
|
for(int i = 0; i < BOARD_SIZE; i++) {
|
|
for(int i = 0; i < BOARD_SIZE; i++) {
|
|
|
for(int j = 0; j < BOARD_SIZE; j++) {
|
|
for(int j = 0; j < BOARD_SIZE; j++) {
|
|
|
- if(board[i][j] == 1) white++;
|
|
|
|
|
- if(board[i][j] == -1) black++;
|
|
|
|
|
|
|
+ if(board[i][j] == player) {
|
|
|
|
|
+ score += weights[i][j];
|
|
|
|
|
+ } else if(board[i][j] == -player) {
|
|
|
|
|
+ score -= weights[i][j];
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return white - black;
|
|
|
|
|
|
|
+ return score;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Make a move on the board and capture any opponent pieces
|
|
// Make a move on the board and capture any opponent pieces
|
|
|
-void make_move(GameState* state, int x, int y, int player) {
|
|
|
|
|
- state->board[x][y] = player;
|
|
|
|
|
|
|
+void make_move(
|
|
|
|
|
+ GameState* game_state,
|
|
|
|
|
+ int8_t board[BOARD_SIZE][BOARD_SIZE],
|
|
|
|
|
+ int x,
|
|
|
|
|
+ int y,
|
|
|
|
|
+ int player) {
|
|
|
|
|
+ board[x][y] = player;
|
|
|
int opponent = -player;
|
|
int opponent = -player;
|
|
|
for(int i = -1; i <= 1; i++) {
|
|
for(int i = -1; i <= 1; i++) {
|
|
|
for(int j = -1; j <= 1; j++) {
|
|
for(int j = -1; j <= 1; j++) {
|
|
|
if(i == 0 && j == 0) continue;
|
|
if(i == 0 && j == 0) continue;
|
|
|
int r = x + i, c = y + j;
|
|
int r = x + i, c = y + j;
|
|
|
- if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE &&
|
|
|
|
|
- state->board[r][c] == opponent) {
|
|
|
|
|
|
|
+ if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) {
|
|
|
int k = 2;
|
|
int k = 2;
|
|
|
while(true) {
|
|
while(true) {
|
|
|
r += i;
|
|
r += i;
|
|
|
c += j;
|
|
c += j;
|
|
|
if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
|
|
if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
|
|
|
- if(state->board[r][c] == player) {
|
|
|
|
|
|
|
+ if(board[r][c] == player) {
|
|
|
r -= i;
|
|
r -= i;
|
|
|
c -= j;
|
|
c -= j;
|
|
|
while(r != x || c != y) {
|
|
while(r != x || c != y) {
|
|
|
- state->board[r][c] = player;
|
|
|
|
|
|
|
+ board[r][c] = player;
|
|
|
r -= i;
|
|
r -= i;
|
|
|
c -= j;
|
|
c -= j;
|
|
|
}
|
|
}
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
- if(state->board[r][c] == 0) break;
|
|
|
|
|
|
|
+ if(board[r][c] == 0) break;
|
|
|
k++;
|
|
k++;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- state->is_game_over = is_game_over(state->board);
|
|
|
|
|
|
|
+ game_state->is_game_over = is_game_over(game_state->board);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void init_game(GameState* state) {
|
|
void init_game(GameState* state) {
|
|
@@ -136,11 +150,68 @@ void human_move(GameState* game_state) {
|
|
|
game_state->cursor_y,
|
|
game_state->cursor_y,
|
|
|
game_state->current_player)) {
|
|
game_state->current_player)) {
|
|
|
make_move(
|
|
make_move(
|
|
|
- game_state, game_state->cursor_x, game_state->cursor_y, game_state->current_player);
|
|
|
|
|
|
|
+ game_state,
|
|
|
|
|
+ game_state->board,
|
|
|
|
|
+ game_state->cursor_x,
|
|
|
|
|
+ game_state->cursor_y,
|
|
|
|
|
+ game_state->current_player);
|
|
|
game_state->current_player = -game_state->current_player;
|
|
game_state->current_player = -game_state->current_player;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+int minimax(
|
|
|
|
|
+ GameState* game_state,
|
|
|
|
|
+ int8_t board[BOARD_SIZE][BOARD_SIZE],
|
|
|
|
|
+ int depth,
|
|
|
|
|
+ bool is_maximizing,
|
|
|
|
|
+ int player,
|
|
|
|
|
+ int alpha,
|
|
|
|
|
+ int beta) {
|
|
|
|
|
+ if(depth == 0 || is_game_over(board)) {
|
|
|
|
|
+ return evaluate_board(board, player);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if(is_maximizing) {
|
|
|
|
|
+ int max_eval = -1000000;
|
|
|
|
|
+ for(int i = 0; i < BOARD_SIZE; i++) {
|
|
|
|
|
+ for(int j = 0; j < BOARD_SIZE; j++) {
|
|
|
|
|
+ if(is_legal_move(board, i, j, player)) {
|
|
|
|
|
+ int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
|
|
|
|
|
+ memcpy(temp_board, board, sizeof(temp_board));
|
|
|
|
|
+ make_move(game_state, temp_board, i, j, player);
|
|
|
|
|
+ int eval =
|
|
|
|
|
+ minimax(game_state, temp_board, depth - 1, false, -player, alpha, beta);
|
|
|
|
|
+ max_eval = max(max_eval, eval);
|
|
|
|
|
+ alpha = max(alpha, eval);
|
|
|
|
|
+ if(beta <= alpha) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return max_eval;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ int min_eval = 1000000;
|
|
|
|
|
+ for(int i = 0; i < BOARD_SIZE; i++) {
|
|
|
|
|
+ for(int j = 0; j < BOARD_SIZE; j++) {
|
|
|
|
|
+ if(is_legal_move(board, i, j, -player)) {
|
|
|
|
|
+ int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
|
|
|
|
|
+ memcpy(temp_board, board, sizeof(temp_board));
|
|
|
|
|
+ make_move(game_state, temp_board, i, j, -player);
|
|
|
|
|
+ int eval =
|
|
|
|
|
+ minimax(game_state, temp_board, depth - 1, true, player, alpha, beta);
|
|
|
|
|
+ min_eval = min(min_eval, eval);
|
|
|
|
|
+ beta = min(beta, eval);
|
|
|
|
|
+ if(beta <= alpha) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return min_eval;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
void computer_move(GameState* game_state) {
|
|
void computer_move(GameState* game_state) {
|
|
|
if(game_state->current_player == game_state->human_color) {
|
|
if(game_state->current_player == game_state->human_color) {
|
|
|
return;
|
|
return;
|
|
@@ -148,19 +219,28 @@ void computer_move(GameState* game_state) {
|
|
|
int best_row = -1, best_col = -1, best_score = -1000000;
|
|
int best_row = -1, best_col = -1, best_score = -1000000;
|
|
|
for(int i = 0; i < BOARD_SIZE; i++) {
|
|
for(int i = 0; i < BOARD_SIZE; i++) {
|
|
|
for(int j = 0; j < BOARD_SIZE; j++) {
|
|
for(int j = 0; j < BOARD_SIZE; j++) {
|
|
|
- if(!is_legal_move(game_state->board, i, j, game_state->current_player)) {
|
|
|
|
|
- continue;
|
|
|
|
|
- }
|
|
|
|
|
- int score = heuristic(game_state->board);
|
|
|
|
|
- if(score > best_score) {
|
|
|
|
|
- best_score = score;
|
|
|
|
|
- best_row = i;
|
|
|
|
|
- best_col = j;
|
|
|
|
|
|
|
+ if(is_legal_move(game_state->board, i, j, game_state->current_player)) {
|
|
|
|
|
+ int8_t temp_board[BOARD_SIZE][BOARD_SIZE];
|
|
|
|
|
+ memcpy(temp_board, game_state->board, sizeof(temp_board));
|
|
|
|
|
+ make_move(game_state, temp_board, i, j, game_state->current_player);
|
|
|
|
|
+ int score = minimax(
|
|
|
|
|
+ game_state,
|
|
|
|
|
+ temp_board,
|
|
|
|
|
+ 3,
|
|
|
|
|
+ false,
|
|
|
|
|
+ -game_state->current_player,
|
|
|
|
|
+ -1000000,
|
|
|
|
|
+ 1000000);
|
|
|
|
|
+ if(score > best_score) {
|
|
|
|
|
+ best_score = score;
|
|
|
|
|
+ best_row = i;
|
|
|
|
|
+ best_col = j;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
if(best_row != -1) {
|
|
if(best_row != -1) {
|
|
|
- make_move(game_state, best_row, best_col, game_state->current_player);
|
|
|
|
|
|
|
+ make_move(game_state, game_state->board, best_row, best_col, game_state->current_player);
|
|
|
}
|
|
}
|
|
|
if(has_legal_moves(game_state->board, game_state->human_color)) {
|
|
if(has_legal_moves(game_state->board, game_state->human_color)) {
|
|
|
game_state->current_player = -game_state->current_player;
|
|
game_state->current_player = -game_state->current_player;
|