engine.c (11560B)
1 #include <ctype.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 6 #include "CBoard.h" 7 #include "attack.h" 8 #include "moves.h" 9 #include "perft.h" 10 #include "score.h" 11 #include "utils.h" 12 13 #include <cul/assert.h> 14 #include <cul/mem.h> 15 16 /* DEBUGGING */ 17 18 // FEN debug positions 19 #define empty_board "8/8/8/8/8/8/8/8 w - - " 20 #define start_position \ 21 "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 " 22 #define tricky_position \ 23 "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 " 24 #define killer_position \ 25 "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1" 26 #define cmk_position \ 27 "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 " 28 29 #define MAX_PLY 64 30 31 typedef struct Stats_T *Stats_T; 32 struct Stats_T { 33 long nodes; 34 int ply; 35 int pv_length[MAX_PLY]; 36 Move pv_table[MAX_PLY][MAX_PLY]; 37 Move killer_moves[2][MAX_PLY]; 38 U32 history_moves[16][64]; 39 }; 40 41 Stats_T Stats_new(void) { 42 Stats_T p; 43 NEW0(p); 44 return p; 45 } 46 47 void Stats_free(Stats_T *p) { FREE(*p); } 48 49 int Move_score(Stats_T stats, Move move) { 50 if (Move_capture(move)) { 51 return Score_capture(Piece_piece(Move_piece(move)), 52 Piece_piece(Move_piece_capture(move))); 53 } else { 54 if (!Move_cmp(stats->killer_moves[0][stats->ply], move)) 55 return 9000; 56 else if (!Move_cmp(stats->killer_moves[1][stats->ply], move)) 57 return 8000; 58 else 59 return stats->history_moves[Piece_index(Move_piece(move))] 60 [Move_target(move)]; 61 } 62 63 return 0; 64 } 65 66 void MoveList_sort(Stats_T stats, MoveList_T list) { 67 int score[list->count]; 68 for (int i = 0; i < list->count; i++) 69 score[i] = Move_score(stats, list->moves[i]); 70 71 for (int i = 0; i < list->count; i++) 72 for (int j = i + 1; j < list->count; j++) 73 if (score[i] < score[j]) { 74 Move t = list->moves[i]; 75 list->moves[i] = list->moves[j]; 76 list->moves[j] = t; 77 78 int s = score[i]; 79 score[i] = score[j]; 80 score[j] = s; 81 } 82 } 83 84 /* SEARCHING */ 85 86 int evaluate(CBoard_T board) { 87 Square square; 88 eColor side = CBoard_side(board); 89 U64 occupancy = CBoard_colorBB(board, side); 90 91 int score = 0; 92 for (int i = 0; i < 6; i++) { 93 U64 bitboard = CBoard_pieceBB(board, i); 94 bitboard_for_each_bit(square, bitboard) { 95 if (bit_get(occupancy, square)) { 96 score += Score_value(i); 97 score += Score_position(i, side, square); 98 } else { 99 score -= Score_value(i); 100 score -= Score_position(i, !side, square); 101 } 102 } 103 } 104 105 return score; 106 } 107 108 int quiescence(Stats_T stats, CBoard_T board, int alpha, int beta) { 109 MoveList_T moves; 110 CBoard_T copy; 111 112 int eval = evaluate(board); 113 stats->nodes++; 114 115 if (eval >= beta) { 116 return beta; 117 } 118 119 if (eval > alpha) { 120 alpha = eval; 121 } 122 123 copy = CBoard_new(); 124 moves = MoveList_generate(NULL, board); 125 MoveList_sort(stats, moves); 126 127 for (int i = 0; i < MoveList_size(moves); i++) { 128 CBoard_copy(board, copy); 129 130 if (Move_make(MoveList_move(moves, i), copy, 1) == 0) continue; 131 132 stats->ply++; 133 int score = -quiescence(stats, copy, -beta, -alpha); 134 stats->ply--; 135 136 if (score >= beta) { 137 MoveList_free(&moves); 138 CBoard_free(©); 139 return beta; 140 } 141 142 if (score > alpha) { 143 alpha = score; 144 } 145 } 146 147 MoveList_free(&moves); 148 CBoard_free(©); 149 return alpha; 150 } 151 152 int negamax(Stats_T stats, CBoard_T board, int alpha, int beta, int depth) { 153 MoveList_T list; 154 CBoard_T copy; 155 int isCheck = 0; 156 int ply = stats->ply; 157 158 stats->pv_length[ply] = ply; 159 160 if (depth == 0) return quiescence(stats, board, alpha, beta); 161 162 if (ply > MAX_PLY - 1) return evaluate(board); 163 164 stats->nodes++; 165 166 copy = CBoard_new(); 167 list = MoveList_generate(NULL, board); 168 isCheck = CBoard_isCheck(board); 169 170 if (isCheck) depth++; 171 172 int legal_moves = 0; 173 MoveList_sort(stats, list); 174 for (int i = 0; i < MoveList_size(list); i++) { 175 Move move = MoveList_move(list, i); 176 177 CBoard_copy(board, copy); 178 if (Move_make(move, copy, 0) == 0) { 179 continue; 180 } 181 182 stats->ply++; 183 int score = -negamax(stats, copy, -beta, -alpha, depth - 1); 184 stats->ply--; 185 legal_moves++; 186 187 if (score >= beta) { 188 if (!Move_capture(move)) { 189 stats->killer_moves[1][ply] = stats->killer_moves[0][ply]; 190 stats->killer_moves[0][ply] = move; 191 } 192 MoveList_free(&list); 193 CBoard_free(©); 194 return beta; 195 } 196 197 if (score > alpha) { 198 if (!Move_capture(move)) 199 stats->history_moves[Piece_index(Move_piece(move))] 200 [Move_target(move)] += depth; 201 alpha = score; 202 203 stats->pv_table[ply][ply] = move; 204 for (int i = stats->ply + 1; i < stats->pv_length[ply + 1]; i++) 205 stats->pv_table[ply][i] = stats->pv_table[ply + 1][i]; 206 stats->pv_length[ply] = stats->pv_length[ply + 1]; 207 } 208 } 209 210 if (legal_moves == 0) { 211 if (isCheck) 212 return -49000 + stats->ply; 213 else 214 return 0; 215 } 216 217 MoveList_free(&list); 218 CBoard_free(©); 219 return alpha; 220 } 221 222 void Move_print_UCI(Move move) { 223 printf("%s%s", square_to_coordinates[Move_source(move)], 224 square_to_coordinates[Move_target(move)]); 225 if (Move_promote(move)) printf(" %c", Piece_asci(Move_piece_promote(move))); 226 } 227 228 void search_position(CBoard_T board, int depth) { 229 Stats_T stats = Stats_new(); 230 231 for (int crnt = 1; crnt <= depth; crnt++) { 232 int score = negamax(stats, board, -50000, 50000, crnt); 233 234 printf("info score cp %d depth %d nodes %ld pv ", score, crnt, 235 stats->nodes); 236 237 for (int i = 0; i < stats->pv_length[0]; i++) { 238 Move_print_UCI(stats->pv_table[0][i]); 239 printf(" "); 240 } 241 printf("\n"); 242 } 243 244 printf("bestmove "); 245 Move_print_UCI(stats->pv_table[0][0]); 246 printf("\n"); 247 248 Stats_free(&stats); 249 } 250 251 void print_info(void) { 252 printf("id name Stellar\n"); 253 printf("id author Dimitrije Dobrota\n"); 254 printf("uciok\n"); 255 } 256 257 typedef struct Instruction_T *Instruction_T; 258 struct Instruction_T { 259 char *command; 260 char *token; 261 char *crnt; 262 }; 263 264 char *Instruction_token_next(Instruction_T self); 265 266 Instruction_T Instruction_new(char *command) { 267 Instruction_T p; 268 NEW0(p); 269 p->command = ALLOC(strlen(command) + 1); 270 p->token = ALLOC(strlen(command) + 1); 271 strcpy(p->command, command); 272 p->crnt = command; 273 Instruction_token_next(p); 274 return p; 275 } 276 277 void Instruction_free(Instruction_T *p) { 278 FREE((*p)->command); 279 FREE((*p)->token); 280 FREE(*p); 281 } 282 283 char *Instruction_token(Instruction_T self) { return self->token; } 284 char *Instruction_token_n(Instruction_T self, int n) { 285 while (isspace(*self->crnt) && *self->crnt != '\0') 286 self->crnt++; 287 288 if (*self->crnt == '\0') { 289 *self->token = '\0'; 290 return NULL; 291 } 292 293 char *p = self->token; 294 while (n--) { 295 while (!isspace(*self->crnt) && *self->crnt != '\0' && 296 *self->crnt != ';') 297 *p++ = *self->crnt++; 298 if (*self->crnt == '\0') { 299 p++; 300 break; 301 } 302 self->crnt++; 303 *p++ = ' '; 304 } 305 *--p = '\0'; 306 307 return self->token; 308 } 309 310 char *Instruction_token_next(Instruction_T self) { 311 return Instruction_token_n(self, 1); 312 } 313 314 Move parse_move(CBoard_T board, char *move_string) { 315 Move result = {0}; 316 MoveList_T moves; 317 Square source, target; 318 319 source = coordinates_to_square(move_string); 320 target = coordinates_to_square(move_string + 2); 321 322 moves = MoveList_generate(NULL, board); 323 for (int i = 0; i < moves->count; i++) { 324 Move move = moves->moves[i]; 325 if (Move_source(move) == source && Move_target(move) == target) { 326 if (move_string[4]) { 327 if (tolower(Piece_code(Move_piece_promote(move))) != 328 move_string[4]) 329 continue; 330 } 331 result = move; 332 break; 333 } 334 } 335 336 MoveList_free(&moves); 337 return result; 338 } 339 340 CBoard_T Instruction_parse(Instruction_T self, CBoard_T board) { 341 char *token = Instruction_token(self); 342 343 if (!board) board = CBoard_new(); 344 345 do { 346 if (strcmp(token, "ucinewgame") == 0) { 347 board = CBoard_fromFEN(board, start_position); 348 continue; 349 } 350 351 if (strcmp(token, "quit") == 0) return NULL; 352 353 if (strcmp(token, "position") == 0) { 354 token = Instruction_token_next(self); 355 if (token && strcmp(token, "startpos") == 0) { 356 board = CBoard_fromFEN(board, start_position); 357 } else if (token && strcmp(token, "fen") == 0) { 358 token = Instruction_token_n(self, 6); 359 board = CBoard_fromFEN(board, token); 360 } else { 361 printf("Unknown argument after position\n"); 362 } 363 // CBoard_print(board); 364 continue; 365 } 366 367 if (strcmp(token, "moves") == 0) { 368 while ((token = Instruction_token_next(self))) { 369 Move move = parse_move(board, token); 370 if (!Move_cmp(move, (Move){0})) { 371 Move_make(move, board, 0); 372 } else { 373 printf("Invalid move %s!\n", token); 374 } 375 } 376 // CBoard_print(board); 377 return board; 378 } 379 380 if (strcmp(token, "go") == 0) { 381 token = Instruction_token_next(self); 382 int depth = 5; 383 if (token && strcmp(token, "depth") == 0) { 384 token = Instruction_token_next(self); 385 depth = atoi(token); 386 } else { 387 printf("Unknown argument after go\n"); 388 } 389 search_position(board, depth); 390 continue; 391 } 392 393 if (strcmp(token, "isready") == 0) { 394 printf("readyok\n"); 395 continue; 396 } 397 398 if (strcmp(token, "uci") == 0) { 399 print_info(); 400 continue; 401 } 402 403 } while ((token = Instruction_token_next(self))); 404 405 return board; 406 } 407 408 void uci_loop(void) { 409 CBoard_T board = NULL; 410 Instruction_T instruction; 411 char input[200000]; 412 413 setbuf(stdin, NULL); 414 setbuf(stdout, NULL); 415 416 print_info(); 417 while (1) { 418 memset(input, 0, sizeof(input)); 419 fflush(stdout); 420 if (!fgets(input, sizeof(input), stdin)) continue; 421 422 instruction = Instruction_new(input); 423 if (!(board = Instruction_parse(instruction, board))) break; 424 Instruction_free(&instruction); 425 } 426 427 Instruction_free(&instruction); 428 CBoard_free(&board); 429 } 430 431 /* MAIN */ 432 433 void init_all() { 434 init_leapers_attacks(); 435 init_sliders_attacks(); 436 } 437 438 int main(void) { 439 init_all(); 440 uci_loop(); 441 return 0; 442 }