stellar

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

commit 3d9fb77e4f91af7bf2fc59cfb14c012007cb9ae0
parent 11aa12bf92ffa2c1f685a8ec4f9dfe4135223e7a
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Fri, 28 Jul 2023 23:21:40 +0200

Split engine into multiple files for easy refactor

Diffstat:
Ainclude/moves.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/perft.h | 9+++++++++
Ainclude/score.h | 10++++++++++
Msrc/engine.c | 544+------------------------------------------------------------------------------
Asrc/moves.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/perft.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/score.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 592 insertions(+), 541 deletions(-)

diff --git a/include/moves.h b/include/moves.h @@ -0,0 +1,54 @@ +#ifndef MOVES_H +#define MOVES_H + +#include <stdint.h> + +#include "CBoard.h" + +typedef struct Move Move; +struct Move { + unsigned source : 6; + unsigned target : 6; + unsigned piece : 5; + unsigned piece_capture : 5; + unsigned piece_promote : 5; + unsigned dbl : 1; + unsigned enpassant : 1; + unsigned castle : 1; + unsigned capture : 1; + unsigned promote : 1; +}; + +typedef struct MoveList_T *MoveList_T; +struct MoveList_T { + Move moves[256]; + int count; +}; + +int Move_cmp(Move a, Move b); +Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture, + Piece_T Promote, int dbl, int enpassant, int castle); +void Move_print(Move move); +MoveList_T MoveList_new(void); +void MoveList_free(MoveList_T *p); +Move MoveList_move(MoveList_T self, int index); +int MoveList_size(MoveList_T self); +void MoveList_reset(MoveList_T self); +void MoveList_add(MoveList_T self, Move move); +void MoveList_print(MoveList_T self); +MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board); +int Move_make(Move move, CBoard_T board, int flag); + +#define Move_source(move) (move.source) +#define Move_target(move) (move.target) +#define Move_double(move) (move.dbl) +#define Move_enpassant(move) (move.enpassant) +#define Move_castle(move) (move.castle) +#define Move_capture(move) (move.capture) +#define Move_promote(move) (move.promote) + +#define Move_piece(move) (Piece_fromIndex(move.piece)) +#define Move_piece_capture(move) (Piece_fromIndex(move.piece_capture)) +#define Move_piece_promote(move) (Piece_fromIndex(move.piece_promote)) + +#endif diff --git a/include/perft.h b/include/perft.h @@ -0,0 +1,9 @@ +#ifndef PERFT_H +#define PERFT_H + +#include "CBoard.h" + +void perft_test_threaded(CBoard_T board, int depth); +void perft_test(CBoard_T board, int depth); + +#endif diff --git a/include/score.h b/include/score.h @@ -0,0 +1,10 @@ +#ifndef SCORE_H +#define SCORE_H + +#include "CBoard.h" + +int Score_capture(ePiece src, ePiece tgt); +int Score_position(ePiece piece, eColor color, Square square); +int Score_value(ePiece piece); + +#endif diff --git a/src/engine.c b/src/engine.c @@ -1,11 +1,13 @@ #include <ctype.h> -#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "CBoard.h" #include "attack.h" +#include "moves.h" +#include "perft.h" +#include "score.h" #include "utils.h" #include <cul/assert.h> @@ -24,177 +26,6 @@ #define cmk_position \ "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 " -typedef struct Move Move; -struct Move { - unsigned source : 6; - unsigned target : 6; - unsigned piece : 5; - unsigned piece_capture : 5; - unsigned piece_promote : 5; - unsigned dbl : 1; - unsigned enpassant : 1; - unsigned castle : 1; - unsigned capture : 1; - unsigned promote : 1; -}; - -int Move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; } - -Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture, - Piece_T Promote, int dbl, int enpassant, int castle) { - return (Move){ - .source = src, - .target = tgt, - .dbl = dbl, - .enpassant = enpassant, - .castle = castle, - .capture = Capture != NULL, - .promote = Promote != NULL, - .piece = Piece_index(Piece), - .piece_capture = Capture ? Piece_index(Capture) : 0, - .piece_promote = Promote ? Piece_index(Promote) : 0, - }; -} - -#define Move_source(move) (move.source) -#define Move_target(move) (move.target) -#define Move_double(move) (move.dbl) -#define Move_enpassant(move) (move.enpassant) -#define Move_castle(move) (move.castle) -#define Move_capture(move) (move.capture) -#define Move_promote(move) (move.promote) - -#define Move_piece(move) (Piece_fromIndex(move.piece)) -#define Move_piece_capture(move) (Piece_fromIndex(move.piece_capture)) -#define Move_piece_promote(move) (Piece_fromIndex(move.piece_promote)) - -// 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 typedef struct Stats_T *Stats_T; @@ -232,47 +63,6 @@ int Move_score(Stats_T stats, Move move) { return 0; } -void Move_print(Move move) { - printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n", - square_to_coordinates[Move_source(move)], - square_to_coordinates[Move_target(move)], - Piece_unicode(Move_piece(move)), - Move_capture(move) ? Piece_unicode(Move_piece_capture(move)) : "X ", - Move_promote(move) ? Piece_unicode(Move_piece_promote(move)) : "X ", - Move_double(move) ? 1 : 0, Move_enpassant(move) ? 1 : 0, - Move_castle(move) ? 1 : 0, Move_capture(move) ? 1 : 0, - Move_promote(move) ? 1 : 0); -} - -typedef struct MoveList_T *MoveList_T; -struct MoveList_T { - Move moves[256]; - int count; -}; - -MoveList_T MoveList_new(void) { - MoveList_T p; - NEW0(p); - 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; -} - -void MoveList_print(MoveList_T self) { - printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n"); - for (int i = 0; i < self->count; i++) - Move_print(self->moves[i]); - printf("Total: %d\n", self->count); -} - void MoveList_sort(Stats_T stats, MoveList_T list) { int score[list->count]; for (int i = 0; i < list->count; i++) @@ -291,206 +81,6 @@ void MoveList_sort(Stats_T stats, MoveList_T list) { } } -#define pawn_canPromote(color, source) \ - ((color == WHITE && source >= a7 && source <= h7) || \ - (color == BLACK && source >= a2 && source <= h2)) - -#define pawn_onStart(color, source) \ - ((color == BLACK && source >= a7 && source <= h7) || \ - (color == WHITE && source >= a2 && source <= h2)) - -#define pawn_promote(source, target, Piece, Capture) \ - for (int i = 1; i < 5; i++) { \ - move = Move_encode(source, target, Piece, Capture, \ - Piece_get(i, color), 0, 0, 0); \ - MoveList_add(moves, move); \ - } - -MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board) { - Move move; - Square src, tgt; - eColor color = CBoard_side(board); - - if (!moves) moves = MoveList_new(); - - { // pawn moves - Piece_T Piece = Piece_get(PAWN, color); - 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(board, tgt)) { - if (pawn_canPromote(color, src)) { - pawn_promote(src, tgt, Piece, 0); - } else { - MoveList_add( - moves, Move_encode(src, tgt, Piece, 0, 0, 0, 0, 0)); - - // two ahead - if (pawn_onStart(color, src) && - !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(board, Piece, src) & - CBoard_colorBB(board, !color); - bitboard_for_each_bit(tgt, attack) { - if (pawn_canPromote(color, src)) { - pawn_promote(src, tgt, Piece, - CBoard_square_piece(board, tgt, !color)); - } else { - MoveList_add(moves, Move_encode(src, tgt, Piece, - CBoard_square_piece( - board, tgt, !color), - 0, 0, 0, 0)); - } - } - } - - { // en passant - 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, - CBoard_square_piece(board, tgt, !color), 0, - 0, 1, 0)); - } - } - } - - // All piece move - for (int piece = 1; piece < 6; piece++) { - Piece_T Piece = Piece_get(piece, color); - U64 bitboard = CBoard_pieceSet(board, Piece); - bitboard_for_each_bit(src, bitboard) { - U64 attack = CBoard_piece_attacks(board, Piece, src) & - ~CBoard_colorBB(board, color); - bitboard_for_each_bit(tgt, attack) { - /* int take = bit_get(CBoard_colorBB(board, !color), tgt); */ - MoveList_add( - moves, Move_encode(src, tgt, Piece, - CBoard_square_piece(board, tgt, !color), - 0, 0, 0, 0)); - } - } - } - - // Castling - { - if (color == WHITE) { - 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(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 { - 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(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)); - } - } - } - - return moves; -} - -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(board); - - if (!Move_capture(move)) - CBoard_piece_move(board, Piece, source, target); - else - CBoard_piece_capture(board, Piece, Move_piece_capture(move), source, - target); - - if (Move_promote(move)) { - CBoard_piece_pop(board, Piece, target); - CBoard_piece_set(board, Move_piece_promote(move), target); - } - - { - int ntarget = target + (color == WHITE ? -8 : +8); - if (Move_enpassant(move)) - CBoard_piece_pop(board, Piece_get(PAWN, !color), ntarget); - - CBoard_enpassant_set(board, Move_double(move) ? ntarget : no_sq); - } - - if (Move_castle(move)) { - Piece_T Rook = Piece_get(ROOK, CBoard_side(board)); - switch (target) { - 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(board, castling_rights[source]); - CBoard_castle_and(board, castling_rights[target]); - - if (!CBoard_isCheck(board)) { - CBoard_side_switch(board); - return 1; - } else - return 0; - } else { - if (Move_capture(move)) - return Move_make(move, board, 0); - else - return 0; - } -} - /* SEARCHING */ int evaluate(CBoard_T board) { @@ -629,8 +219,6 @@ int negamax(Stats_T stats, 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)]); @@ -840,132 +428,6 @@ void uci_loop(void) { CBoard_free(&board); } -/* PERFT */ - -struct MoveList_T moveList[10]; -long nodes; - -void perft_driver(CBoard_T board, struct MoveList_T *moveList, int depth, - unsigned long long *nodes) { - 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, moveList, depth - 1, nodes); - } - - MoveList_reset(list); - CBoard_free(&copy); -} - -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"); - - nodes = 0; - 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; - unsigned long long node = 0; - perft_driver(copy, moveList, depth - 1, &node); - printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)], - square_to_coordinates[Move_target(move)], node); - nodes += node; - } - MoveList_reset(list); - CBoard_free(&copy); - - printf("\nNodes searched: %ld\n\n", nodes); - printf("\n Depth: %d\n", depth); - printf(" Nodes: %ld\n", nodes); - printf(" Time: %ld\n\n", get_time_ms() - start); -} - -#include <pthread.h> -#include <semaphore.h> - -typedef struct perf_shared perf_shared; -struct perf_shared { - CBoard_T board; - MoveList_T list; - int depth; - sem_t *mutex; - int *index; - sem_t *finish; - unsigned long long *total; -}; - -void *perft_thread(void *arg) { - perf_shared *shared = (perf_shared *)arg; - CBoard_T board = CBoard_new(); - unsigned long long node_count = 0; - - struct MoveList_T moveList[10]; - - while (1) { - sem_wait(shared->mutex); - *shared->total += node_count; - if (*shared->index >= MoveList_size(shared->list)) { - sem_post(shared->mutex); - break; - } - Move move = MoveList_move(shared->list, (*shared->index)++); - sem_post(shared->mutex); - - CBoard_copy(shared->board, board); - if (!Move_make(move, board, 0)) continue; - - node_count = 0; - perft_driver(board, moveList, shared->depth, &node_count); - printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)], - square_to_coordinates[Move_target(move)], node_count); - } - CBoard_free(&board); - sem_post(shared->finish); - return NULL; -} - -void perft_test_threaded(CBoard_T board, int depth) { - MoveList_T list = MoveList_generate(NULL, board); - int size = 8; - - unsigned long long total = 0; - perf_shared shared[size]; - pthread_t threads[size]; - sem_t mutex, finish; - - int index = 0; - sem_init(&mutex, 0, 1); - sem_init(&finish, 0, 0); - for (int i = 0; i < size; i++) { - shared[i].board = board; - shared[i].list = list; - shared[i].depth = depth - 1; - shared[i].mutex = &mutex; - shared[i].index = &index; - shared[i].finish = &finish; - shared[i].total = &total; - pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i)); - } - - for (int i = 0; i < size; i++) - sem_wait(&finish); - MoveList_free(&list); - - printf("Nodes processed: %llu\n", total); -} - /* MAIN */ void init_all() { diff --git a/src/moves.c b/src/moves.c @@ -0,0 +1,272 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <cul/mem.h> + +#include "moves.h" + +int Move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; } + +Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture, + Piece_T Promote, int dbl, int enpassant, int castle) { + return (Move){ + .source = src, + .target = tgt, + .dbl = dbl, + .enpassant = enpassant, + .castle = castle, + .capture = Capture != NULL, + .promote = Promote != NULL, + .piece = Piece_index(Piece), + .piece_capture = Capture ? Piece_index(Capture) : 0, + .piece_promote = Promote ? Piece_index(Promote) : 0, + }; +} + +void Move_print(Move move) { + printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n", + square_to_coordinates[Move_source(move)], + square_to_coordinates[Move_target(move)], + Piece_unicode(Move_piece(move)), + Move_capture(move) ? Piece_unicode(Move_piece_capture(move)) : "X ", + Move_promote(move) ? Piece_unicode(Move_piece_promote(move)) : "X ", + Move_double(move) ? 1 : 0, Move_enpassant(move) ? 1 : 0, + Move_castle(move) ? 1 : 0, Move_capture(move) ? 1 : 0, + Move_promote(move) ? 1 : 0); +} + +MoveList_T MoveList_new(void) { + MoveList_T p; + NEW0(p); + 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; +} + +void MoveList_print(MoveList_T self) { + printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n"); + for (int i = 0; i < self->count; i++) + Move_print(self->moves[i]); + printf("Total: %d\n", self->count); +} + +#define pawn_canPromote(color, source) \ + ((color == WHITE && source >= a7 && source <= h7) || \ + (color == BLACK && source >= a2 && source <= h2)) + +#define pawn_onStart(color, source) \ + ((color == BLACK && source >= a7 && source <= h7) || \ + (color == WHITE && source >= a2 && source <= h2)) + +#define pawn_promote(source, target, Piece, Capture) \ + for (int i = 1; i < 5; i++) { \ + move = Move_encode(source, target, Piece, Capture, \ + Piece_get(i, color), 0, 0, 0); \ + MoveList_add(moves, move); \ + } + +MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board) { + Move move; + Square src, tgt; + eColor color = CBoard_side(board); + + if (!moves) moves = MoveList_new(); + + { // pawn moves + Piece_T Piece = Piece_get(PAWN, color); + 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(board, tgt)) { + if (pawn_canPromote(color, src)) { + pawn_promote(src, tgt, Piece, 0); + } else { + MoveList_add( + moves, Move_encode(src, tgt, Piece, 0, 0, 0, 0, 0)); + + // two ahead + if (pawn_onStart(color, src) && + !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(board, Piece, src) & + CBoard_colorBB(board, !color); + bitboard_for_each_bit(tgt, attack) { + if (pawn_canPromote(color, src)) { + pawn_promote(src, tgt, Piece, + CBoard_square_piece(board, tgt, !color)); + } else { + MoveList_add(moves, Move_encode(src, tgt, Piece, + CBoard_square_piece( + board, tgt, !color), + 0, 0, 0, 0)); + } + } + } + + { // en passant + 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, + CBoard_square_piece(board, tgt, !color), 0, + 0, 1, 0)); + } + } + } + + // All piece move + for (int piece = 1; piece < 6; piece++) { + Piece_T Piece = Piece_get(piece, color); + U64 bitboard = CBoard_pieceSet(board, Piece); + bitboard_for_each_bit(src, bitboard) { + U64 attack = CBoard_piece_attacks(board, Piece, src) & + ~CBoard_colorBB(board, color); + bitboard_for_each_bit(tgt, attack) { + /* int take = bit_get(CBoard_colorBB(board, !color), tgt); */ + MoveList_add( + moves, Move_encode(src, tgt, Piece, + CBoard_square_piece(board, tgt, !color), + 0, 0, 0, 0)); + } + } + } + + // Castling + { + if (color == WHITE) { + 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(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 { + 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(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)); + } + } + } + + 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 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(board); + + if (!Move_capture(move)) + CBoard_piece_move(board, Piece, source, target); + else + CBoard_piece_capture(board, Piece, Move_piece_capture(move), source, + target); + + if (Move_promote(move)) { + CBoard_piece_pop(board, Piece, target); + CBoard_piece_set(board, Move_piece_promote(move), target); + } + + { + int ntarget = target + (color == WHITE ? -8 : +8); + if (Move_enpassant(move)) + CBoard_piece_pop(board, Piece_get(PAWN, !color), ntarget); + + CBoard_enpassant_set(board, Move_double(move) ? ntarget : no_sq); + } + + if (Move_castle(move)) { + Piece_T Rook = Piece_get(ROOK, CBoard_side(board)); + switch (target) { + 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(board, castling_rights[source]); + CBoard_castle_and(board, castling_rights[target]); + + if (!CBoard_isCheck(board)) { + CBoard_side_switch(board); + return 1; + } else + return 0; + } else { + if (Move_capture(move)) + return Move_make(move, board, 0); + else + return 0; + } +} diff --git a/src/perft.c b/src/perft.c @@ -0,0 +1,127 @@ +#include <pthread.h> +#include <semaphore.h> +#include <stdio.h> + +#include "moves.h" +#include "perft.h" + +struct MoveList_T moveList[10]; +long nodes; + +void perft_driver(CBoard_T board, struct MoveList_T *moveList, int depth, + unsigned long long *nodes) { + 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, moveList, depth - 1, nodes); + } + + MoveList_reset(list); + CBoard_free(&copy); +} + +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"); + + nodes = 0; + 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; + unsigned long long node = 0; + perft_driver(copy, moveList, depth - 1, &node); + printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)], + square_to_coordinates[Move_target(move)], node); + nodes += node; + } + MoveList_reset(list); + CBoard_free(&copy); + + printf("\nNodes searched: %ld\n\n", nodes); + printf("\n Depth: %d\n", depth); + printf(" Nodes: %ld\n", nodes); + printf(" Time: %ld\n\n", get_time_ms() - start); +} + +typedef struct perf_shared perf_shared; +struct perf_shared { + CBoard_T board; + MoveList_T list; + int depth; + sem_t *mutex; + int *index; + sem_t *finish; + unsigned long long *total; +}; + +void *perft_thread(void *arg) { + perf_shared *shared = (perf_shared *)arg; + CBoard_T board = CBoard_new(); + unsigned long long node_count = 0; + + struct MoveList_T moveList[10]; + + while (1) { + sem_wait(shared->mutex); + *shared->total += node_count; + if (*shared->index >= MoveList_size(shared->list)) { + sem_post(shared->mutex); + break; + } + Move move = MoveList_move(shared->list, (*shared->index)++); + sem_post(shared->mutex); + + CBoard_copy(shared->board, board); + if (!Move_make(move, board, 0)) continue; + + node_count = 0; + perft_driver(board, moveList, shared->depth, &node_count); + printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)], + square_to_coordinates[Move_target(move)], node_count); + } + CBoard_free(&board); + sem_post(shared->finish); + return NULL; +} + +void perft_test_threaded(CBoard_T board, int depth) { + MoveList_T list = MoveList_generate(NULL, board); + int size = 8; + + unsigned long long total = 0; + perf_shared shared[size]; + pthread_t threads[size]; + sem_t mutex, finish; + + int index = 0; + sem_init(&mutex, 0, 1); + sem_init(&finish, 0, 0); + for (int i = 0; i < size; i++) { + shared[i].board = board; + shared[i].list = list; + shared[i].depth = depth - 1; + shared[i].mutex = &mutex; + shared[i].index = &index; + shared[i].finish = &finish; + shared[i].total = &total; + pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i)); + } + + for (int i = 0; i < size; i++) + sem_wait(&finish); + MoveList_free(&list); + + printf("Nodes processed: %llu\n", total); +} diff --git a/src/score.c b/src/score.c @@ -0,0 +1,117 @@ +#include "score.h" + +// clang-format off +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]; }