stellar

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

commit d02e946f5b524acd6871c196d4ef505cfa0b5e0a
parent 12abec85ea91fce6605ed4f197efa5aa75d9ca88
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Wed,  9 Aug 2023 23:36:20 +0200

Rest of the rewrite, - debugging...

Diffstat:
Msrc/attacks/CMakeLists.txt | 1-
Msrc/attacks/magic_generate.cpp | 17+++++++++++------
Msrc/board/CMakeLists.txt | 4++--
Msrc/board/board.cpp | 23+++++++++++++----------
Msrc/engine/CMakeLists.txt | 7+++----
Dsrc/engine/engine.c | 556-------------------------------------------------------------------------------
Asrc/engine/engine.cpp | 520+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/engine/score.c | 141-------------------------------------------------------------------------------
Asrc/engine/score.cpp | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/engine/score.h | 13-------------
Asrc/engine/score.hpp | 17+++++++++++++++++
Dsrc/engine/stats.h | 22----------------------
Asrc/engine/stats.hpp | 22++++++++++++++++++++++
Dsrc/engine/transposition.c | 88-------------------------------------------------------------------------------
Asrc/engine/transposition.cpp | 38++++++++++++++++++++++++++++++++++++++
Dsrc/engine/transposition.h | 30------------------------------
Asrc/engine/transposition.hpp | 44++++++++++++++++++++++++++++++++++++++++++++
Msrc/include/board.hpp | 7+++----
Dsrc/include/moves.h | 67-------------------------------------------------------------------
Asrc/include/moves.hpp | 48++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/include/perft.h | 24------------------------
Asrc/include/perft.hpp | 24++++++++++++++++++++++++
Msrc/include/piece.hpp | 4+++-
Dsrc/include/random.h | 10----------
Dsrc/include/utils.h | 79-------------------------------------------------------------------------------
Msrc/include/utils_cpp.hpp | 4++++
Msrc/moves/CMakeLists.txt | 6+++---
Dsrc/moves/moves.c | 81-------------------------------------------------------------------------------
Asrc/moves/moves.cpp | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/moves/moves_generate.c | 131-------------------------------------------------------------------------------
Asrc/moves/moves_generate.cpp | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/moves/moves_make.c | 99-------------------------------------------------------------------------------
Asrc/moves/moves_make.cpp | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/perft/CMakeLists.txt | 3+--
Dsrc/perft/perft.c | 183-------------------------------------------------------------------------------
Asrc/perft/perft.cpp | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/piece/piece.cpp | 5-----
Msrc/utils/CMakeLists.txt | 10+---------
Dsrc/utils/random.c | 26--------------------------
Asrc/utils/random.cpp | 26++++++++++++++++++++++++++
Dsrc/utils/utils.c | 64----------------------------------------------------------------
41 files changed, 1392 insertions(+), 1661 deletions(-)

diff --git a/src/attacks/CMakeLists.txt b/src/attacks/CMakeLists.txt @@ -25,7 +25,6 @@ add_executable(magic ) target_link_libraries(magic - PRIVATE utils PRIVATE random ) diff --git a/src/attacks/magic_generate.cpp b/src/attacks/magic_generate.cpp @@ -1,5 +1,6 @@ -#include "internal.h" -#include "random.h" +#include "internal.hpp" +#include "random.hpp" +#include "utils_cpp.hpp" #include <inttypes.h> #include <stdio.h> @@ -54,11 +55,15 @@ int main(void) { random_state_reset(); printf("Bishup Magic Numbers:\n"); - for (int i = 0; i < 64; i++) - printf(FORMAT, find_magic_number(i, bishop_relevant_bits[i], 1)); + for (Square square : SquareIter()) + printf(FORMAT, + find_magic_number( + square, bishop_relevant_bits[to_underlying(square)], 1)); printf("Rook Magic Numbers:\n"); - for (int i = 0; i < 64; i++) - printf(FORMAT, find_magic_number(i, rook_relevant_bits[i], 0)); + for (Square square : SquareIter()) + printf(FORMAT, + find_magic_number(square, + rook_relevant_bits[to_underlying(square)], 0)); return 0; } diff --git a/src/board/CMakeLists.txt b/src/board/CMakeLists.txt @@ -1,6 +1,6 @@ add_library(board OBJECT - board.c - zobrist.c + board.cpp + zobrist.cpp ) target_include_directories(board diff --git a/src/board/board.cpp b/src/board/board.cpp @@ -1,4 +1,5 @@ #include <ctype.h> +#include <exception> #include <stdio.h> #include <string.h> @@ -61,14 +62,19 @@ piece::Type Board::get_square_piece_type(Square square) const { throw std::exception(); } -const piece::Piece &Board::get_square_piece(Square square) const { - return piece::get(get_square_piece_type(square), - get_square_piece_color(square)); +const piece::Piece *Board::get_square_piece(Square square) const { + try { + return &piece::get(get_square_piece_type(square), + get_square_piece_color(square)); + } catch (std::exception e) { + return nullptr; + } } /* Setters */ -void Board::and_castle(Castle right) { castle &= to_underlying(right); } +void Board::xor_hash(U64 op) { hash ^= op; } +void Board::and_castle(uint8_t right) { castle &= right; } void Board::switch_side(void) { side = (side == Color::BLACK) ? Color::WHITE : Color::BLACK; @@ -186,12 +192,9 @@ Board::Board(const std::string &fen) { std::ostream &operator<<(std::ostream &os, const Board &board) { for (int rank = 0; rank < 8; rank++) { for (int file = 0; file < 8; file++) { - try { - Square square = static_cast<Square>((7 - rank) * 8 + file); - os << board.get_square_piece(square).code; - } catch (std::exception e) { - os << ". "; - } + Square square = static_cast<Square>((7 - rank) * 8 + file); + const piece::Piece *piece = board.get_square_piece(square); + os << (piece ? piece->code : '.') << " "; } printf("\n"); } diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt @@ -1,7 +1,7 @@ add_executable(engine - engine.c - score.c - transposition.c + engine.cpp + score.cpp + transposition.cpp ) target_link_libraries(engine @@ -9,7 +9,6 @@ target_link_libraries(engine PRIVATE board PRIVATE moves PRIVATE piece - PRIVATE utils PRIVATE random ) diff --git a/src/engine/engine.c b/src/engine/engine.c @@ -1,556 +0,0 @@ -#include <ctype.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "attacks.h" -#include "board.h" -#include "moves.h" -#include "perft.h" -#include "score.h" -#include "stats.h" -#include "transposition.h" -#include "utils.h" -#include "zobrist.h" - -#include <cul/assert.h> -#include <cul/mem.h> -#include <cul/utils.h> - -#define FULL_DEPTH 4 -#define REDUCTION_LIMIT 3 -#define REDUCTION_MOVE 2 - -#define WINDOW 50 - -void move_list_sort_pv(MoveList *list, Stats *stats, Move best) { - assert(list && stats); - - for (int i = 0; i < move_list_size(list); i++) { - const Move move = move_list_index_move(list, i); - if (move_cmp(best, move)) { - move_list_index_score_set(list, i, 30000); - return; - } - } - - if (stats->ply && stats->follow_pv) { - stats->follow_pv = 0; - for (int i = 0; i < move_list_size(list); i++) { - const Move move = move_list_index_move(list, i); - if (move_cmp(stats->pv_table[0][stats->ply], move)) { - move_list_index_score_set(list, i, 20000); - stats->follow_pv = 1; - break; - } - } - } -} - -/* SEARCHING */ - -int evaluate(const Board *board) { - assert(board); - - Square square; - eColor side = board_side(board); - U64 occupancy = board_color(board, side); - - int score = 0; - for (int i = 0; i < 6; i++) { - U64 bitboard = board_piece(board, i); - bitboard_for_each_bit(square, bitboard) { - if (bit_get(occupancy, square)) { - score += Score_value(i); - score += Score_position(i, side, square); - } else { - score -= Score_value(i); - score -= Score_position(i, !side, square); - } - } - } - - return score; -} - -int is_repetition() { return 0; } - -int stats_move_make(Stats *self, Board *copy, Move move, int flag) { - assert(self && copy); - - board_copy(self->board, copy); - if (!move_make(move, self->board, flag)) { - board_copy(copy, self->board); - return 0; - } - self->ply++; - return 1; -} - -void stats_move_make_pruning(Stats *self, Board *copy) { - assert(self && copy); - - board_copy(self->board, copy); - board_side_switch(self->board); - board_enpassant_set(self->board, no_sq); - self->ply++; -} - -void stats_move_unmake(Stats *self, Board *copy) { - assert(self && copy); - - board_copy(copy, self->board); - self->ply--; -} - -void stats_pv_store(Stats *self, Move move) { - assert(self); - - const int ply = self->ply; - self->pv_table[ply][ply] = move; - for (int i = ply + 1; i < self->pv_length[ply + 1]; i++) { - self->pv_table[ply][i] = self->pv_table[ply + 1][i]; - } - self->pv_length[ply] = self->pv_length[ply + 1]; -} - -int quiescence(Stats *stats, int alpha, int beta) { - assert(stats); - - stats->pv_length[stats->ply] = stats->ply; - stats->nodes++; - - int score = evaluate(stats->board); - if (stats->ply > MAX_PLY - 1) return score; - if (score >= beta) return beta; - if (score > alpha) alpha = score; - - Board copy; - MoveList list; - move_list_generate(&list, stats->board); - Score_move_list(stats, &list); - move_list_sort_pv(&list, stats, (Move){0}); - move_list_sort(&list); - - for (int i = 0; i < move_list_size(&list); i++) { - Move move = move_list_index_move(&list, i); - if (!stats_move_make(stats, &copy, move, 1)) continue; - score = -quiescence(stats, -beta, -alpha); - stats_move_unmake(stats, &copy); - - if (score > alpha) { - alpha = score; - stats_pv_store(stats, move); - if (score >= beta) return beta; - } - } - - return alpha; -} - -int negamax(Stats *stats, int alpha, int beta, int depth, bool null) { - assert(stats); - - int pv_node = (beta - alpha) > 1; - HasheFlag flag = flagAlpha; - Move bestMove = {0}; - int futility = 0; - Board copy; - - stats->pv_length[stats->ply] = stats->ply; - - int score = ttable_read(stats, &bestMove, alpha, beta, depth); - if (stats->ply && score != TTABLE_UNKNOWN && !pv_node) return score; - - // && fifty >= 100 - if (stats->ply && is_repetition()) return 0; - if (depth == 0) { - stats->nodes++; - int score = quiescence(stats, alpha, beta); - // ttable_write(stats, bestMove, score, depth, flagExact); - return score; - } - - if (alpha < -MATE_VALUE) alpha = -MATE_VALUE; - if (beta > MATE_VALUE - 1) beta = MATE_VALUE - 1; - if (alpha >= beta) return alpha; - // if (stats->ply > MAX_PLY - 1) return evaluate(stats->board); - - int isCheck = board_isCheck(stats->board); - if (isCheck) depth++; - - if (!pv_node && !isCheck) { - int staticEval = evaluate(stats->board); - - // evaluation pruning - if (depth < 3 && abs(beta - 1) > -MATE_VALUE + 100) { - int marginEval = Score_value(PAWN) * depth; - if (staticEval - marginEval >= beta) return staticEval - marginEval; - } - - if (null) { - // null move pruning - if (stats->ply && depth > 2 && staticEval >= beta) { - stats_move_make_pruning(stats, &copy); - score = -negamax(stats, -beta, -beta + 1, - depth - 1 - REDUCTION_MOVE, false); - stats_move_unmake(stats, &copy); - if (score >= beta) return beta; - } - - // razoring - score = staticEval + Score_value(PAWN); - int scoreNew = quiescence(stats, alpha, beta); - - if (score < beta && depth == 1) { - return (scoreNew > score) ? scoreNew : score; - } - - score += Score_value(PAWN); - if (score < beta && depth < 4) { - if (scoreNew < beta) - return (scoreNew > score) ? scoreNew : score; - } - } - - // futility pruning condition - static const int margin[] = {0, 100, 300, 500}; - if (depth < 4 && abs(alpha) < MATE_SCORE && - staticEval + margin[depth] <= alpha) - futility = 1; - } - - MoveList list; - move_list_generate(&list, stats->board); - Score_move_list(stats, &list); - move_list_sort_pv(&list, stats, bestMove); - move_list_sort(&list); - - int legal_moves = 0; - int searched = 0; - for (int i = 0; i < move_list_size(&list); i++) { - const Move move = move_list_index_move(&list, i); - if (!stats_move_make(stats, &copy, move, 0)) continue; - legal_moves++; - - // futility pruning - if (futility && searched && !move_capture(move) && - !move_promote(move) && !board_isCheck(stats->board)) { - stats_move_unmake(stats, &copy); - continue; - } - - if (!searched) { - score = -negamax(stats, -beta, -alpha, depth - 1, true); - } else { - // Late Move Reduction - if (!pv_node && searched >= FULL_DEPTH && - depth >= REDUCTION_LIMIT && !isCheck && !move_capture(move) && - !move_promote(move) && - (move_source(move) != - move_source(stats->killer[0][stats->ply]) || - move_target(move) != - move_target(stats->killer[0][stats->ply])) && - (move_source(move) != - move_source(stats->killer[1][stats->ply]) || - move_target(move) != - move_target(stats->killer[1][stats->ply]))) { - score = -negamax(stats, -alpha - 1, -alpha, depth - 2, true); - } else - score = alpha + 1; - - // found better move - // Principal Variation Search - if (score > alpha) { - score = -negamax(stats, -alpha - 1, -alpha, depth - 1, true); - - // if fail research - if ((score > alpha) && (score < beta)) - score = -negamax(stats, -beta, -alpha, depth - 1, true); - } - } - - stats_move_unmake(stats, &copy); - searched++; - - if (score > alpha) { - if (!move_capture(move)) { - stats->history[piece_index(move_piece(move))] - [move_target(move)] += depth; - } - - alpha = score; - flag = flagExact; - bestMove = move; - stats_pv_store(stats, move); - - if (score >= beta) { - ttable_write(stats, bestMove, beta, depth, flagBeta); - - if (!move_capture(move)) { - stats->killer[1][stats->ply] = stats->killer[0][stats->ply]; - stats->killer[0][stats->ply] = move; - } - - return beta; - } - } - } - - if (legal_moves == 0) { - if (isCheck) - return -MATE_VALUE + stats->ply; - else - return 0; - } - - ttable_write(stats, bestMove, alpha, depth, flag); - return alpha; -} - -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_piece_promote(move))); -} - -TTable *ttable = NULL; -void search_position(Board *board, int depth) { - assert(board); - - Stats stats = {.ttable = ttable, .board = board, 0}; - - int alpha = -INFINITY, beta = INFINITY; - for (int crnt = 1; crnt <= depth;) { - stats.follow_pv = 1; - - int score = negamax(&stats, alpha, beta, crnt, true); - if ((score <= alpha) || (score >= beta)) { - alpha = -INFINITY; - beta = INFINITY; - continue; - } - alpha = score - WINDOW; - beta = score + WINDOW; - - if (stats.pv_length[0]) { - if (score > -MATE_VALUE && score < -MATE_SCORE) { - printf("info score mate %d depth %d nodes %ld pv ", - -(score + MATE_VALUE) / 2 - 1, crnt, stats.nodes); - } else if (score > MATE_SCORE && score < MATE_VALUE) { - printf("info score mate %d depth %d nodes %ld pv ", - (MATE_VALUE - score) / 2 + 1, crnt, stats.nodes); - } else { - printf("info score cp %d depth %d nodes %ld pv ", score, crnt, - stats.nodes); - } - - for (int i = 0; i < stats.pv_length[0]; i++) { - move_print_UCI(stats.pv_table[0][i]); - printf(" "); - } - printf("\n"); - } - crnt++; - } - - printf("bestmove "); - move_print_UCI(stats.pv_table[0][0]); - printf("\n"); -} - -void print_info(void) { - printf("id name Stellar\n"); - printf("id author Dimitrije Dobrota\n"); - printf("uciok\n"); -} - -typedef struct Instruction Instruction; -struct Instruction { - char *command; - char *token; - char *crnt; -}; - -char *Instruction_token_next(Instruction *self); - -Instruction *Instruction_new(char *command) { - Instruction *p; - NEW0(p); - p->command = ALLOC(strlen(command) + 1); - p->token = ALLOC(strlen(command) + 1); - strcpy(p->command, command); - p->crnt = command; - Instruction_token_next(p); - return p; -} - -void Instruction_free(Instruction **p) { - FREE((*p)->command); - FREE((*p)->token); - FREE(*p); -} - -char *Instruction_token(Instruction *self) { return self->token; } -char *Instruction_token_n(Instruction *self, int n) { - while (isspace(*self->crnt) && *self->crnt != '\0') - self->crnt++; - - if (*self->crnt == '\0') { - *self->token = '\0'; - return NULL; - } - - char *p = self->token; - while (n--) { - while (!isspace(*self->crnt) && *self->crnt != '\0' && - *self->crnt != ';') - *p++ = *self->crnt++; - if (*self->crnt == '\0') { - p++; - break; - } - self->crnt++; - *p++ = ' '; - } - *--p = '\0'; - - return self->token; -} - -char *Instruction_token_next(Instruction *self) { - return Instruction_token_n(self, 1); -} - -Move parse_move(Board *board, char *move_string) { - Move result = {0}; - MoveList *moves; - Square source, target; - - source = coordinates_to_square(move_string); - target = coordinates_to_square(move_string + 2); - - moves = move_list_generate(NULL, board); - for (int i = 0; i < moves->count; i++) { - Move move = move_list_index_move(moves, i); - if (move_source(move) == source && move_target(move) == target) { - if (move_string[4]) { - if (tolower(piece_code(move_piece_promote(move))) != - move_string[4]) - continue; - } - result = move; - break; - } - } - - move_list_free(&moves); - return result; -} - -Board *Instruction_parse(Instruction *self, Board *board) { - char *token = Instruction_token(self); - - if (!board) board = board_new(); - - do { - if (strcmp(token, "ucinewgame") == 0) { - board = board_from_FEN(board, start_position); - continue; - } - - if (strcmp(token, "quit") == 0) return NULL; - - if (strcmp(token, "position") == 0) { - token = Instruction_token_next(self); - if (token && strcmp(token, "startpos") == 0) { - board = board_from_FEN(board, start_position); - } else if (token && strcmp(token, "fen") == 0) { - token = Instruction_token_n(self, 6); - board = board_from_FEN(board, token); - } else { - printf("Unknown argument after position\n"); - } - // board_print(board); - continue; - } - - if (strcmp(token, "moves") == 0) { - while ((token = Instruction_token_next(self))) { - Move move = parse_move(board, token); - if (!move_cmp(move, (Move){0})) { - move_make(move, board, 0); - } else { - printf("Invalid move %s!\n", token); - } - } - // board_print(board); - return board; - } - - if (strcmp(token, "go") == 0) { - int depth = 6; - for (token = Instruction_token_next(self); token; - token = Instruction_token_next(self)) { - - if (token && strcmp(token, "depth") == 0) { - token = Instruction_token_next(self); - depth = atoi(token); - } else { - // printf("Unknown argument %s after go\n", token); - } - } - search_position(board, depth); - continue; - } - - if (strcmp(token, "isready") == 0) { - printf("readyok\n"); - continue; - } - - if (strcmp(token, "uci") == 0) { - print_info(); - continue; - } - } while ((token = Instruction_token_next(self))); - - return board; -} - -void uci_loop(void) { - Board board; - Instruction *instruction; - char input[200000]; - - setbuf(stdin, NULL); - setbuf(stdout, NULL); - - print_info(); - while (1) { - memset(input, 0, sizeof(input)); - fflush(stdout); - if (!fgets(input, sizeof(input), stdin)) continue; - - instruction = Instruction_new(input); - if (!Instruction_parse(instruction, &board)) break; - Instruction_free(&instruction); - } - - Instruction_free(&instruction); -} - -/* MAIN */ - -void init(void) { - attacks_init(); - zobrist_init(); - ttable = ttable_new(C64(0x400000)); -} - -int main(void) { - init(); - uci_loop(); - ttable_free(&ttable); - return 0; -} diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp @@ -0,0 +1,520 @@ +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "attacks.hpp" +#include "board.hpp" +#include "moves.hpp" +#include "score.hpp" +#include "stats.hpp" +#include "transposition.hpp" +#include "utils_cpp.hpp" +#include "zobrist.hpp" + +#define FULL_DEPTH 4 +#define REDUCTION_LIMIT 3 +#define REDUCTION_MOVE 2 + +#define WINDOW 50 + +void move_list_sort_pv(std::vector<MoveE> &list, Stats &stats, Move best) { + for (MoveE &move : list) { + if (move_cmp(best, move.move)) { + move.score = 30000; + return; + } + } + + if (stats.ply && stats.follow_pv) { + stats.follow_pv = 0; + for (MoveE &move : list) { + if (move_cmp(stats.pv_table[0][stats.ply], move.move)) { + move.score = 20000; + stats.follow_pv = 1; + break; + } + } + } +} + +/* SEARCHING */ + +int evaluate(const Board &board) { + Color side = board.get_side(); + Color sideOther = (side == Color::BLACK) ? Color::WHITE : Color::BLACK; + + U64 occupancy = board.get_bitboard_color(side); + uint8_t square_i; + + int score = 0; + for (piece::Type type : piece::TypeIter()) { + U64 bitboard = board.get_bitboard_piece(type); + bitboard_for_each_bit(square_i, bitboard) { + Square square = static_cast<Square>(square_i); + if (bit_get(occupancy, square_i)) { + score += Score_value(type); + score += Score_position(type, side, square); + } else { + score -= Score_value(type); + score -= Score_position(type, sideOther, square); + } + } + } + + return score; +} + +int is_repetition() { return 0; } + +int stats_move_make(Stats &stats, Board &copy, Move move, int flag) { + copy = stats.board; + if (!move_make(move, stats.board, flag)) { + stats.board = copy; + return 0; + } + stats.ply++; + return 1; +} + +void stats_move_make_pruning(Stats &stats, Board &copy) { + copy = stats.board; + stats.board.switch_side(); + stats.board.set_enpassant(Square::no_sq); + stats.ply++; +} + +void stats_move_unmake(Stats &stats, Board &copy) { + stats.board = copy; + stats.ply--; +} + +void stats_pv_store(Stats &stats, Move move) { + const int ply = stats.ply; + stats.pv_table[ply][ply] = move; + for (int i = ply + 1; i < stats.pv_length[ply + 1]; i++) { + stats.pv_table[ply][i] = stats.pv_table[ply + 1][i]; + } + stats.pv_length[ply] = stats.pv_length[ply + 1]; +} + +int quiescence(Stats &stats, int alpha, int beta) { + stats.pv_length[stats.ply] = stats.ply; + stats.nodes++; + + int score = evaluate(stats.board); + if (stats.ply > MAX_PLY - 1) return score; + if (score >= beta) return beta; + if (score > alpha) alpha = score; + + Board copy; + std::vector<MoveE> list = move_list_generate(stats.board); + Score_move_list(stats, list); + move_list_sort_pv(list, stats, (Move){0}); + move_list_sort(list); + + for (const auto [move, _] : list) { + if (!stats_move_make(stats, copy, move, 1)) continue; + score = -quiescence(stats, -beta, -alpha); + stats_move_unmake(stats, copy); + + if (score > alpha) { + alpha = score; + stats_pv_store(stats, move); + if (score >= beta) return beta; + } + } + + return alpha; +} + +int negamax(Stats &stats, int alpha, int beta, int depth, bool null) { + int pv_node = (beta - alpha) > 1; + HasheFlag flag = HasheFlag::Alpha; + Move bestMove = {0}; + int futility = 0; + Board copy; + + stats.pv_length[stats.ply] = stats.ply; + + int score = stats.ttable.read(stats, &bestMove, alpha, beta, depth); + if (stats.ply && score != TTABLE_UNKNOWN && !pv_node) return score; + + // && fifty >= 100 + if (stats.ply && is_repetition()) return 0; + if (depth == 0) { + stats.nodes++; + int score = quiescence(stats, alpha, beta); + // ttable_write(stats, bestMove, score, depth, HasheFlag::Exact); + return score; + } + + if (alpha < -MATE_VALUE) alpha = -MATE_VALUE; + if (beta > MATE_VALUE - 1) beta = MATE_VALUE - 1; + if (alpha >= beta) return alpha; + // if (stats.ply > MAX_PLY - 1) return evaluate(stats.board); + + int isCheck = stats.board.is_check(); + if (isCheck) depth++; + + if (!pv_node && !isCheck) { + int staticEval = evaluate(stats.board); + + // evaluation pruning + if (depth < 3 && abs(beta - 1) > -MATE_VALUE + 100) { + int marginEval = Score_value(piece::Type::PAWN) * depth; + if (staticEval - marginEval >= beta) return staticEval - marginEval; + } + + if (null) { + // null move pruning + if (stats.ply && depth > 2 && staticEval >= beta) { + stats_move_make_pruning(stats, copy); + score = -negamax(stats, -beta, -beta + 1, + depth - 1 - REDUCTION_MOVE, false); + stats_move_unmake(stats, copy); + if (score >= beta) return beta; + } + + // razoring + score = staticEval + Score_value(piece::Type::PAWN); + int scoreNew = quiescence(stats, alpha, beta); + + if (score < beta && depth == 1) { + return (scoreNew > score) ? scoreNew : score; + } + + score += Score_value(piece::Type::PAWN); + if (score < beta && depth < 4) { + if (scoreNew < beta) + return (scoreNew > score) ? scoreNew : score; + } + } + + // futility pruning condition + static const int margin[] = {0, 100, 300, 500}; + if (depth < 4 && abs(alpha) < MATE_SCORE && + staticEval + margin[depth] <= alpha) + futility = 1; + } + + std::vector<MoveE> list = move_list_generate(stats.board); + Score_move_list(stats, list); + move_list_sort_pv(list, stats, bestMove); + move_list_sort(list); + + int legal_moves = 0; + int searched = 0; + for (const auto [move, _] : list) { + if (!stats_move_make(stats, copy, move, 0)) continue; + legal_moves++; + + // futility pruning + if (futility && searched && !move_capture(move) && + !move_promote(move) && !stats.board.is_check()) { + stats_move_unmake(stats, copy); + continue; + } + + if (!searched) { + score = -negamax(stats, -beta, -alpha, depth - 1, true); + } else { + // Late Move Reduction + if (!pv_node && searched >= FULL_DEPTH && + depth >= REDUCTION_LIMIT && !isCheck && !move_capture(move) && + !move_promote(move) && + (move_source(move) != move_source(stats.killer[0][stats.ply]) || + move_target(move) != + move_target(stats.killer[0][stats.ply])) && + (move_source(move) != move_source(stats.killer[1][stats.ply]) || + move_target(move) != + move_target(stats.killer[1][stats.ply]))) { + score = -negamax(stats, -alpha - 1, -alpha, depth - 2, true); + } else + score = alpha + 1; + + // found better move + // Principal Variation Search + if (score > alpha) { + score = -negamax(stats, -alpha - 1, -alpha, depth - 1, true); + + // if fail research + if ((score > alpha) && (score < beta)) + score = -negamax(stats, -beta, -alpha, depth - 1, true); + } + } + + stats_move_unmake(stats, copy); + searched++; + + if (score > alpha) { + if (!move_capture(move)) { + stats.history[move_piece(move).index][move_target(move)] += + depth; + } + + alpha = score; + flag = HasheFlag::Exact; + bestMove = move; + stats_pv_store(stats, move); + + if (score >= beta) { + stats.ttable.write(stats, bestMove, beta, depth, + HasheFlag::Beta); + + if (!move_capture(move)) { + stats.killer[1][stats.ply] = stats.killer[0][stats.ply]; + stats.killer[0][stats.ply] = move; + } + + return beta; + } + } + } + + if (legal_moves == 0) { + if (isCheck) + return -MATE_VALUE + stats.ply; + else + return 0; + } + + stats.ttable.write(stats, bestMove, alpha, depth, flag); + return alpha; +} + +void move_print_UCI(Move move) { + printf("%s%s", + square_to_coordinates(static_cast<Square>(move_source(move))), + square_to_coordinates(static_cast<Square>(move_target(move)))); + if (move_promote(move)) printf("%c", (move_piece_promote(move).code)); +} + +TTable ttable(C64(0x400000)); +void search_position(Board &board, int depth) { + Stats stats = {ttable, board, 0}; + + int alpha = -INFINITY, beta = INFINITY; + for (int crnt = 1; crnt <= depth;) { + stats.follow_pv = 1; + + int score = negamax(stats, alpha, beta, crnt, true); + if ((score <= alpha) || (score >= beta)) { + alpha = -INFINITY; + beta = INFINITY; + continue; + } + alpha = score - WINDOW; + beta = score + WINDOW; + + if (stats.pv_length[0]) { + if (score > -MATE_VALUE && score < -MATE_SCORE) { + printf("info score mate %d depth %d nodes %ld pv ", + -(score + MATE_VALUE) / 2 - 1, crnt, stats.nodes); + } else if (score > MATE_SCORE && score < MATE_VALUE) { + printf("info score mate %d depth %d nodes %ld pv ", + (MATE_VALUE - score) / 2 + 1, crnt, stats.nodes); + } else { + printf("info score cp %d depth %d nodes %ld pv ", score, crnt, + stats.nodes); + } + + for (int i = 0; i < stats.pv_length[0]; i++) { + move_print_UCI(stats.pv_table[0][i]); + printf(" "); + } + printf("\n"); + } + crnt++; + } + + printf("bestmove "); + move_print_UCI(stats.pv_table[0][0]); + printf("\n"); +} + +void print_info(void) { + printf("id name Stellar\n"); + printf("id author Dimitrije Dobrota\n"); + printf("uciok\n"); +} + +typedef struct Instruction Instruction; +struct Instruction { + char *command; + char *token; + char *crnt; +}; + +char *Instruction_token_next(Instruction *self); + +Instruction *Instruction_new(char *command) { + Instruction *p = new Instruction(); + p->command = new char(strlen(command) + 1); + p->token = new char(strlen(command) + 1); + strcpy(p->command, command); + p->crnt = command; + Instruction_token_next(p); + return p; +} + +void Instruction_free(Instruction **p) { + delete ((*p)->command); + delete ((*p)->token); + delete (*p); +} + +char *Instruction_token(Instruction *self) { return self->token; } +char *Instruction_token_n(Instruction *self, int n) { + while (isspace(*self->crnt) && *self->crnt != '\0') + self->crnt++; + + if (*self->crnt == '\0') { + *self->token = '\0'; + return NULL; + } + + char *p = self->token; + while (n--) { + while (!isspace(*self->crnt) && *self->crnt != '\0' && + *self->crnt != ';') + *p++ = *self->crnt++; + if (*self->crnt == '\0') { + p++; + break; + } + self->crnt++; + *p++ = ' '; + } + *--p = '\0'; + + return self->token; +} + +char *Instruction_token_next(Instruction *self) { + return Instruction_token_n(self, 1); +} + +Move parse_move(Board &board, char *move_string) { + Move result = {0}; + + uint8_t source = to_underlying(square_from_coordinates(move_string)); + uint8_t target = to_underlying(square_from_coordinates(move_string + 2)); + + std::vector<MoveE> list = move_list_generate(board); + for (const auto [move, _] : list) { + if (move_source(move) == source && move_target(move) == target) { + if (move_string[4]) { + if (tolower(move_piece_promote(move).code) != move_string[4]) + continue; + } + result = move; + break; + } + } + + return result; +} + +Board *Instruction_parse(Instruction *self, Board &board) { + char *token = Instruction_token(self); + + do { + if (strcmp(token, "ucinewgame") == 0) { + board = Board(start_position); + continue; + } + + if (strcmp(token, "quit") == 0) return nullptr; + + if (strcmp(token, "position") == 0) { + token = Instruction_token_next(self); + if (token && strcmp(token, "startpos") == 0) { + board = Board(start_position); + } else if (token && strcmp(token, "fen") == 0) { + token = Instruction_token_n(self, 6); + board = Board(token); + } else { + printf("Unknown argument after position\n"); + } + // board_print(board); + continue; + } + + if (strcmp(token, "moves") == 0) { + while ((token = Instruction_token_next(self))) { + Move move = parse_move(board, token); + if (!move_cmp(move, (Move){0})) { + move_make(move, board, 0); + } else { + printf("Invalid move %s!\n", token); + } + } + // board_print(board); + return &board; + } + + if (strcmp(token, "go") == 0) { + int depth = 6; + for (token = Instruction_token_next(self); token; + token = Instruction_token_next(self)) { + + if (token && strcmp(token, "depth") == 0) { + token = Instruction_token_next(self); + depth = atoi(token); + } else { + // printf("Unknown argument %s after go\n", token); + } + } + search_position(board, depth); + continue; + } + + if (strcmp(token, "isready") == 0) { + printf("readyok\n"); + continue; + } + + if (strcmp(token, "uci") == 0) { + print_info(); + continue; + } + } while ((token = Instruction_token_next(self))); + + return &board; +} + +void uci_loop(void) { + Board board; + Instruction *instruction; + char input[200000]; + + setbuf(stdin, NULL); + setbuf(stdout, NULL); + + print_info(); + while (1) { + memset(input, 0, sizeof(input)); + fflush(stdout); + if (!fgets(input, sizeof(input), stdin)) continue; + + instruction = Instruction_new(input); + if (!Instruction_parse(instruction, board)) break; + Instruction_free(&instruction); + } + + Instruction_free(&instruction); +} + +/* MAIN */ + +void init(void) { + attacks_init(); + zobrist_init(); +} + +int main(void) { + init(); + uci_loop(); + return 0; +} diff --git a/src/engine/score.c b/src/engine/score.c @@ -1,141 +0,0 @@ -#include "score.h" -#include "moves.h" -#include "stats.h" - -// clang-format off -struct Score_T { - int value; - int position[64]; - int capture[6]; -}; - -static const 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] + 10000; -} - -int Score_move(const Stats *stats, Move move) { - if (move_capture(move)) { - return Score_capture(piece_piece(move_piece(move)), - piece_piece(move_piece_capture(move))); - } - - if (move_cmp(stats->killer[0][stats->ply], move)) - return 9000; - else if (move_cmp(stats->killer[1][stats->ply], move)) - return 8000; - - return stats->history[piece_index(move_piece(move))][move_target(move)]; -} - -void Score_move_list(const Stats *stats, MoveList *list) { - for (int i = 0; i < move_list_size(list); i++) { - list->moves[i].score = Score_move(stats, move_list_index_move(list, i)); - } -} diff --git a/src/engine/score.cpp b/src/engine/score.cpp @@ -0,0 +1,130 @@ +#include "score.hpp" +#include "moves.hpp" +#include "stats.hpp" +#include "utils_cpp.hpp" + +// clang-format off +struct Score_T { + int value; + int position[64]; + int capture[6]; +}; + +static const struct Score_T Scores[] = { +{ +.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 = { 105, 205, 305, 405, 505, 605} }, +{ +.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 = { 104, 204, 304, 404, 504, 604} }, +{ +.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 = { 103, 203, 303, 403, 503, 603} }, +{ +.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 = { 102, 202, 302, 402, 502, 602} }, +{ +.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 = { 101, 201, 301, 401, 501, 601} }, +{ +.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 = { 100, 200, 300, 400, 500, 600} }, +}; + +const Square mirror_score[128] = +{ + Square::a8, Square::b8, Square::c8, Square::d8, Square::e8, Square::f8, Square::g8, Square::h8, + Square::a7, Square::b7, Square::c7, Square::d7, Square::e7, Square::f7, Square::g7, Square::h7, + Square::a6, Square::b6, Square::c6, Square::d6, Square::e6, Square::f6, Square::g6, Square::h6, + Square::a5, Square::b5, Square::c5, Square::d5, Square::e5, Square::f5, Square::g5, Square::h5, + Square::a4, Square::b4, Square::c4, Square::d4, Square::e4, Square::f4, Square::g4, Square::h4, + Square::a3, Square::b3, Square::c3, Square::d3, Square::e3, Square::f3, Square::g3, Square::h3, + Square::a2, Square::b2, Square::c2, Square::d2, Square::e2, Square::f2, Square::g2, Square::h2, + Square::a1, Square::b1, Square::c1, Square::d1, Square::e1, Square::f1, Square::g1, Square::h1, Square::no_sq, +}; +// clang-format on + +int Score_value(piece::Type piece) { + return Scores[to_underlying(piece)].value; +} + +int Score_position(piece::Type piece, Color color, Square square) { + if (color == Color::BLACK) square = mirror_score[to_underlying(square)]; + return Scores[to_underlying(piece)].position[to_underlying(square)]; +} + +int Score_move(const Stats &stats, Move move) { + const int piece = to_underlying(move_piece(move).type); + const int capture = to_underlying(move_piece_capture(move).type); + + if (move_capture(move)) { + return Scores[piece].capture[capture] + 10000; + } + + if (move_cmp(stats.killer[0][stats.ply], move)) + return 9000; + else if (move_cmp(stats.killer[1][stats.ply], move)) + return 8000; + + return stats.history[piece][move_target(move)]; +} + +void Score_move_list(const Stats &stats, std::vector<MoveE> &list) { + for (MoveE &move : list) { + move.score = Score_move(stats, move.move); + } +} diff --git a/src/engine/score.h b/src/engine/score.h @@ -1,13 +0,0 @@ -#ifndef STELLAR_SCORE_H -#define STELLAR_SCORE_H - -#include "board.h" -#include "moves.h" -#include "stats.h" - -int Score_position(ePiece piece, eColor color, Square square); -void Score_move_list(const Stats *stats, MoveList *list); -int Score_capture(ePiece src, ePiece tgt); -int Score_value(ePiece piece); - -#endif diff --git a/src/engine/score.hpp b/src/engine/score.hpp @@ -0,0 +1,17 @@ +#ifndef STELLAR_SCORE_H +#define STELLAR_SCORE_H + +#include "board.hpp" +#include "moves.hpp" +#include "stats.hpp" + +#define INFINITY 50000 +#define MATE_VALUE 49000 +#define MATE_SCORE 48000 + +void Score_move_list(const Stats &stats, std::vector<MoveE> &list); +int Score_position(piece::Type piece, Color color, Square square); +int Score_capture(piece::Type src, piece::Type tgt); +int Score_value(piece::Type piece); + +#endif diff --git a/src/engine/stats.h b/src/engine/stats.h @@ -1,22 +0,0 @@ -#ifndef STELLAR_STATS_H -#define STELLAR_STATS_H - -#include "moves.h" -#include "utils.h" - -#define MAX_PLY 64 - -typedef struct Stats Stats; -struct Stats { - Move pv_table[MAX_PLY][MAX_PLY]; - Move killer[2][MAX_PLY]; - U32 history[16][64]; - int pv_length[MAX_PLY]; - struct TTable *ttable; - Board *board; - int follow_pv; - long nodes; - int ply; -}; - -#endif diff --git a/src/engine/stats.hpp b/src/engine/stats.hpp @@ -0,0 +1,22 @@ +#ifndef STELLAR_STATS_H +#define STELLAR_STATS_H + +#include "moves.hpp" +#include "utils_cpp.hpp" + +#define MAX_PLY 64 + +typedef struct Stats Stats; +struct Stats { + struct TTable &ttable; + Board &board; + Move pv_table[MAX_PLY][MAX_PLY]; + Move killer[2][MAX_PLY]; + U32 history[16][64]; + int pv_length[MAX_PLY]; + int follow_pv; + long nodes; + int ply; +}; + +#endif diff --git a/src/engine/transposition.c b/src/engine/transposition.c @@ -1,88 +0,0 @@ -#include <cul/assert.h> -#include <cul/mem.h> -#include <string.h> - -#include "board.h" -#include "moves.h" -#include "transposition.h" - -#define TTABLE_SIZE 0x400000 - -#define T TTable - -typedef struct Hashe Hashe; -struct Hashe { - U64 key; - Move best; - int depth; - int score; - HasheFlag flag; -}; - -struct T { - U64 size; - Hashe table[]; -}; - -T *ttable_new(U64 size) { - T *self = CALLOC(1, sizeof(T) + size * sizeof(Hashe)); - self->size = size; - return self; -} - -void ttable_free(T **self) { - assert(self && *self); - FREE(*self); -} - -void ttable_clear(T *self) { - assert(self); - memset(self->table, 0x0, sizeof(T) + self->size * sizeof(Hashe)); -} - -int ttable_read(const Stats *stats, Move *best, int alpha, int beta, - int depth) { - assert(stats); - assert(stats->ttable); - - T *self = stats->ttable; - U64 hash = board_hash(stats->board); - - Hashe *phashe = &self->table[hash % self->size]; - if (phashe->key == hash) { - if (phashe->depth >= depth) { - int score = phashe->score; - - if (score < -MATE_SCORE) score += stats->ply; - if (score > MATE_SCORE) score -= stats->ply; - - if (phashe->flag == flagExact) return score; - if ((phashe->flag == flagAlpha) && (score <= alpha)) return alpha; - if ((phashe->flag == flagBeta) && (score >= beta)) return beta; - } - *best = phashe->best; - } - return TTABLE_UNKNOWN; -} - -void ttable_write(const Stats *stats, Move best, int score, int depth, - HasheFlag flag) { - assert(stats); - assert(stats->ttable); - - T *self = stats->ttable; - U64 hash = board_hash(stats->board); - - Hashe *phashe = &self->table[hash % self->size]; - - if (score < -MATE_SCORE) score += stats->ply; - if (score > MATE_SCORE) score -= stats->ply; - - *phashe = (Hashe){ - .key = hash, - .best = best, - .depth = depth, - .score = score, - .flag = flag, - }; -} diff --git a/src/engine/transposition.cpp b/src/engine/transposition.cpp @@ -0,0 +1,38 @@ +#include "transposition.hpp" +#include "board.hpp" +#include "score.hpp" + +int TTable::read(const Stats &stats, Move *best, int alpha, int beta, + int depth) const { + + U64 hash = stats.board.get_hash(); + const Hashe &phashe = table[hash % table.size()]; + if (phashe.key == hash) { + if (phashe.depth >= depth) { + int score = phashe.score; + + if (score < -MATE_SCORE) score += stats.ply; + if (score > MATE_SCORE) score -= stats.ply; + + if (phashe.flag == HasheFlag::Exact) return score; + if ((phashe.flag == HasheFlag::Alpha) && (score <= alpha)) + return alpha; + if ((phashe.flag == HasheFlag::Beta) && (score >= beta)) + return beta; + } + *best = phashe.best; + } + return TTABLE_UNKNOWN; +} + +void TTable::write(const Stats &stats, Move best, int score, int depth, + HasheFlag flag) { + + U64 hash = stats.board.get_hash(); + Hashe &phashe = table[hash % table.size()]; + + if (score < -MATE_SCORE) score += stats.ply; + if (score > MATE_SCORE) score -= stats.ply; + + phashe = {hash, best, depth, score, flag}; +} diff --git a/src/engine/transposition.h b/src/engine/transposition.h @@ -1,30 +0,0 @@ -#ifndef STELLAR_TRANSPOSITION_H -#define STELLAR_TRANSPOSITION_H - -#include "moves.h" -#include "stats.h" -#include "utils.h" - -#define TTABLE_UNKNOWN 100000 - -#define T TTable - -typedef enum HasheFlag HasheFlag; -enum HasheFlag { - flagExact, - flagAlpha, - flagBeta -}; - -typedef struct T T; - -T *ttable_new(U64 size); -void ttable_free(T **self); -void ttable_clear(T *self); - -int ttable_read(const Stats *stats, Move *best, int alpha, int beta, int depth); -void ttable_write(const Stats *stats, Move best, int score, int depth, - HasheFlag flag); - -#undef T -#endif diff --git a/src/engine/transposition.hpp b/src/engine/transposition.hpp @@ -0,0 +1,44 @@ +#ifndef STELLAR_TRANSPOSITION_H +#define STELLAR_TRANSPOSITION_H + +#include "moves.hpp" +#include "stats.hpp" +#include "utils_cpp.hpp" + +#include <vector> + +#define TTABLE_UNKNOWN 100000 + +#define T TTable + +enum class HasheFlag : uint8_t { + Exact, + Alpha, + Beta +}; + +typedef struct Hashe Hashe; +struct Hashe { + U64 key; + Move best; + int depth; + int score; + HasheFlag flag; +}; + +class TTable { + public: + TTable(U64 size) : table(size, {0}) {} + + void clear() { table.clear(); }; + void write(const Stats &stats, Move best, int score, int depth, + HasheFlag flag); + int read(const Stats &stats, Move *best, int alpha, int beta, + int depth) const; + + private: + std::vector<Hashe> table; +}; + +#undef T +#endif diff --git a/src/include/board.hpp b/src/include/board.hpp @@ -44,12 +44,13 @@ class Board { // exception if not found Color get_square_piece_color(Square square) const; piece::Type get_square_piece_type(Square square) const; - const piece::Piece &get_square_piece(Square square) const; + const piece::Piece *get_square_piece(Square square) const; /* Setters */ + void xor_hash(U64 op); void switch_side(void); - void and_castle(Castle right); + void and_castle(uint8_t right); void set_enpassant(Square target); void pop_bitboard_color(Color color, Square square); @@ -79,6 +80,4 @@ class Board { const piece::Piece &board_square_piece(const Board *self, Square square, Color side); -void board_print(const Board *self); - #endif diff --git a/src/include/moves.h b/src/include/moves.h @@ -1,67 +0,0 @@ -#ifndef STELLAR_MOVES_H -#define STELLAR_MOVES_H - -#include <stdbool.h> -#include <stdint.h> - -#include "board.h" - -typedef struct Move Move; -struct Move { - unsigned source : 6; - unsigned target : 6; - unsigned piece : 5; - unsigned piece_capture : 5; - unsigned piece_promote : 5; - bool dbl : 1; - bool enpassant : 1; - bool castle : 1; - bool capture : 1; - bool promote : 1; -}; - -typedef struct MoveList MoveList; -typedef struct MoveE MoveE; - -struct MoveList { - int count; - struct MoveE { - Move move; - int score; - } moves[256]; -}; - -int move_cmp(Move a, Move b); -Move move_encode(Square src, Square tgt, Piece piece, Piece capture, - Piece promote, int dbl, int enpassant, int castle); -void move_print(Move move); -MoveList *move_list_new(void); - -void move_list_free(MoveList **p); -Move move_list_index_move(const MoveList *self, int index); -int move_list_index_score(const MoveList *self, int index); -void move_list_index_score_set(MoveList *self, int index, int score); - -int move_make(Move move, Board *board, int flag); - -int move_list_size(const MoveList *self); -void move_list_reset(MoveList *self); -void move_list_add(MoveList *self, Move move); -void move_list_print(const MoveList *self); -void move_list_sort(MoveList *list); - -MoveList *move_list_generate(MoveList *moves, const Board *board); - -#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_from_index(move.piece)) -#define move_piece_capture(move) (piece_from_index(move.piece_capture)) -#define move_piece_promote(move) (piece_from_index(move.piece_promote)) - -#endif diff --git a/src/include/moves.hpp b/src/include/moves.hpp @@ -0,0 +1,48 @@ +#ifndef STELLAR_MOVES_H +#define STELLAR_MOVES_H + +#include "board.hpp" +#include "piece.hpp" + +#include <vector> + +struct Move { + unsigned source : 6; + unsigned target : 6; + unsigned piece : 5; + unsigned piece_capture : 5; + unsigned piece_promote : 5; + bool dbl : 1; + bool enpassant : 1; + bool castle : 1; + bool capture : 1; + bool promote : 1; +}; + +struct MoveE { + Move move; + int score; +}; + +Move move_encode(uint8_t src, uint8_t tgt, const piece::Piece *piece, + const piece::Piece *capture, const piece::Piece *promote, + bool dbl, bool enpassant, bool castle); +std::vector<MoveE> move_list_generate(const Board &board); +int move_make(Move move, Board &board, int flag); +void move_list_sort(std::vector<MoveE> &list); +int move_cmp(Move a, Move b); +void move_print(Move move); + +#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::get_from_index(move.piece)) +#define move_piece_capture(move) (piece::get_from_index(move.piece_capture)) +#define move_piece_promote(move) (piece::get_from_index(move.piece_promote)) + +#endif diff --git a/src/include/perft.h b/src/include/perft.h @@ -1,24 +0,0 @@ -#ifndef STELLAR_PERFT_H -#define STELLAR_PERFT_H - -#include "board.h" - -typedef unsigned long long ull; -typedef struct PerftResult PerftResult; -struct PerftResult { - ull node; -#ifdef USE_FULL_COUNT - ull capture; - ull enpassant; - ull castle; - ull promote; - ull check; - // ull checkDiscovered; - // ull checkDouble; - // ull checkmate; -#endif -}; - -PerftResult perft_test(const char *fen, int depth, int thread_num); - -#endif diff --git a/src/include/perft.hpp b/src/include/perft.hpp @@ -0,0 +1,24 @@ +#ifndef STELLAR_PERFT_H +#define STELLAR_PERFT_H + +#include "board.hpp" + +typedef unsigned long long ull; +typedef struct PerftResult PerftResult; +struct PerftResult { + ull node; +#ifdef USE_FULL_COUNT + ull capture; + ull enpassant; + ull castle; + ull promote; + ull check; + // ull checkDiscovered; + // ull checkDouble; + // ull checkmate; +#endif +}; + +PerftResult perft_test(const char *fen, int depth, int thread_num); + +#endif diff --git a/src/include/piece.hpp b/src/include/piece.hpp @@ -106,7 +106,9 @@ constexpr const Piece &get_from_code(char code) { throw std::exception(); } -// constexpr const Piece &get_from_index(uint8_t index); +constexpr const Piece &get_from_index(uint8_t index) { + return table[index / 6][index % 6]; +} } // namespace piece diff --git a/src/include/random.h b/src/include/random.h @@ -1,10 +0,0 @@ -#ifndef STELLAR_RANDOM_H -#define STELLAR_RANDOM_H - -#include "utils.hpp" - -void random_state_reset(); -U32 random_get_U32(); -U64 random_get_U64(); - -#endif diff --git a/src/include/utils.h b/src/include/utils.h @@ -1,79 +0,0 @@ -#ifndef STELLAR_UTILS_H -#define STELLAR_UTILS_H - -#include <inttypes.h> - -#define INFINITY 50000 -#define MATE_VALUE 49000 -#define MATE_SCORE 48000 - -// useful macros -#define MAX(a, b) ((a > b) ? a : b) -#define MIN(a, b) ((a < b) ? a : b) - -// define number types -typedef unsigned long long U64; // define bitboard data type -#define C64(constantU64) constantU64##ULL // define shorthand for constants - -typedef unsigned int U32; -#define C32(constantU32) constantU32##U - -// useful bit patterns -extern const U64 universe; -extern const U64 notAFile; -extern const U64 notHFile; - -// useful bit functions -#define bit_get(bitboard, square) (((bitboard) >> (square)) & C64(1)) -#define bit_set(bitboard, square) ((bitboard) |= C64(1) << (square)) -#define bit_pop(bitboard, square) ((bitboard) &= ~(C64(1) << (square))) - -uint8_t bit_count(U64 bitboard); -uint8_t bit_lsb_index(U64 bitboard); -#define bit_lsb_pop(bitboard) ((bitboard) &= (bitboard) & ((bitboard)-1)) - -#define bitboard_for_each_bit(var, bb) \ - for (var = bit_lsb_index(bb); bb; bit_lsb_pop(bb), var = bit_lsb_index(bb)) - -// squares -// clang-format off -enum enumSquare { - a1, b1, c1, d1, e1, f1, g1, h1, - a2, b2, c2, d2, e2, f2, g2, h2, - a3, b3, c3, d3, e3, f3, g3, h3, - a4, b4, c4, d4, e4, f4, g4, h4, - a5, b5, c5, d5, e5, f5, g5, h5, - a6, b6, c6, d6, e6, f6, g6, h6, - a7, b7, c7, d7, e7, f7, g7, h7, - a8, b8, c8, d8, e8, f8, g8, h8, no_sq -}; -// clang-format on -typedef enum enumSquare eSquare; - -extern const char *square_to_coordinates[]; -eSquare coordinates_to_square(const char *cord); - -// board moving -typedef U64 (*direction_f)(U64); -U64 soutOne(U64 b); -U64 nortOne(U64 b); -U64 eastOne(U64 b); -U64 westOne(U64 b); -U64 soEaOne(U64 b); -U64 soWeOne(U64 b); -U64 noEaOne(U64 b); -U64 noWeOne(U64 b); - -// board rotation -U64 rotateLeft(U64 x, int s); -U64 rotateRight(U64 x, int s); - -int get_time_ms(void); - -typedef U64 (*attack_f)(eSquare square, U64 occupancy); - -#define empty_board "8/8/8/8/8/8/8/8 w - - " -#define start_position \ - "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 " - -#endif diff --git a/src/include/utils_cpp.hpp b/src/include/utils_cpp.hpp @@ -32,6 +32,7 @@ template <typename C, C beginVal, C endVal> class Iterator { }; #define C64(constantU64) constantU64##ULL +#define C32(constantU64) constantU64##UL typedef uint64_t U64; typedef uint32_t U32; @@ -120,4 +121,7 @@ inline U64 soWeOne(U64 b) { return (b & notAFile) >> 9; } inline U64 noEaOne(U64 b) { return (b & notHFile) << 9; } inline U64 noWeOne(U64 b) { return (b & notAFile) << 7; } +#define start_position \ + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 " + #endif diff --git a/src/moves/CMakeLists.txt b/src/moves/CMakeLists.txt @@ -1,7 +1,7 @@ add_library(moves OBJECT - moves.c - moves_make.c - moves_generate.c + moves.cpp + moves_make.cpp + moves_generate.cpp ) target_include_directories(moves diff --git a/src/moves/moves.c b/src/moves/moves.c @@ -1,81 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> - -#include <cul/assert.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 piece, Piece capture, - Piece 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 %2c %2c %2c %4d %4d %4d %4d %4d\n", - square_to_coordinates[move_source(move)], - square_to_coordinates[move_target(move)], - piece_asci(move_piece(move)), - move_capture(move) ? piece_asci(move_piece_capture(move)) : '.', - move_promote(move) ? piece_asci(move_piece_promote(move)) : '.', - 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 *move_list_new(void) { - MoveList *p; - NEW0(p); - return p; -} - -void move_list_free(MoveList **p) { FREE(*p); } - -Move move_list_index_move(const MoveList *self, int index) { - return self->moves[index].move; -} - -int move_list_index_score(const MoveList *self, int index) { - return self->moves[index].score; -} - -void move_list_index_score_set(MoveList *self, int index, int score) { - self->moves[index].score = score; -} - -int move_list_size(const MoveList *self) { return self->count; } -void move_list_reset(MoveList *self) { self->count = 0; } - -void move_list_add(MoveList *self, Move move) { - self->moves[self->count++].move = move; -} - -int move_list_cmp(const void *a, const void *b) { - return ((MoveE *)a)->score <= ((MoveE *)b)->score; -} - -void move_list_sort(MoveList *list) { - qsort(list->moves, list->count, sizeof(MoveE), move_list_cmp); -} - -void move_list_print(const MoveList *self) { - printf("Score From To Pi Cap Prmt Dbl Enp Cst C P\n"); - for (int i = 0; i < self->count; i++) { - printf("%5d: ", self->moves[i].score); - move_print(self->moves[i].move); - } - printf("Total: %d\n", self->count); -} diff --git a/src/moves/moves.cpp b/src/moves/moves.cpp @@ -0,0 +1,50 @@ +#include "moves.hpp" +#include "utils_cpp.hpp" + +#include <algorithm> +#include <cstdio> + +int move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; } + +Move move_encode(uint8_t src, uint8_t tgt, const piece::Piece *piece, + const piece::Piece *capture, const piece::Piece *promote, + bool dbl, bool enpassant, bool castle) { + return (Move){ + .source = src, + .target = tgt, + .piece = piece->index, + .piece_capture = capture ? capture->index : 0u, + .piece_promote = promote ? promote->index : 0u, + .dbl = dbl, + .enpassant = enpassant, + .castle = castle, + .capture = capture != NULL, + .promote = promote != NULL, + }; +} + +void move_print(Move move) { + printf("%5s %5s %2c %2c %2c %4d %4d %4d %4d %4d\n", + square_to_coordinates(static_cast<Square>(move_source(move))), + square_to_coordinates(static_cast<Square>(move_target(move))), + move_piece(move).code, + move_capture(move) ? move_piece_capture(move).code : '.', + move_promote(move) ? move_piece_promote(move).code : '.', + 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); +} + +void move_list_sort(std::vector<MoveE> &list) { + std::sort(list.begin(), list.end(), + [](const MoveE &a, const MoveE &b) { return a.score < b.score; }); +} + +void move_list_print(const std::vector<MoveE> &list) { + printf("Score From To Pi Cap Prmt Dbl Enp Cst C P\n"); + for (const MoveE &move : list) { + printf("%5d: ", move.score); + move_print(move.move); + } + printf("Total: %lu\n", list.size()); +} diff --git a/src/moves/moves_generate.c b/src/moves/moves_generate.c @@ -1,131 +0,0 @@ -#include "board.h" -#include "moves.h" - -#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); \ - move_list_add(moves, move); \ - } - -MoveList *move_list_generate(MoveList *moves, const Board *board) { - Move move; - Square src, tgt; - eColor color = board_side(board); - - if (!moves) - moves = move_list_new(); - else - move_list_reset(moves); - - // pawn moves - Piece piece = piece_get(PAWN, color); - U64 bitboard = board_pieceSet(board, piece); - bitboard_for_each_bit(src, bitboard) { - { // quiet - int add = (color == WHITE) ? +8 : -8; - tgt = src + add; - if (!board_square_isOccupied(board, tgt)) { - if (pawn_canPromote(color, src)) { - pawn_promote(src, tgt, piece, 0); - } else { - move_list_add(moves, - move_encode(src, tgt, piece, 0, 0, 0, 0, 0)); - - // two ahead - if (pawn_onStart(color, src) && - !board_square_isOccupied(board, tgt += add)) - move_list_add( - moves, move_encode(src, tgt, piece, 0, 0, 1, 0, 0)); - } - } - } - { // capture - U64 attack = board_piece_attacks(board, piece, src) & - board_color(board, !color); - bitboard_for_each_bit(tgt, attack) { - if (pawn_canPromote(color, src)) { - pawn_promote(src, tgt, piece, - board_square_piece(board, tgt, !color)); - } else { - move_list_add(moves, move_encode(src, tgt, piece, - board_square_piece( - board, tgt, !color), - 0, 0, 0, 0)); - } - } - } - - { // en passant - if (board_enpassant(board) != no_sq && - board_piece_attacks(board, piece, src) & - (C64(1) << board_enpassant(board))) - move_list_add(moves, - move_encode(src, board_enpassant(board), piece, - piece_get(PAWN, !color), 0, 0, 1, 0)); - } - } - - // All piece move - for (int piece_idx = 1; piece_idx < 6; piece_idx++) { - Piece piece = piece_get(piece_idx, color); - U64 bitboard = board_pieceSet(board, piece); - bitboard_for_each_bit(src, bitboard) { - U64 attack = board_piece_attacks(board, piece, src) & - ~board_color(board, color); - bitboard_for_each_bit(tgt, attack) { - move_list_add( - moves, move_encode(src, tgt, piece, - board_square_piece(board, tgt, !color), - 0, 0, 0, 0)); - } - } - } - - // Castling - if (color == WHITE) { - Piece piece = piece_get(KING, WHITE); - if (board_castle(board) & WK) { - if (!board_square_isOccupied(board, f1) && - !board_square_isOccupied(board, g1) && - !board_square_isAttack(board, e1, BLACK) && - !board_square_isAttack(board, f1, BLACK)) - move_list_add(moves, move_encode(e1, g1, piece, 0, 0, 0, 0, 1)); - } - if (board_castle(board) & WQ) { - if (!board_square_isOccupied(board, d1) && - !board_square_isOccupied(board, c1) && - !board_square_isOccupied(board, b1) && - !board_square_isAttack(board, e1, BLACK) && - !board_square_isAttack(board, d1, BLACK)) - move_list_add(moves, move_encode(e1, c1, piece, 0, 0, 0, 0, 1)); - } - } else { - Piece piece = piece_get(KING, BLACK); - if (board_castle(board) & BK) { - if (!board_square_isOccupied(board, f8) && - !board_square_isOccupied(board, g8) && - !board_square_isAttack(board, e8, WHITE) && - !board_square_isAttack(board, f8, WHITE)) - move_list_add(moves, move_encode(e8, g8, piece, 0, 0, 0, 0, 1)); - } - if (board_castle(board) & BQ) { - if (!board_square_isOccupied(board, d8) && - !board_square_isOccupied(board, c8) && - !board_square_isOccupied(board, b8) && - !board_square_isAttack(board, e8, WHITE) && - !board_square_isAttack(board, d8, WHITE)) - move_list_add(moves, move_encode(e8, c8, piece, 0, 0, 0, 0, 1)); - } - } - - return moves; -} diff --git a/src/moves/moves_generate.cpp b/src/moves/moves_generate.cpp @@ -0,0 +1,162 @@ +#include "board.hpp" +#include "moves.hpp" +#include "piece.hpp" +#include "utils_cpp.hpp" + +#define pawn_canPromote(color, source) \ + ((color == Color::WHITE && source >= Square::a7 && \ + source <= Square::h7) || \ + (color == Color::BLACK && source >= Square::a2 && source <= Square::h2)) + +#define pawn_onStart(color, source) \ + ((color == Color::BLACK && source >= Square::a7 && \ + source <= Square::h7) || \ + (color == Color::WHITE && source >= Square::a2 && source <= Square::h2)) + +#define pawn_promote(source, target, piece, capture) \ + res.push_back( \ + {move_encode(source, target, &piece, 0, \ + &piece::get(piece::Type::KNIGHT, color), 0, 0, 0), \ + 0}); \ + res.push_back( \ + {move_encode(source, target, &piece, 0, \ + &piece::get(piece::Type::BISHOP, color), 0, 0, 0), \ + 0}); \ + res.push_back( \ + {move_encode(source, target, &piece, 0, \ + &piece::get(piece::Type::ROOK, color), 0, 0, 0), \ + 0}); \ + res.push_back( \ + {move_encode(source, target, &piece, 0, \ + &piece::get(piece::Type::QUEEN, color), 0, 0, 0), \ + 0}); + +std::vector<MoveE> move_list_generate(const Board &board) { + Move move; + uint8_t src_i, tgt_i; + + Color color = board.get_side(); + Color colorOther = color == Color::BLACK ? Color::WHITE : Color::BLACK; + + std::vector<MoveE> res; + res.reserve(256); + + // pawn moves + const piece::Piece &piece = piece::get(piece::Type::PAWN, color); + U64 bitboard = board.get_bitboard_piece(piece); + bitboard_for_each_bit(src_i, bitboard) { + // quiet + int add = (color == Color::WHITE) ? +8 : -8; + tgt_i = src_i + add; + Square src = static_cast<Square>(src_i); + Square tgt = static_cast<Square>(tgt_i); + if (!board.is_square_occupied(tgt)) { + if (pawn_canPromote(color, src)) { + pawn_promote(src_i, tgt_i, piece, nullptr); + continue; + } + res.push_back( + {move_encode(src_i, tgt_i, &piece, 0, 0, 0, 0, 0), 0}); + + // two ahead + if (pawn_onStart(color, src) && !board.is_square_occupied(tgt)) + res.push_back( + {move_encode(src_i, tgt_i + add, &piece, 0, 0, 1, 0, 0), + 0}); + } + + // capture + U64 attack = board.get_bitboard_piece_attacks(piece, src) & + board.get_bitboard_color(colorOther); + bitboard_for_each_bit(tgt_i, attack) { + const piece::Piece *capture = board.get_square_piece(tgt); + if (pawn_canPromote(color, src)) { + pawn_promote(src_i, tgt_i, piece, capture); + } else { + res.push_back( + {move_encode(src_i, tgt_i, &piece, capture, 0, 0, 0, 0), + 0}); + } + } + + // en passant + if (board.get_enpassant() != Square::no_sq && + board.get_bitboard_piece_attacks(piece, + static_cast<Square>(src_i)) & + (C64(1) << to_underlying(board.get_enpassant()))) + res.push_back( + {move_encode(src_i, to_underlying(board.get_enpassant()), + &piece, &piece::get(piece::Type::PAWN, colorOther), + 0, 0, 1, 0), + 0}); + } + + // All piece move + for (piece::Type type : ++piece::TypeIter()) { + const piece::Piece &piece = piece::get(type, color); + U64 bitboard = board.get_bitboard_piece(piece); + Square src = static_cast<Square>(src_i); + bitboard_for_each_bit(src_i, bitboard) { + U64 attack = board.get_bitboard_piece_attacks(piece, src) & + ~board.get_bitboard_color(color); + bitboard_for_each_bit(tgt_i, attack) { + Square tgt = static_cast<Square>(tgt_i); + res.push_back( + {move_encode(src_i, tgt_i, &piece, + board.get_square_piece(tgt), 0, 0, 0, 0), + 0}); + } + } + } + + // Castling + if (color == Color::WHITE) { + const piece::Piece &piece = piece::get(piece::Type::KING, Color::WHITE); + if (board.get_castle() & to_underlying(Board::Castle::WK)) { + if (!board.is_square_occupied(Square::f1) && + !board.is_square_occupied(Square::g1) && + !board.is_square_attacked(Square::e1, Color::BLACK) && + !board.is_square_attacked(Square::f1, Color::BLACK)) + res.push_back({move_encode(to_underlying(Square::e1), + to_underlying(Square::g1), &piece, 0, + 0, 0, 0, 1), + 0}); + } + if (board.get_castle() & to_underlying(Board::Castle::WQ)) { + if (!board.is_square_occupied(Square::d1) && + !board.is_square_occupied(Square::c1) && + !board.is_square_occupied(Square::b1) && + !board.is_square_attacked(Square::e1, Color::BLACK) && + !board.is_square_attacked(Square::d1, Color::BLACK)) + res.push_back({move_encode(to_underlying(Square::e1), + to_underlying(Square::c1), &piece, 0, + 0, 0, 0, 1), + 0}); + } + } else { + const piece::Piece &piece = piece::get(piece::Type::KING, Color::BLACK); + if (board.get_castle() & to_underlying(Board::Castle::BK)) { + if (!board.is_square_occupied(Square::f8) && + !board.is_square_occupied(Square::g8) && + !board.is_square_attacked(Square::e8, Color::WHITE) && + !board.is_square_attacked(Square::f8, Color::WHITE)) + res.push_back({move_encode(to_underlying(Square::e8), + to_underlying(Square::g8), &piece, 0, + 0, 0, 0, 1), + 0}); + } + if (board.get_castle() & to_underlying(Board::Castle::BQ)) { + if (!board.is_square_occupied(Square::d8) && + !board.is_square_occupied(Square::c8) && + !board.is_square_occupied(Square::b8) && + !board.is_square_attacked(Square::e8, Color::WHITE) && + !board.is_square_attacked(Square::d8, Color::WHITE)) + res.push_back({move_encode(to_underlying(Square::e8), + to_underlying(Square::c8), &piece, 0, + 0, 0, 0, 1), + 0}); + } + } + + return res; +} diff --git a/src/moves/moves_make.c b/src/moves/moves_make.c @@ -1,99 +0,0 @@ -#include "board.h" -#include "moves.h" -#include "zobrist.h" - -// 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 - -void _piece_remove(Board *self, Piece piece, Square square) { - board_piece_pop(self, piece, square); - self->hash ^= zobrist_key_piece(piece, square); -} - -void _piece_set(Board *self, Piece piece, Square square) { - board_piece_set(self, piece, square); - self->hash ^= zobrist_key_piece(piece, square); -} - -void _piece_move(Board *self, Piece piece, Square source, Square target) { - _piece_remove(self, piece, source); - _piece_set(self, piece, target); -} - -int move_make(Move move, Board *board, int flag) { - if (flag) { - if (move_capture(move)) return move_make(move, board, 0); - return 0; - } else { - Piece piece = move_piece(move); - eColor color = board_side(board); - Square source = move_source(move); - Square target = move_target(move); - Square ntarget = target + (color == WHITE ? -8 : +8); - - if (!move_capture(move)) { - if (move_promote(move)) { - _piece_remove(board, piece, source); - _piece_set(board, move_piece_promote(move), target); - } else { - _piece_move(board, piece, source, target); - } - } else { - if (move_enpassant(move)) { - _piece_move(board, piece, source, target); - _piece_remove(board, move_piece_capture(move), ntarget); - } else if (move_promote(move)) { - _piece_remove(board, piece, source); - _piece_remove(board, move_piece_capture(move), target); - _piece_set(board, move_piece_promote(move), target); - } else { - _piece_remove(board, piece, source); - _piece_remove(board, move_piece_capture(move), target); - _piece_set(board, piece, target); - } - } - - board_enpassant_set(board, move_double(move) ? ntarget : no_sq); - - if (move_castle(move)) { - Piece Rook = piece_get(ROOK, board_side(board)); - switch (target) { - case g1: - _piece_move(board, Rook, h1, f1); - break; - case c1: - _piece_move(board, Rook, a1, d1); - break; - case g8: - _piece_move(board, Rook, h8, f8); - break; - case c8: - _piece_move(board, Rook, a8, d8); - break; - default: - break; - } - } - - board->hash ^= zobrist_key_castle(board_castle(board)); - board_castle_and(board, castling_rights[source]); - board_castle_and(board, castling_rights[target]); - board->hash ^= zobrist_key_castle(board_castle(board)); - - if (!board_isCheck(board)) { - board_side_switch(board); - return 1; - } - return 0; - } -} diff --git a/src/moves/moves_make.cpp b/src/moves/moves_make.cpp @@ -0,0 +1,94 @@ +#include "board.hpp" +#include "moves.hpp" +#include "zobrist.hpp" + +// 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 + +void _piece_remove(Board &board, const piece::Piece &piece, Square square) { + board.pop_piece(piece, square); + board.xor_hash(zobrist_key_piece(piece, square)); +} + +void _piece_set(Board &board, const piece::Piece &piece, Square square) { + board.set_piece(piece, square); + board.xor_hash(zobrist_key_piece(piece, square)); +} + +void _piece_move(Board &board, const piece::Piece &piece, Square source, + Square target) { + _piece_remove(board, piece, source); + _piece_set(board, piece, target); +} + +int move_make(Move move, Board &board, int flag) { + if (flag) { + if (move_capture(move)) return move_make(move, board, 0); + return 0; + } else { + const piece::Piece &piece = move_piece(move); + const Color color = board.get_side(); + const Square source = static_cast<Square>(move_source(move)); + const Square target = static_cast<Square>(move_target(move)); + const Square ntarget = static_cast<Square>( + move_target(move) + (color == Color::WHITE ? -8 : +8)); + + if (!move_capture(move)) { + if (move_promote(move)) { + _piece_remove(board, piece, source); + _piece_set(board, move_piece_promote(move), target); + } else { + _piece_move(board, piece, source, target); + } + } else { + if (move_enpassant(move)) { + _piece_move(board, piece, source, target); + _piece_remove(board, move_piece_capture(move), ntarget); + } else if (move_promote(move)) { + _piece_remove(board, piece, source); + _piece_remove(board, move_piece_capture(move), target); + _piece_set(board, move_piece_promote(move), target); + } else { + _piece_remove(board, piece, source); + _piece_remove(board, move_piece_capture(move), target); + _piece_set(board, piece, target); + } + } + + board.set_enpassant(move_double(move) ? ntarget : Square::no_sq); + + if (move_castle(move)) { + static const piece::Piece &rook = + piece::get(piece::Type::ROOK, board.get_side()); + if (target == Square::g1) + _piece_move(board, rook, Square::h1, Square::f1); + if (target == Square::c1) + _piece_move(board, rook, Square::a1, Square::d1); + if (target == Square::g8) + _piece_move(board, rook, Square::h8, Square::f8); + if (target == Square::c8) + _piece_move(board, rook, Square::a8, Square::d8); + } + + board.xor_hash(zobrist_key_castle(board.get_castle())); + board.and_castle(castling_rights[move_source(move)]); + board.and_castle(castling_rights[move_target(move)]); + board.xor_hash(zobrist_key_castle(board.get_castle())); + + if (!board.is_check()) { + board.switch_side(); + return 1; + } + return 0; + } +} diff --git a/src/perft/CMakeLists.txt b/src/perft/CMakeLists.txt @@ -5,7 +5,7 @@ project( LANGUAGES C CXX ) -add_executable(perft perft.c) +add_executable(perft perft.cpp) option(WITH_FULL_COUNT "Make count on types of moves" OFF) if(WITH_FULL_COUNT) @@ -17,7 +17,6 @@ target_link_libraries(perft PRIVATE board PRIVATE moves PRIVATE piece - PRIVATE utils PRIVATE random ) diff --git a/src/perft/perft.c b/src/perft/perft.c @@ -1,183 +0,0 @@ -#include <pthread.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> - -#include <cul/assert.h> - -#include "attacks.h" -#include "board.h" -#include "moves.h" -#include "perft.h" -#include "utils.h" -#include "zobrist.h" - -// FEN debug positions -#define tricky_position \ - "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 " -#define killer_position \ - "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1" -#define cmk_position \ - "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 " - -void perft_result_print(PerftResult res) { - printf(" - Perft Results -\n\n"); - printf(" Nodes: %llu\n", res.node); -#ifdef USE_FULL_COUNT - printf(" Captures: %llu\n", res.capture); - printf(" Enpassants: %llu\n", res.enpassant); - printf(" Castles: %llu\n", res.castle); - printf(" Promotions: %llu\n", res.promote); - printf(" Checks: %llu\n", res.check); - // printf("Discovered Checks: %llu\n", res.checkDiscovered); - // printf(" Dobule Checks: %llu\n", res.checkDouble); - // printf(" Checkmates: %llu\n", res.checkmate); -#else - printf("Other stats are disabled in this build...\n"); -#endif -} - -void perft_result_add(PerftResult *base, PerftResult *add) { - base->node += add->node; -#ifdef USE_FULL_COUNT - base->capture += add->capture; - base->enpassant += add->enpassant; - base->castle += add->castle; - base->promote += add->promote; - base->check += add->check; - // base->checkDiscovered += add->checkDiscovered; - // base->checkDouble += add->checkDouble; - // base->checkmate += add->checkmate; -#endif -} - -void perft_driver(Board *board, struct MoveList *moveList, int depth, - PerftResult *result) { - MoveList *list = move_list_generate(&moveList[depth], board); - Board *copy = board_new(); - - for (int i = 0; i < move_list_size(list); i++) { - Move move = move_list_index_move(list, i); - board_copy(board, copy); - if (!move_make(move, copy, 0)) continue; - - if (depth != 1) { - perft_driver(copy, moveList, depth - 1, result); - } else { - result->node++; -#ifdef USE_FULL_COUNT - if (board_isCheck(copy)) result->check++; - if (move_capture(move)) result->capture++; - if (move_enpassant(move)) result->enpassant++; - if (move_castle(move)) result->castle++; - if (move_promote(move)) result->promote++; -#endif - } - } - - move_list_reset(list); - board_free(&copy); -} - -typedef struct perf_shared perf_shared; -struct perf_shared { - const char *fen; - MoveList *list; - int depth; - pthread_mutex_t mutex; - unsigned int index; - PerftResult result; -}; - -void *perft_thread(void *arg) { - PerftResult result = {0}; - perf_shared *shared = (perf_shared *)arg; - struct MoveList moveList[shared->depth + 1]; - - Board *board = board_from_FEN(NULL, shared->fen); - Board *copy = board_new(); - - while (1) { - pthread_mutex_lock(&shared->mutex); - perft_result_add(&shared->result, &result); - if (shared->index >= move_list_size(shared->list)) { - pthread_mutex_unlock(&shared->mutex); - break; - } - Move move = move_list_index_move(shared->list, shared->index++); - pthread_mutex_unlock(&shared->mutex); - - result = (PerftResult){0}; - - board_copy(board, copy); - if (!move_make(move, copy, 0)) continue; - - if (shared->depth != 1) { - perft_driver(copy, moveList, shared->depth - 1, &result); - } else { - result.node++; -#ifdef USE_FULL_COUNT - if (board_isCheck(copy)) result.check++; - if (move_capture(move)) result.capture++; - if (move_enpassant(move)) result.enpassant++; - if (move_castle(move)) result.castle++; - if (move_promote(move)) result.promote++; -#endif - } - } - board_free(&board); - return NULL; -} - -PerftResult perft_test(const char *fen, int depth, int thread_num) { - assert(fen); - assert(depth > 0); - - pthread_t threads[thread_num]; - perf_shared shared = (perf_shared){ - .list = move_list_generate(NULL, board_from_FEN(NULL, fen)), - .depth = depth, - .fen = fen, - }; - - pthread_mutex_init(&shared.mutex, NULL); - for (int i = 0; i < thread_num; i++) - pthread_create(threads + i, NULL, perft_thread, (void *)(&shared)); - - for (int i = 0; i < thread_num; i++) - pthread_join(threads[i], NULL); - - move_list_free(&shared.list); - return shared.result; -} - -int main(int argc, char *argv[]) { - - int c, depth = 1, thread_num = 1; - char *fen = start_position; - while ((c = getopt(argc, argv, "t:f:d:")) != -1) { - switch (c) { - case 't': - thread_num = atoi(optarg); - if (thread_num <= 0) abort(); - break; - case 'f': - fen = optarg; - break; - case 'd': - depth = atoi(optarg); - if (depth <= 0) abort(); - break; - default: - abort(); - } - } - - attacks_init(); - zobrist_init(); - - board_print(board_from_FEN(NULL, fen)); - PerftResult res = perft_test(fen, depth, thread_num); - perft_result_print(res); -} diff --git a/src/perft/perft.cpp b/src/perft/perft.cpp @@ -0,0 +1,173 @@ +#include <pthread.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "attacks.hpp" +#include "board.hpp" +#include "moves.hpp" +#include "perft.hpp" +#include "utils_cpp.hpp" +#include "zobrist.hpp" + +// FEN debug positions +#define tricky_position \ + "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 " +#define killer_position \ + "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1" +#define cmk_position \ + "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 " + +void perft_result_print(PerftResult res) { + printf(" - Perft Results -\n\n"); + printf(" Nodes: %llu\n", res.node); +#ifdef USE_FULL_COUNT + printf(" Captures: %llu\n", res.capture); + printf(" Enpassants: %llu\n", res.enpassant); + printf(" Castles: %llu\n", res.castle); + printf(" Promotions: %llu\n", res.promote); + printf(" Checks: %llu\n", res.check); + // printf("Discovered Checks: %llu\n", res.checkDiscovered); + // printf(" Dobule Checks: %llu\n", res.checkDouble); + // printf(" Checkmates: %llu\n", res.checkmate); +#else + printf("Other stats are disabled in this build...\n"); +#endif +} + +void perft_result_add(PerftResult *base, PerftResult *add) { + base->node += add->node; +#ifdef USE_FULL_COUNT + base->capture += add->capture; + base->enpassant += add->enpassant; + base->castle += add->castle; + base->promote += add->promote; + base->check += add->check; + // base->checkDiscovered += add->checkDiscovered; + // base->checkDouble += add->checkDouble; + // base->checkmate += add->checkmate; +#endif +} + +typedef std::vector<MoveE> MoveList; + +void perft_driver(Board &board, MoveList *moveList, int depth, + PerftResult *result) { + moveList[depth] = move_list_generate(board); + Board copy; + + for (const auto [move, _] : moveList[depth]) { + copy = board; + if (!move_make(move, copy, 0)) continue; + + if (depth != 1) { + perft_driver(copy, moveList, depth - 1, result); + } else { + result->node++; +#ifdef USE_FULL_COUNT + if (board_isCheck(copy)) result->check++; + if (move_capture(move)) result->capture++; + if (move_enpassant(move)) result->enpassant++; + if (move_castle(move)) result->castle++; + if (move_promote(move)) result->promote++; +#endif + } + } +} + +typedef struct perf_shared perf_shared; +struct perf_shared { + MoveList list; + int depth; + const char *fen; + pthread_mutex_t mutex; + unsigned int index; + PerftResult result; +}; + +void *perft_thread(void *arg) { + PerftResult result = {0}; + perf_shared *shared = (perf_shared *)arg; + MoveList moveList[shared->depth + 1]; + + Board board = Board(shared->fen), copy; + + while (1) { + pthread_mutex_lock(&shared->mutex); + perft_result_add(&shared->result, &result); + if (shared->index >= shared->list.size()) { + pthread_mutex_unlock(&shared->mutex); + break; + } + Move move = shared->list[shared->index++].move; + pthread_mutex_unlock(&shared->mutex); + + result = (PerftResult){0}; + + copy = board; + if (!move_make(move, copy, 0)) continue; + + if (shared->depth != 1) { + perft_driver(copy, moveList, shared->depth - 1, &result); + } else { + result.node++; +#ifdef USE_FULL_COUNT + if (board_isCheck(copy)) result.check++; + if (move_capture(move)) result.capture++; + if (move_enpassant(move)) result.enpassant++; + if (move_castle(move)) result.castle++; + if (move_promote(move)) result.promote++; +#endif + } + } + return NULL; +} + +PerftResult perft_test(const char *fen, int depth, int thread_num) { + pthread_t threads[thread_num]; + perf_shared shared = (perf_shared){ + .list = move_list_generate(Board(fen)), + .depth = depth, + .fen = fen, + }; + + pthread_mutex_init(&shared.mutex, NULL); + for (int i = 0; i < thread_num; i++) + pthread_create(threads + i, NULL, perft_thread, (void *)(&shared)); + + for (int i = 0; i < thread_num; i++) + pthread_join(threads[i], NULL); + + return shared.result; +} + +int main(int argc, char *argv[]) { + + int c, depth = 1, thread_num = 1; + std::string s(start_position); + const char *fen = s.data(); + while ((c = getopt(argc, argv, "t:f:d:")) != -1) { + switch (c) { + case 't': + thread_num = atoi(optarg); + if (thread_num <= 0) abort(); + break; + case 'f': + fen = optarg; + break; + case 'd': + depth = atoi(optarg); + if (depth <= 0) abort(); + break; + default: + abort(); + } + } + + attacks_init(); + zobrist_init(); + + PerftResult res = perft_test(fen, depth, thread_num); + perft_result_print(res); +} diff --git a/src/piece/piece.cpp b/src/piece/piece.cpp @@ -1,6 +1 @@ #include "piece.hpp" - -#include <iostream> -int main(void) { - std::cout << piece::get(piece::Type::PAWN, Color::WHITE).code << std::endl; -} diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt @@ -1,13 +1,5 @@ -add_library(utils OBJECT - utils.c -) - -target_include_directories(utils - PUBLIC "${PROJECT_SOURCE_DIR}/src/include" -) - add_library(random OBJECT - random.c + random.cpp ) target_include_directories(random diff --git a/src/utils/random.c b/src/utils/random.c @@ -1,26 +0,0 @@ -#include "random.h" - -U32 state = C32(1804289383); - -void random_state_reset() { state = C32(1804289383); } - -U32 random_get_U32() { - U32 number = state; - - number ^= number << 13; - number ^= number >> 17; - number ^= number << 5; - - return state = number; -} - -U64 random_get_U64() { - U64 n1, n2, n3, n4; - - n1 = (U64)(random_get_U32()) & C64(0xFFFF); - n2 = (U64)(random_get_U32()) & C64(0xFFFF); - n3 = (U64)(random_get_U32()) & C64(0xFFFF); - n4 = (U64)(random_get_U32()) & C64(0xFFFF); - - return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48); -} diff --git a/src/utils/random.cpp b/src/utils/random.cpp @@ -0,0 +1,26 @@ +#include "random.hpp" + +U32 state = C32(1804289383); + +void random_state_reset() { state = C32(1804289383); } + +U32 random_get_U32() { + U32 number = state; + + number ^= number << 13; + number ^= number >> 17; + number ^= number << 5; + + return state = number; +} + +U64 random_get_U64() { + U64 n1, n2, n3, n4; + + n1 = (U64)(random_get_U32()) & C64(0xFFFF); + n2 = (U64)(random_get_U32()) & C64(0xFFFF); + n3 = (U64)(random_get_U32()) & C64(0xFFFF); + n4 = (U64)(random_get_U32()) & C64(0xFFFF); + + return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48); +} diff --git a/src/utils/utils.c b/src/utils/utils.c @@ -1,64 +0,0 @@ -#include <stdio.h> -#include <sys/time.h> - -#include "utils.h" - -const U64 universe = C64(0xffffffffffffffff); -const U64 notAFile = C64(0xfefefefefefefefe); -const U64 notHFile = C64(0x7f7f7f7f7f7f7f7f); - -inline uint8_t bit_count(U64 bitboard) { -#if __has_builtin(__builtin_popcountll) - return __builtin_popcountll(bitboard); -#endif - - int count = 0; - for (; bitboard > 0; bitboard &= bitboard - 1) - count++; - return count; -} - -inline uint8_t bit_lsb_index(U64 bitboard) { -#if __has_builtin(__builtin_ffsll) - return __builtin_ffsll(bitboard) - 1; -#endif - - if (!bitboard) return -1; - return bit_count((bitboard & -bitboard) - 1); -} - -U64 soutOne(U64 b) { return b >> 8; } -U64 nortOne(U64 b) { return b << 8; } -U64 eastOne(U64 b) { return (b & notHFile) << 1; } -U64 westOne(U64 b) { return (b & notAFile) >> 1; } -U64 soEaOne(U64 b) { return (b & notHFile) >> 7; } -U64 soWeOne(U64 b) { return (b & notAFile) >> 9; } -U64 noEaOne(U64 b) { return (b & notHFile) << 9; } -U64 noWeOne(U64 b) { return (b & notAFile) << 7; } - -// board rotation -U64 rotateLeft(U64 x, int s) { return (x << s) | (x >> (64 - s)); } -U64 rotateRight(U64 x, int s) { return (x >> s) | (x << (64 - s)); } - -// clang-format off -const char *square_to_coordinates[]={ - "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", - "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", - "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", - "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", - "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", - "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", - "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", - "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", " " -}; -// clang-format on - -eSquare coordinates_to_square(const char *cord) { - return (cord[1] - '1') * 8 + (cord[0] - 'a'); -} - -int get_time_ms(void) { - struct timeval time; - gettimeofday(&time, NULL); - return time.tv_sec * 1000 + time.tv_usec / 1000; -}