stellar

Stellar - UCI Chess engine written in C++20
git clone git://git.dimitrijedobrota.com/stellar.git
Log | Files | Refs | README | LICENSE

commit cdfff1b8b0d8706f3178cfaede6210a2550c2e8a
parent 7e7b1f3d78e9ea7f09b3f992d5025df7499bdd8e
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Sun,  2 Oct 2022 14:07:43 +0200

Refactoring engine

Diffstat:
Msrc/CBoard.c | 6++----
Msrc/engine.c | 690+++++++++++++++++++++++++++++++++++++++++--------------------------------------
2 files changed, 361 insertions(+), 335 deletions(-)

diff --git a/src/CBoard.c b/src/CBoard.c @@ -116,11 +116,9 @@ U64 CBoard_colorBB_get(CBoard_T self, eColor color, Square target) { } int CBoard_piece_get(CBoard_T self, Square square) { - for (int i = 0; i < 6; i++) { - if (bit_get(self->pieceBB[i], square)) { + for (int i = 0; i < 6; i++) + if (bit_get(self->pieceBB[i], square)) return i; - } - } return -1; } diff --git a/src/engine.c b/src/engine.c @@ -25,40 +25,195 @@ "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 " typedef U32 Move; -#define Move_encode(source, target, piece, promoted, capture, dbl, enpassant, \ - castling) \ - (source) | (target << 6) | (piece << 12) | (promoted << 16) | \ - (capture << 20) | (dbl << 21) | (enpassant << 22) | (castling << 23) + +Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Promotion, + int capture, int dbl, int enpassant, int castle) { + assert(Piece); + int prom = (Promotion == 0) ? 0 : Piece_index(Promotion); + return (src) | (tgt << 6) | (Piece_index(Piece) << 12) | (prom << 16) | + (capture << 20) | (dbl << 21) | (enpassant << 22) | (castle << 23); +} #define Move_source(move) (((move)&C32(0x00003f))) #define Move_target(move) (((move)&C32(0x000fc0)) >> 6) #define Move_piece(move) Piece_fromIndex((((move)&C32(0x00f000)) >> 12)) -#define Move_promote(move) (((move)&C32(0x0f0000)) >> 16) #define Move_capture(move) (((move)&C32(0x100000)) >> 20) #define Move_double(move) (((move)&C32(0x200000)) >> 21) #define Move_enpassant(move) (((move)&C32(0x400000)) >> 22) #define Move_castle(move) (((move)&C32(0x800000)) >> 23) +Piece_T Move_promote(Move move) { + int index = (move & C32(0x0f0000)) >> 16; + if (!index) + return NULL; + return Piece_fromIndex(index); +} + +// clang-format off +const int castling_rights[64] = { + 13, 15, 15, 15, 12, 15, 15, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 7, 15, 15, 15, 3, 15, 15, 11, +}; + +struct Score_T { + int value; + int position[64]; + int capture[6]; +}; + +struct Score_T Scores[] = { +[PAWN] = { +.value = 100, +.position = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -10, -10, 0, 0, 0, + 0, 0, 0, 5, 5, 0, 0, 0, + 5, 5, 10, 20, 20, 5, 5, 5, + 10, 10, 10, 20, 20, 10, 10, 10, + 20, 20, 20, 30, 30, 30, 20, 20, + 30, 30, 30, 40, 40, 30, 30, 30, + 90, 90, 90, 90, 90, 90, 90, 90, }, +.capture = { [PAWN] = 105, [KNIGHT] = 205, + [BISHOP] = 305, [ROOK] = 405, + [QUEEN] = 505, [KING] = 605} }, +[KNIGHT] = { +.value = 300, +.position = { + -5, -10 , 0, 0, 0, 0, -10, -5, + -5, 0, 0, 0, 0, 0, 0, -5, + -5, 5, 20, 10, 10, 20, 5, -5, + -5, 10, 20, 30, 30, 20, 10, -5, + -5, 10, 20, 30, 30, 20, 10, -5, + -5, 5, 20, 20, 20, 20, 5, -5, + -5, 0, 0, 10, 10, 0, 0, -5, + -5, 0, 0, 0, 0, 0, 0, -5, }, +.capture = { [PAWN] = 104, [KNIGHT] = 204, + [BISHOP] = 304, [ROOK] = 404, + [QUEEN] = 504, [KING] = 604} }, +[BISHOP] = { +.value = 350, +.position = { + 0, 0, -10, 0, 0, -10, 0, 0, + 0, 30, 0, 0, 0, 0, 30, 0, + 0, 10, 0, 0, 0, 0, 10, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 0, 0, 0, 10, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }, +.capture = { [PAWN] = 103, [KNIGHT] = 203, + [BISHOP] = 303, [ROOK] = 403, + [QUEEN] = 503, [KING] = 603} }, +[ROOK] = { +.value = 500, +.position = { + 0, 0, 0, 20, 20, 0, 0, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 0, 0, 10, 20, 20, 10, 0, 0, + 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, }, +.capture = { [PAWN] = 102, [KNIGHT] = 202, + [BISHOP] = 302, [ROOK] = 402, + [QUEEN] = 502, [KING] = 602} }, +[QUEEN] = { +.value = 1000, +.position = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }, +.capture = { [PAWN] = 101, [KNIGHT] = 201, + [BISHOP] = 301, [ROOK] = 401, + [QUEEN] = 501, [KING] = 601} }, +[KING] = { +.value = 10000, +.position = { + 0, 0, 5, 0, -15, 0, 10, 0, + 0, 5, 5, -5, -5, 0, 5, 0, + 0, 0, 5, 10, 10, 5, 0, 0, + 0, 5, 10, 20, 20, 10, 5, 0, + 0, 5, 10, 20, 20, 10, 5, 0, + 0, 5, 5, 10, 10, 5, 5, 0, + 0, 0, 5, 5, 5, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }, +.capture = { [PAWN] = 100, [KNIGHT] = 200, + [BISHOP] = 300, [ROOK] = 400, + [QUEEN] = 500, [KING] = 600} }, +}; + +const int mirror_score[128] = +{ + a8, b8, c8, d8, e8, f8, g8, h8, + a7, b7, c7, d7, e7, f7, g7, h7, + a6, b6, c6, d6, e6, f6, g6, h6, + a5, b5, c5, d5, e5, f5, g5, h5, + a4, b4, c4, d4, e4, f4, g4, h4, + a3, b3, c3, d3, e3, f3, g3, h3, + a2, b2, c2, d2, e2, f2, g2, h2, + a1, b1, c1, d1, e1, f1, g1, h1, no_sq, +}; +// clang-format on + +int Score_value(ePiece piece) { return Scores[piece].value; } + +int Score_position(ePiece piece, eColor color, Square square) { + if (color == BLACK) + square = mirror_score[square]; + return Scores[piece].position[square]; +} + +int Score_capture(ePiece src, ePiece tgt) { return Scores[src].capture[tgt]; } + +#define MAX_PLY 64 + +long nodes = 0; + +int pv_length[MAX_PLY]; +Move pv_table[MAX_PLY][MAX_PLY]; + +int ply; +Move killer_moves[2][MAX_PLY]; +Move history_moves[16][64]; + +int Move_score(CBoard_T board, Move move) { + if (Move_capture(move)) { + return Score_capture(CBoard_piece_get(board, Move_source(move)), + CBoard_piece_get(board, Move_target(move))); + } else { + if (killer_moves[0][ply] == move) + return 9000; + else if (killer_moves[1][ply] == move) + return 8000; + else + return history_moves[Piece_index(Move_piece(move))][Move_target(move)]; + } + + return 0; +} + void Move_print(Move move) { - int promote = Move_promote(move); printf("%5s %5s %5s %5c %4d %4d %4d %4d\n", square_to_coordinates[Move_source(move)], square_to_coordinates[Move_target(move)], Piece_unicode(Move_piece(move)), - promote ? Piece_asci(Piece_fromIndex(promote)) : 'X', + Move_promote(move) ? Piece_asci(Move_promote(move)) : 'X', Move_capture(move) ? 1 : 0, Move_double(move) ? 1 : 0, Move_enpassant(move) ? 1 : 0, Move_castle(move) ? 1 : 0); } -void Move_print_UCI(Move move) { - int prom; - - printf("%s%s", square_to_coordinates[Move_source(move)], - square_to_coordinates[Move_target(move)]); - if ((prom = Move_promote(move))) - printf(" %c", Piece_asci(Piece_fromIndex(prom))); -} - typedef struct MoveList_T *MoveList_T; struct MoveList_T { Move moves[256]; @@ -71,6 +226,12 @@ MoveList_T MoveList_new(void) { return p; } +void MoveList_free(MoveList_T *p) { FREE(*p); } + +Move MoveList_move(MoveList_T self, int index) { return self->moves[index]; } +int MoveList_size(MoveList_T self) { return self->count; } +void MoveList_reset(MoveList_T self) { self->count = 0; } + void MoveList_add(MoveList_T self, Move move) { self->moves[self->count++] = move; } @@ -82,7 +243,23 @@ void MoveList_print(MoveList_T self) { printf("Total: %d\n", self->count); } -void MoveList_free(MoveList_T *p) { FREE(*p); } +void MoveList_sort(CBoard_T board, MoveList_T list) { + int score[list->count]; + for (int i = 0; i < list->count; i++) + score[i] = Move_score(board, list->moves[i]); + + for (int i = 0; i < list->count; i++) + for (int j = i + 1; j < list->count; j++) + if (score[i] < score[j]) { + Move t = list->moves[i]; + list->moves[i] = list->moves[j]; + list->moves[j] = t; + + int s = score[i]; + score[i] = score[j]; + score[j] = s; + } +} #define pawn_canPromote(color, source) \ ((color == WHITE && source >= a7 && source <= h7) || \ @@ -94,57 +271,56 @@ void MoveList_free(MoveList_T *p) { FREE(*p); } #define pawn_promote(source, target, index, capture) \ for (int i = 1; i < 5; i++) { \ - move = Move_encode(source, target, index, \ - Piece_index(Piece_get(i, color)), capture, 0, 0, 0); \ + move = Move_encode(source, target, index, Piece_get(i, color), capture, 0, \ + 0, 0); \ MoveList_add(moves, move); \ } -MoveList_T generate_moves(CBoard_T cboard, MoveList_T moves) { +MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board) { Move move; Square src, tgt; - eColor color = CBoard_side(cboard); + eColor color = CBoard_side(board); if (!moves) moves = MoveList_new(); { // pawn moves Piece_T Piece = Piece_get(PAWN, color); - int index = Piece_index(Piece); - U64 bitboard = CBoard_pieceSet(cboard, Piece); + U64 bitboard = CBoard_pieceSet(board, Piece); bitboard_for_each_bit(src, bitboard) { { // quiet int add = (color == WHITE) ? +8 : -8; tgt = src + add; - if (tgt > a1 && tgt < h8 && !CBoard_square_isOccupied(cboard, tgt)) { + if (tgt > a1 && tgt < h8 && !CBoard_square_isOccupied(board, tgt)) { if (pawn_canPromote(color, src)) { - pawn_promote(src, tgt, index, 0); + pawn_promote(src, tgt, Piece, 0); } else { - MoveList_add(moves, Move_encode(src, tgt, index, 0, 0, 0, 0, 0)); + MoveList_add(moves, Move_encode(src, tgt, Piece, 0, 0, 0, 0, 0)); // two ahead if (pawn_onStart(color, src) && - !CBoard_square_isOccupied(cboard, tgt += add)) - MoveList_add(moves, Move_encode(src, tgt, index, 0, 0, 1, 0, 0)); + !CBoard_square_isOccupied(board, tgt += add)) + MoveList_add(moves, Move_encode(src, tgt, Piece, 0, 0, 1, 0, 0)); } } } { // capture - U64 attack = CBoard_piece_attacks(cboard, Piece, src) & - CBoard_colorBB(cboard, !color); + U64 attack = CBoard_piece_attacks(board, Piece, src) & + CBoard_colorBB(board, !color); bitboard_for_each_bit(tgt, attack) { if (pawn_canPromote(color, src)) { - pawn_promote(src, tgt, index, 1); + pawn_promote(src, tgt, Piece, 1); } else { - MoveList_add(moves, Move_encode(src, tgt, index, 0, 1, 0, 0, 0)); + MoveList_add(moves, Move_encode(src, tgt, Piece, 0, 1, 0, 0, 0)); } } } { // en passant - if (CBoard_enpassant(cboard) != no_sq && - CBoard_piece_attacks(cboard, Piece, src) & - (C64(1) << CBoard_enpassant(cboard))) - MoveList_add(moves, Move_encode(src, CBoard_enpassant(cboard), index, + if (CBoard_enpassant(board) != no_sq && + CBoard_piece_attacks(board, Piece, src) & + (C64(1) << CBoard_enpassant(board))) + MoveList_add(moves, Move_encode(src, CBoard_enpassant(board), Piece, 0, 1, 0, 1, 0)); } } @@ -153,14 +329,13 @@ MoveList_T generate_moves(CBoard_T cboard, MoveList_T moves) { // All piece move for (int piece = 1; piece < 6; piece++) { Piece_T Piece = Piece_get(piece, color); - U64 bitboard = CBoard_pieceSet(cboard, Piece); + U64 bitboard = CBoard_pieceSet(board, Piece); bitboard_for_each_bit(src, bitboard) { - U64 attack = CBoard_piece_attacks(cboard, Piece, src) & - ~CBoard_colorBB(cboard, color); + U64 attack = CBoard_piece_attacks(board, Piece, src) & + ~CBoard_colorBB(board, color); bitboard_for_each_bit(tgt, attack) { - int take = bit_get(CBoard_colorBB(cboard, !color), tgt); - MoveList_add( - moves, Move_encode(src, tgt, Piece_index(Piece), 0, take, 0, 0, 0)); + int take = bit_get(CBoard_colorBB(board, !color), tgt); + MoveList_add(moves, Move_encode(src, tgt, Piece, 0, take, 0, 0, 0)); } } } @@ -168,38 +343,38 @@ MoveList_T generate_moves(CBoard_T cboard, MoveList_T moves) { // Castling { if (color == WHITE) { - int index = Piece_index(Piece_get(KING, WHITE)); - if (CBoard_castle(cboard) & WK) { - if (!CBoard_square_isOccupied(cboard, f1) && - !CBoard_square_isOccupied(cboard, g1) && - !CBoard_square_isAttack(cboard, e1, BLACK) && - !CBoard_square_isAttack(cboard, f1, BLACK)) - MoveList_add(moves, Move_encode(e1, g1, index, 0, 0, 0, 0, 1)); + Piece_T Piece = Piece_get(KING, WHITE); + if (CBoard_castle(board) & WK) { + if (!CBoard_square_isOccupied(board, f1) && + !CBoard_square_isOccupied(board, g1) && + !CBoard_square_isAttack(board, e1, BLACK) && + !CBoard_square_isAttack(board, f1, BLACK)) + MoveList_add(moves, Move_encode(e1, g1, Piece, 0, 0, 0, 0, 1)); } - if (CBoard_castle(cboard) & WQ) { - if (!CBoard_square_isOccupied(cboard, d1) && - !CBoard_square_isOccupied(cboard, c1) && - !CBoard_square_isOccupied(cboard, b1) && - !CBoard_square_isAttack(cboard, e1, BLACK) && - !CBoard_square_isAttack(cboard, d1, BLACK)) - MoveList_add(moves, Move_encode(e1, c1, index, 0, 0, 0, 0, 1)); + if (CBoard_castle(board) & WQ) { + if (!CBoard_square_isOccupied(board, d1) && + !CBoard_square_isOccupied(board, c1) && + !CBoard_square_isOccupied(board, b1) && + !CBoard_square_isAttack(board, e1, BLACK) && + !CBoard_square_isAttack(board, d1, BLACK)) + MoveList_add(moves, Move_encode(e1, c1, Piece, 0, 0, 0, 0, 1)); } } else { - int index = Piece_index(Piece_get(KING, BLACK)); - if (CBoard_castle(cboard) & BK) { - if (!CBoard_square_isOccupied(cboard, f8) && - !CBoard_square_isOccupied(cboard, g8) && - !CBoard_square_isAttack(cboard, e8, WHITE) && - !CBoard_square_isAttack(cboard, f8, WHITE)) - MoveList_add(moves, Move_encode(e8, g8, index, 0, 0, 0, 0, 1)); + Piece_T Piece = Piece_get(KING, BLACK); + if (CBoard_castle(board) & BK) { + if (!CBoard_square_isOccupied(board, f8) && + !CBoard_square_isOccupied(board, g8) && + !CBoard_square_isAttack(board, e8, WHITE) && + !CBoard_square_isAttack(board, f8, WHITE)) + MoveList_add(moves, Move_encode(e8, g8, Piece, 0, 0, 0, 0, 1)); } - if (CBoard_castle(cboard) & BQ) { - if (!CBoard_square_isOccupied(cboard, d8) && - !CBoard_square_isOccupied(cboard, c8) && - !CBoard_square_isOccupied(cboard, b8) && - !CBoard_square_isAttack(cboard, e8, WHITE) && - !CBoard_square_isAttack(cboard, d8, WHITE)) - MoveList_add(moves, Move_encode(e8, c8, index, 0, 0, 0, 0, 1)); + if (CBoard_castle(board) & BQ) { + if (!CBoard_square_isOccupied(board, d8) && + !CBoard_square_isOccupied(board, c8) && + !CBoard_square_isOccupied(board, b8) && + !CBoard_square_isAttack(board, e8, WHITE) && + !CBoard_square_isAttack(board, d8, WHITE)) + MoveList_add(moves, Move_encode(e8, c8, Piece, 0, 0, 0, 0, 1)); } } } @@ -207,240 +382,60 @@ MoveList_T generate_moves(CBoard_T cboard, MoveList_T moves) { return moves; } -// clang-format off -const int castling_rights[64] = { - 13, 15, 15, 15, 12, 15, 15, 14, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, - 7, 15, 15, 15, 3, 15, 15, 11, -}; -// clang-format on - -int make_move(CBoard_T cboard, Move move, int flag) { +int Move_make(Move move, CBoard_T board, int flag) { if (flag == 0) { Square source = Move_source(move); Square target = Move_target(move); Piece_T Piece = Move_piece(move); - eColor color = CBoard_side(cboard); + eColor color = CBoard_side(board); if (!Move_capture(move)) - CBoard_piece_move(cboard, Piece, source, target); + CBoard_piece_move(board, Piece, source, target); else - CBoard_piece_capture(cboard, Piece, source, target); + CBoard_piece_capture(board, Piece, source, target); if (Move_promote(move)) { - Piece_T Promote = Piece_fromIndex(Move_promote(move)); - CBoard_piece_pop(cboard, Piece, target); - CBoard_piece_set(cboard, Promote, target); + CBoard_piece_pop(board, Piece, target); + CBoard_piece_set(board, Move_promote(move), target); } { int ntarget = target + (color == WHITE ? -8 : +8); if (Move_enpassant(move)) - CBoard_piece_pop(cboard, Piece_get(PAWN, !color), ntarget); + CBoard_piece_pop(board, Piece_get(PAWN, !color), ntarget); - CBoard_enpassant_set(cboard, Move_double(move) ? ntarget : no_sq); + CBoard_enpassant_set(board, Move_double(move) ? ntarget : no_sq); } if (Move_castle(move)) { - Piece_T Rook = Piece_get(ROOK, CBoard_side(cboard)); + Piece_T Rook = Piece_get(ROOK, CBoard_side(board)); switch (target) { - case g1: CBoard_piece_move(cboard, Rook, h1, f1); break; - case c1: CBoard_piece_move(cboard, Rook, a1, d1); break; - case g8: CBoard_piece_move(cboard, Rook, h8, f8); break; - case c8: CBoard_piece_move(cboard, Rook, a8, d8); break; + case g1: CBoard_piece_move(board, Rook, h1, f1); break; + case c1: CBoard_piece_move(board, Rook, a1, d1); break; + case g8: CBoard_piece_move(board, Rook, h8, f8); break; + case c8: CBoard_piece_move(board, Rook, a8, d8); break; default: break; } } - CBoard_castle_and(cboard, castling_rights[source]); - CBoard_castle_and(cboard, castling_rights[target]); + CBoard_castle_and(board, castling_rights[source]); + CBoard_castle_and(board, castling_rights[target]); - if (!CBoard_isCheck(cboard)) { - CBoard_side_switch(cboard); + if (!CBoard_isCheck(board)) { + CBoard_side_switch(board); return 1; } else return 0; } else { if (Move_capture(move)) - return make_move(cboard, move, 0); + return Move_make(move, board, 0); else return 0; } } -long nodes = 0; -struct MoveList_T moveList[10]; - -void perft_driver(CBoard_T self, int depth) { - if (depth == 0) { - nodes++; - return; - } - - MoveList_T moves = generate_moves(self, &moveList[depth]); - CBoard_T copy = CBoard_new(); - - for (int i = 0; i < moves->count; i++) { - CBoard_copy(self, copy); - if (!make_move(copy, moves->moves[i], 0)) - continue; - - perft_driver(copy, depth - 1); - } - moveList[depth].count = 0; -} - -void perft_test(CBoard_T self, int depth) { - MoveList_T moves = generate_moves(self, &moveList[depth]); - CBoard_T copy = CBoard_new(); - long start = get_time_ms(); - - printf("\n Performance test\n\n"); - for (int i = 0; i < moves->count; i++) { - CBoard_copy(self, copy); - if (!make_move(copy, moves->moves[i], 0)) - continue; - long cummulative_nodes = nodes; - perft_driver(copy, depth - 1); - long old_nodes = nodes - cummulative_nodes; - printf("%s%s: %ld\n", square_to_coordinates[Move_source(moves->moves[i])], - square_to_coordinates[Move_target(moves->moves[i])], old_nodes); - } - - printf("\nNodes searched: %ld\n\n", nodes); - return; - printf("\n Depth: %d\n", depth); - printf(" Nodes: %ld\n", nodes); - printf(" Time: %ld\n\n", get_time_ms() - start); -} - -void init_all() { - init_leapers_attacks(); - init_sliders_attacks(); -} - -/* UCI */ - -struct Score_T { - int value; - int score[64]; - int capture[6]; -}; - -// clang-format off -struct Score_T Scores[] = { -[PAWN] = { -.value = 100, -.score = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, -10, -10, 0, 0, 0, - 0, 0, 0, 5, 5, 0, 0, 0, - 5, 5, 10, 20, 20, 5, 5, 5, - 10, 10, 10, 20, 20, 10, 10, 10, - 20, 20, 20, 30, 30, 30, 20, 20, - 30, 30, 30, 40, 40, 30, 30, 30, - 90, 90, 90, 90, 90, 90, 90, 90, }, -.capture = { [PAWN] = 105, [KNIGHT] = 205, - [BISHOP] = 305, [ROOK] = 405, - [QUEEN] = 505, [KING] = 605} }, -[KNIGHT] = { -.value = 300, -.score = { - -5, -10 , 0, 0, 0, 0, -10, -5, - -5, 0, 0, 0, 0, 0, 0, -5, - -5, 5, 20, 10, 10, 20, 5, -5, - -5, 10, 20, 30, 30, 20, 10, -5, - -5, 10, 20, 30, 30, 20, 10, -5, - -5, 5, 20, 20, 20, 20, 5, -5, - -5, 0, 0, 10, 10, 0, 0, -5, - -5, 0, 0, 0, 0, 0, 0, -5, }, -.capture = { [PAWN] = 104, [KNIGHT] = 204, - [BISHOP] = 304, [ROOK] = 404, - [QUEEN] = 504, [KING] = 604} }, -[BISHOP] = { -.value = 350, -.score = { - 0, 0, -10, 0, 0, -10, 0, 0, - 0, 30, 0, 0, 0, 0, 30, 0, - 0, 10, 0, 0, 0, 0, 10, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 0, 0, 0, 10, 10, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, }, -.capture = { [PAWN] = 103, [KNIGHT] = 203, - [BISHOP] = 303, [ROOK] = 403, - [QUEEN] = 503, [KING] = 603} }, -[ROOK] = { -.value = 500, -.score = { - 0, 0, 0, 20, 20, 0, 0, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 0, 0, 10, 20, 20, 10, 0, 0, - 50, 50, 50, 50, 50, 50, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, }, -.capture = { [PAWN] = 102, [KNIGHT] = 202, - [BISHOP] = 302, [ROOK] = 402, - [QUEEN] = 502, [KING] = 602} }, -[QUEEN] = { -.value = 1000, -.score = { - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, }, -.capture = { [PAWN] = 101, [KNIGHT] = 201, - [BISHOP] = 301, [ROOK] = 401, - [QUEEN] = 501, [KING] = 601} }, -[KING] = { -.value = 10000, -.score = { - 0, 0, 5, 0, -15, 0, 10, 0, - 0, 5, 5, -5, -5, 0, 5, 0, - 0, 0, 5, 10, 10, 5, 0, 0, - 0, 5, 10, 20, 20, 10, 5, 0, - 0, 5, 10, 20, 20, 10, 5, 0, - 0, 5, 5, 10, 10, 5, 5, 0, - 0, 0, 5, 5, 5, 5, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, }, -.capture = { [PAWN] = 100, [KNIGHT] = 200, - [BISHOP] = 300, [ROOK] = 400, - [QUEEN] = 500, [KING] = 600} }, -}; - -const int mirror_score[128] = -{ - a8, b8, c8, d8, e8, f8, g8, h8, - a7, b7, c7, d7, e7, f7, g7, h7, - a6, b6, c6, d6, e6, f6, g6, h6, - a5, b5, c5, d5, e5, f5, g5, h5, - a4, b4, c4, d4, e4, f4, g4, h4, - a3, b3, c3, d3, e3, f3, g3, h3, - a2, b2, c2, d2, e2, f2, g2, h2, - a1, b1, c1, d1, e1, f1, g1, h1, no_sq, -}; -// clang-format on - -int Score_value(ePiece piece) { return Scores[piece].value; } - -int Score_score(ePiece piece, eColor color, Square square) { - if (color == BLACK) - square = mirror_score[square]; - return Scores[piece].score[square]; -} +/* SEARCHING */ int evaluate(CBoard_T board) { Square square; @@ -453,10 +448,10 @@ int evaluate(CBoard_T board) { bitboard_for_each_bit(square, bitboard) { if (bit_get(occupancy, square)) { score += Score_value(i); - score += Score_score(i, side, square); + score += Score_position(i, side, square); } else { score -= Score_value(i); - score -= Score_score(i, !side, square); + score -= Score_position(i, !side, square); } } } @@ -464,47 +459,6 @@ int evaluate(CBoard_T board) { return score; } -int pv_length[64]; -Move pv_table[64][64]; - -int ply; -int killer_moves[2][64]; -int history_moves[16][64]; - -int move_score(CBoard_T board, Move move) { - if (Move_capture(move)) { - return Scores[CBoard_piece_get(board, Move_source(move))] - .capture[CBoard_piece_get(board, Move_target(move))]; - } else { - if (killer_moves[0][ply] == move) - return 9000; - else if (killer_moves[1][ply] == move) - return 8000; - else - return history_moves[Piece_index(Move_piece(move))][Move_target(move)]; - } - - return 0; -} - -void MoveList_sort(CBoard_T board, MoveList_T list) { - int score[list->count]; - for (int i = 0; i < list->count; i++) - score[i] = move_score(board, list->moves[i]); - - for (int i = 0; i < list->count; i++) - for (int j = i + 1; j < list->count; j++) - if (score[i] < score[j]) { - Move t = list->moves[i]; - list->moves[i] = list->moves[j]; - list->moves[j] = t; - - int s = score[i]; - score[i] = score[j]; - score[j] = s; - } -} - int quiescence(CBoard_T board, int alpha, int beta) { MoveList_T moves; CBoard_T copy; @@ -521,14 +475,14 @@ int quiescence(CBoard_T board, int alpha, int beta) { } copy = CBoard_new(); - moves = generate_moves(board, NULL); + moves = MoveList_generate(NULL, board); MoveList_sort(board, moves); - for (int i = 0; i < moves->count; i++) { + for (int i = 0; i < MoveList_size(moves); i++) { CBoard_copy(board, copy); ply++; - if (make_move(copy, moves->moves[i], 1) == 0) { + if (Move_make(MoveList_move(moves, i), copy, 1) == 0) { ply--; continue; } @@ -562,10 +516,13 @@ int negamax(CBoard_T board, int alpha, int beta, int depth) { if (depth == 0) return quiescence(board, alpha, beta); + if (ply > MAX_PLY - 1) + return evaluate(board); + nodes++; copy = CBoard_new(); - list = generate_moves(board, NULL); + list = MoveList_generate(NULL, board); isCheck = CBoard_isCheck(board); if (isCheck) @@ -573,12 +530,12 @@ int negamax(CBoard_T board, int alpha, int beta, int depth) { int legal_moves = 0; MoveList_sort(board, list); - for (int i = 0; i < list->count; i++) { - Move move = list->moves[i]; + for (int i = 0; i < MoveList_size(list); i++) { + Move move = MoveList_move(list, i); ply++; CBoard_copy(board, copy); - if (make_move(copy, move, 0) == 0) { + if (Move_make(move, copy, 0) == 0) { ply--; continue; } @@ -622,16 +579,33 @@ int negamax(CBoard_T board, int alpha, int beta, int depth) { return alpha; } +/* UCI */ + +void Move_print_UCI(Move move) { + printf("%s%s", square_to_coordinates[Move_source(move)], + square_to_coordinates[Move_target(move)]); + if (Move_promote(move)) + printf(" %c", Piece_asci(Move_promote(move))); +} + void search_position(CBoard_T board, int depth) { - int score = negamax(board, -50000, 50000, depth); + memset(killer_moves, 0, sizeof(killer_moves)); + memset(history_moves, 0, sizeof(history_moves)); + memset(pv_table, 0, sizeof(pv_table)); + memset(pv_length, 0, sizeof(pv_length)); + nodes = 0; - printf("info score cp %d depth %d nodes %ld pv ", score, depth, nodes); + for (int crnt = 1; crnt <= depth; crnt++) { + int score = negamax(board, -50000, 50000, crnt); - for (int i = 0; i < pv_length[0]; i++) { - Move_print_UCI(pv_table[0][i]); - printf(" "); + printf("info score cp %d depth %d nodes %ld pv ", score, crnt, nodes); + + for (int i = 0; i < pv_length[0]; i++) { + Move_print_UCI(pv_table[0][i]); + printf(" "); + } + printf("\n"); } - printf("\n"); printf("bestmove "); Move_print_UCI(pv_table[0][0]); @@ -700,7 +674,7 @@ char *Instruction_token_next(Instruction_T self) { return Instruction_token_n(self, 1); } -Move parse_move(CBoard_T self, char *move_string) { +Move parse_move(CBoard_T board, char *move_string) { Move result = 0; MoveList_T moves; Square source, target; @@ -708,13 +682,12 @@ Move parse_move(CBoard_T self, char *move_string) { source = coordinates_to_square(move_string); target = coordinates_to_square(move_string + 2); - moves = generate_moves(self, NULL); + moves = MoveList_generate(NULL, board); for (int i = 0; i < moves->count; i++) { Move move = moves->moves[i]; if (Move_source(move) == source && Move_target(move) == target) { if (move_string[4]) { - Piece_T promoted = Piece_fromIndex(Move_promote(move)); - if (tolower(Piece_code(promoted)) != move_string[4]) + if (tolower(Piece_code(Move_promote(move))) != move_string[4]) continue; } result = move; @@ -759,7 +732,7 @@ CBoard_T Instruction_parse(Instruction_T self, CBoard_T board) { while ((token = Instruction_token_next(self))) { Move move = parse_move(board, token); if (move) { - make_move(board, move, 0); + Move_make(move, board, 0); } else { printf("Invalid move %s!\n", token); } @@ -821,9 +794,64 @@ void uci_loop(void) { CBoard_free(&board); } +/* PERFT */ + +struct MoveList_T moveList[10]; + +void perft_driver(CBoard_T board, int depth) { + if (depth == 0) { + nodes++; + return; + } + + MoveList_T list = MoveList_generate(&moveList[depth], board); + CBoard_T copy = CBoard_new(); + + for (int i = 0; i < MoveList_size(list); i++) { + CBoard_copy(board, copy); + if (!Move_make(MoveList_move(list, i), copy, 0)) + continue; + perft_driver(copy, depth - 1); + } + MoveList_reset(list); +} + +void perft_test(CBoard_T board, int depth) { + MoveList_T list = MoveList_generate(&moveList[depth], board); + CBoard_T copy = CBoard_new(); + long start = get_time_ms(); + + printf("\n Performance test\n\n"); + for (int i = 0; i < MoveList_size(list); i++) { + CBoard_copy(board, copy); + Move move = MoveList_move(list, i); + if (!Move_make(MoveList_move(list, i), copy, 0)) + continue; + long cummulative_nodes = nodes; + perft_driver(copy, depth - 1); + long old_nodes = nodes - cummulative_nodes; + printf("%s%s: %ld\n", square_to_coordinates[Move_source(move)], + square_to_coordinates[Move_target(move)], old_nodes); + } + MoveList_reset(list); + + printf("\nNodes searched: %ld\n\n", nodes); + return; + printf("\n Depth: %d\n", depth); + printf(" Nodes: %ld\n", nodes); + printf(" Time: %ld\n\n", get_time_ms() - start); +} + +/* MAIN */ + +void init_all() { + init_leapers_attacks(); + init_sliders_attacks(); +} + int main(void) { init_all(); - int debug = 0; + int debug = 1; if (debug) { printf("debugging!\n"); CBoard_T board = NULL;