|
5 | 5 |
|
6 | 6 | // Psst! Most of this file was written with Copilot
|
7 | 7 |
|
| 8 | +const int weights[BOARD_SIZE][BOARD_SIZE] = { |
| 9 | + {100, -10, 10, 10, 10, 10, -10, 100}, |
| 10 | + {-10, -20, -5, -5, -5, -5, -20, -10}, |
| 11 | + {10, -5, 5, 1, 1, 5, -5, 10}, |
| 12 | + {10, -5, 1, 0, 0, 1, -5, 10}, |
| 13 | + {10, -5, 1, 0, 0, 1, -5, 10}, |
| 14 | + {10, -5, 5, 1, 1, 5, -5, 10}, |
| 15 | + {-10, -20, -5, -5, -5, -5, -20, -10}, |
| 16 | + {100, -10, 10, 10, 10, 10, -10, 100}}; |
| 17 | + |
8 | 18 | // Check if the move is legal by checking if it results in any opponent pieces being captured
|
9 | 19 | bool is_legal_move(int8_t board[BOARD_SIZE][BOARD_SIZE], int row, int col, int player) {
|
10 | 20 | 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)
|
53 | 63 | return false;
|
54 | 64 | }
|
55 | 65 |
|
56 |
| -// Calculate the heuristic value of the current board. This function can |
57 |
| -// be replaced with a more complex evaluation function that takes into |
58 |
| -// account factors such as mobility, piece square tables, etc. |
59 |
| -int heuristic(int8_t board[BOARD_SIZE][BOARD_SIZE]) { |
60 |
| - int white = 0, black = 0; |
| 66 | +int evaluate_board(int8_t board[BOARD_SIZE][BOARD_SIZE], int player) { |
| 67 | + int score = 0; |
61 | 68 | for(int i = 0; i < BOARD_SIZE; i++) {
|
62 | 69 | for(int j = 0; j < BOARD_SIZE; j++) {
|
63 |
| - if(board[i][j] == 1) white++; |
64 |
| - if(board[i][j] == -1) black++; |
| 70 | + if(board[i][j] == player) { |
| 71 | + score += weights[i][j]; |
| 72 | + } else if(board[i][j] == -player) { |
| 73 | + score -= weights[i][j]; |
| 74 | + } |
65 | 75 | }
|
66 | 76 | }
|
67 |
| - return white - black; |
| 77 | + return score; |
68 | 78 | }
|
69 | 79 |
|
70 | 80 | // Make a move on the board and capture any opponent pieces
|
71 |
| -void make_move(GameState* state, int x, int y, int player) { |
72 |
| - state->board[x][y] = player; |
| 81 | +void make_move( |
| 82 | + GameState* game_state, |
| 83 | + int8_t board[BOARD_SIZE][BOARD_SIZE], |
| 84 | + int x, |
| 85 | + int y, |
| 86 | + int player) { |
| 87 | + board[x][y] = player; |
73 | 88 | int opponent = -player;
|
74 | 89 | for(int i = -1; i <= 1; i++) {
|
75 | 90 | for(int j = -1; j <= 1; j++) {
|
76 | 91 | if(i == 0 && j == 0) continue;
|
77 | 92 | int r = x + i, c = y + j;
|
78 |
| - if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && |
79 |
| - state->board[r][c] == opponent) { |
| 93 | + if(r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && board[r][c] == opponent) { |
80 | 94 | int k = 2;
|
81 | 95 | while(true) {
|
82 | 96 | r += i;
|
83 | 97 | c += j;
|
84 | 98 | if(r < 0 || r >= BOARD_SIZE || c < 0 || c >= BOARD_SIZE) break;
|
85 |
| - if(state->board[r][c] == player) { |
| 99 | + if(board[r][c] == player) { |
86 | 100 | r -= i;
|
87 | 101 | c -= j;
|
88 | 102 | while(r != x || c != y) {
|
89 |
| - state->board[r][c] = player; |
| 103 | + board[r][c] = player; |
90 | 104 | r -= i;
|
91 | 105 | c -= j;
|
92 | 106 | }
|
93 | 107 | break;
|
94 | 108 | }
|
95 |
| - if(state->board[r][c] == 0) break; |
| 109 | + if(board[r][c] == 0) break; |
96 | 110 | k++;
|
97 | 111 | }
|
98 | 112 | }
|
99 | 113 | }
|
100 | 114 | }
|
101 |
| - state->is_game_over = is_game_over(state->board); |
| 115 | + game_state->is_game_over = is_game_over(game_state->board); |
102 | 116 | }
|
103 | 117 |
|
104 | 118 | void init_game(GameState* state) {
|
@@ -136,31 +150,97 @@ void human_move(GameState* game_state) {
|
136 | 150 | game_state->cursor_y,
|
137 | 151 | game_state->current_player)) {
|
138 | 152 | make_move(
|
139 |
| - game_state, game_state->cursor_x, game_state->cursor_y, game_state->current_player); |
| 153 | + game_state, |
| 154 | + game_state->board, |
| 155 | + game_state->cursor_x, |
| 156 | + game_state->cursor_y, |
| 157 | + game_state->current_player); |
140 | 158 | game_state->current_player = -game_state->current_player;
|
141 | 159 | }
|
142 | 160 | }
|
143 | 161 |
|
| 162 | +int minimax( |
| 163 | + GameState* game_state, |
| 164 | + int8_t board[BOARD_SIZE][BOARD_SIZE], |
| 165 | + int depth, |
| 166 | + bool is_maximizing, |
| 167 | + int player, |
| 168 | + int alpha, |
| 169 | + int beta) { |
| 170 | + if(depth == 0 || is_game_over(board)) { |
| 171 | + return evaluate_board(board, player); |
| 172 | + } |
| 173 | + |
| 174 | + if(is_maximizing) { |
| 175 | + int max_eval = -1000000; |
| 176 | + for(int i = 0; i < BOARD_SIZE; i++) { |
| 177 | + for(int j = 0; j < BOARD_SIZE; j++) { |
| 178 | + if(is_legal_move(board, i, j, player)) { |
| 179 | + int8_t temp_board[BOARD_SIZE][BOARD_SIZE]; |
| 180 | + memcpy(temp_board, board, sizeof(temp_board)); |
| 181 | + make_move(game_state, temp_board, i, j, player); |
| 182 | + int eval = |
| 183 | + minimax(game_state, temp_board, depth - 1, false, -player, alpha, beta); |
| 184 | + max_eval = max(max_eval, eval); |
| 185 | + alpha = max(alpha, eval); |
| 186 | + if(beta <= alpha) { |
| 187 | + break; |
| 188 | + } |
| 189 | + } |
| 190 | + } |
| 191 | + } |
| 192 | + return max_eval; |
| 193 | + } else { |
| 194 | + int min_eval = 1000000; |
| 195 | + for(int i = 0; i < BOARD_SIZE; i++) { |
| 196 | + for(int j = 0; j < BOARD_SIZE; j++) { |
| 197 | + if(is_legal_move(board, i, j, -player)) { |
| 198 | + int8_t temp_board[BOARD_SIZE][BOARD_SIZE]; |
| 199 | + memcpy(temp_board, board, sizeof(temp_board)); |
| 200 | + make_move(game_state, temp_board, i, j, -player); |
| 201 | + int eval = |
| 202 | + minimax(game_state, temp_board, depth - 1, true, player, alpha, beta); |
| 203 | + min_eval = min(min_eval, eval); |
| 204 | + beta = min(beta, eval); |
| 205 | + if(beta <= alpha) { |
| 206 | + break; |
| 207 | + } |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + return min_eval; |
| 212 | + } |
| 213 | +} |
| 214 | + |
144 | 215 | void computer_move(GameState* game_state) {
|
145 | 216 | if(game_state->current_player == game_state->human_color) {
|
146 | 217 | return;
|
147 | 218 | }
|
148 | 219 | int best_row = -1, best_col = -1, best_score = -1000000;
|
149 | 220 | for(int i = 0; i < BOARD_SIZE; i++) {
|
150 | 221 | for(int j = 0; j < BOARD_SIZE; j++) {
|
151 |
| - if(!is_legal_move(game_state->board, i, j, game_state->current_player)) { |
152 |
| - continue; |
153 |
| - } |
154 |
| - int score = heuristic(game_state->board); |
155 |
| - if(score > best_score) { |
156 |
| - best_score = score; |
157 |
| - best_row = i; |
158 |
| - best_col = j; |
| 222 | + if(is_legal_move(game_state->board, i, j, game_state->current_player)) { |
| 223 | + int8_t temp_board[BOARD_SIZE][BOARD_SIZE]; |
| 224 | + memcpy(temp_board, game_state->board, sizeof(temp_board)); |
| 225 | + make_move(game_state, temp_board, i, j, game_state->current_player); |
| 226 | + int score = minimax( |
| 227 | + game_state, |
| 228 | + temp_board, |
| 229 | + 3, |
| 230 | + false, |
| 231 | + -game_state->current_player, |
| 232 | + -1000000, |
| 233 | + 1000000); |
| 234 | + if(score > best_score) { |
| 235 | + best_score = score; |
| 236 | + best_row = i; |
| 237 | + best_col = j; |
| 238 | + } |
159 | 239 | }
|
160 | 240 | }
|
161 | 241 | }
|
162 | 242 | if(best_row != -1) {
|
163 |
| - make_move(game_state, best_row, best_col, game_state->current_player); |
| 243 | + make_move(game_state, game_state->board, best_row, best_col, game_state->current_player); |
164 | 244 | }
|
165 | 245 | if(has_legal_moves(game_state->board, game_state->human_color)) {
|
166 | 246 | game_state->current_player = -game_state->current_player;
|
|
0 commit comments