stellarUCI 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 21:36:20 +0200 |
Rest of the rewrite, - debugging...
Diffstat:M | src/attacks/CMakeLists.txt | | | - |
M | src/attacks/magic_generate.cpp | | | +++++++++++------ |
M | src/board/CMakeLists.txt | | | ++-- |
M | src/board/board.cpp | | | +++++++++++++---------- |
M | src/engine/CMakeLists.txt | | | +++---- |
D | src/engine/engine.c | | | --------------------------------------------------------------------------------- |
A | src/engine/engine.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/engine/score.c | | | --------------------------------------------------------------------------------- |
A | src/engine/score.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/engine/score.h | | | ------------- |
A | src/engine/score.hpp | | | +++++++++++++++++ |
D | src/engine/stats.h | | | ---------------------- |
A | src/engine/stats.hpp | | | ++++++++++++++++++++++ |
D | src/engine/transposition.c | | | --------------------------------------------------------------------------------- |
A | src/engine/transposition.cpp | | | ++++++++++++++++++++++++++++++++++++++ |
D | src/engine/transposition.h | | | ------------------------------ |
A | src/engine/transposition.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++ |
M | src/include/board.hpp | | | +++---- |
D | src/include/moves.h | | | ------------------------------------------------------------------- |
A | src/include/moves.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/include/perft.h | | | ------------------------ |
A | src/include/perft.hpp | | | ++++++++++++++++++++++++ |
M | src/include/piece.hpp | | | +++- |
D | src/include/random.h | | | ---------- |
D | src/include/utils.h | | | ------------------------------------------------------------------------------- |
M | src/include/utils_cpp.hpp | | | ++++ |
M | src/moves/CMakeLists.txt | | | +++--- |
D | src/moves/moves.c | | | --------------------------------------------------------------------------------- |
A | src/moves/moves.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/moves/moves_generate.c | | | --------------------------------------------------------------------------------- |
A | src/moves/moves_generate.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/moves/moves_make.c | | | --------------------------------------------------------------------------------- |
A | src/moves/moves_make.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/perft/CMakeLists.txt | | | +-- |
D | src/perft/perft.c | | | --------------------------------------------------------------------------------- |
A | src/perft/perft.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/piece/piece.cpp | | | ----- |
M | src/utils/CMakeLists.txt | | | +--------- |
D | src/utils/random.c | | | -------------------------- |
A | src/utils/random.cpp | | | ++++++++++++++++++++++++++ |
D | src/utils/utils.c | | | ---------------------------------------------------------------- |
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, ©, move, 1)) continue;
score = -quiescence(stats, -beta, -alpha);
stats_move_unmake(stats, ©);
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, ©);
score = -negamax(stats, -beta, -beta + 1,
depth - 1 - REDUCTION_MOVE, false);
stats_move_unmake(stats, ©);
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, ©, move, 0)) continue;
legal_moves++;
// futility pruning
if (futility && searched && !move_capture(move) &&
!move_promote(move) && !board_isCheck(stats->board)) {
stats_move_unmake(stats, ©);
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, ©);
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 ©, 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 = stats.board;
stats.board.switch_side();
stats.board.set_enpassant(Square::no_sq);
stats.ply++;
}
void stats_move_unmake(Stats &stats, Board ©) {
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(©);
}
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,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;
}