stellar

Stellar - Chess engine written in C
Log | Files | Refs

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(&copy);
    139             return beta;
    140         }
    141 
    142         if (score > alpha) {
    143             alpha = score;
    144         }
    145     }
    146 
    147     MoveList_free(&moves);
    148     CBoard_free(&copy);
    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(&copy);
    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(&copy);
    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 }