stellar

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

commit3d9fb77e4f91af7bf2fc59cfb14c012007cb9ae0
parent11aa12bf92ffa2c1f685a8ec4f9dfe4135223e7a
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateFri, 28 Jul 2023 21:21:40 +0200

Split engine into multiple files for easy refactor

Diffstat:
Ainclude/moves.h|++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/perft.h|+++++++++
Ainclude/score.h|++++++++++
Msrc/engine.c|+---------------------------------------------------------------------------------
Asrc/moves.c|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/perft.c|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/score.c|+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

7 files changed, 592 insertions(+), 541 deletions(-)


diff --git a/include/moves.h b/include/moves.h

@@ -0,0 +1,54 @@

#ifndef MOVES_H
#define MOVES_H
#include <stdint.h>
#include "CBoard.h"
typedef struct Move Move;
struct Move {
unsigned source : 6;
unsigned target : 6;
unsigned piece : 5;
unsigned piece_capture : 5;
unsigned piece_promote : 5;
unsigned dbl : 1;
unsigned enpassant : 1;
unsigned castle : 1;
unsigned capture : 1;
unsigned promote : 1;
};
typedef struct MoveList_T *MoveList_T;
struct MoveList_T {
Move moves[256];
int count;
};
int Move_cmp(Move a, Move b);
Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture,
Piece_T Promote, int dbl, int enpassant, int castle);
void Move_print(Move move);
MoveList_T MoveList_new(void);
void MoveList_free(MoveList_T *p);
Move MoveList_move(MoveList_T self, int index);
int MoveList_size(MoveList_T self);
void MoveList_reset(MoveList_T self);
void MoveList_add(MoveList_T self, Move move);
void MoveList_print(MoveList_T self);
MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board);
int Move_make(Move move, CBoard_T board, int flag);
#define Move_source(move) (move.source)
#define Move_target(move) (move.target)
#define Move_double(move) (move.dbl)
#define Move_enpassant(move) (move.enpassant)
#define Move_castle(move) (move.castle)
#define Move_capture(move) (move.capture)
#define Move_promote(move) (move.promote)
#define Move_piece(move) (Piece_fromIndex(move.piece))
#define Move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
#define Move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
#endif

diff --git a/include/perft.h b/include/perft.h

@@ -0,0 +1,9 @@

#ifndef PERFT_H
#define PERFT_H
#include "CBoard.h"
void perft_test_threaded(CBoard_T board, int depth);
void perft_test(CBoard_T board, int depth);
#endif

diff --git a/include/score.h b/include/score.h

@@ -0,0 +1,10 @@

#ifndef SCORE_H
#define SCORE_H
#include "CBoard.h"
int Score_capture(ePiece src, ePiece tgt);
int Score_position(ePiece piece, eColor color, Square square);
int Score_value(ePiece piece);
#endif

diff --git a/src/engine.c b/src/engine.c

@@ -1,11 +1,13 @@

#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CBoard.h"
#include "attack.h"
#include "moves.h"
#include "perft.h"
#include "score.h"
#include "utils.h"
#include <cul/assert.h>

@@ -24,177 +26,6 @@

#define cmk_position \
"r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
typedef struct Move Move;
struct Move {
unsigned source : 6;
unsigned target : 6;
unsigned piece : 5;
unsigned piece_capture : 5;
unsigned piece_promote : 5;
unsigned dbl : 1;
unsigned enpassant : 1;
unsigned castle : 1;
unsigned capture : 1;
unsigned promote : 1;
};
int Move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; }
Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture,
Piece_T Promote, int dbl, int enpassant, int castle) {
return (Move){
.source = src,
.target = tgt,
.dbl = dbl,
.enpassant = enpassant,
.castle = castle,
.capture = Capture != NULL,
.promote = Promote != NULL,
.piece = Piece_index(Piece),
.piece_capture = Capture ? Piece_index(Capture) : 0,
.piece_promote = Promote ? Piece_index(Promote) : 0,
};
}
#define Move_source(move) (move.source)
#define Move_target(move) (move.target)
#define Move_double(move) (move.dbl)
#define Move_enpassant(move) (move.enpassant)
#define Move_castle(move) (move.castle)
#define Move_capture(move) (move.capture)
#define Move_promote(move) (move.promote)
#define Move_piece(move) (Piece_fromIndex(move.piece))
#define Move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
#define Move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
// clang-format off
const int castling_rights[64] = {
13, 15, 15, 15, 12, 15, 15, 14,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
7, 15, 15, 15, 3, 15, 15, 11,
};
struct Score_T {
int value;
int position[64];
int capture[6];
};
struct Score_T Scores[] = {
[PAWN] = {
.value = 100,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -10, -10, 0, 0, 0,
0, 0, 0, 5, 5, 0, 0, 0,
5, 5, 10, 20, 20, 5, 5, 5,
10, 10, 10, 20, 20, 10, 10, 10,
20, 20, 20, 30, 30, 30, 20, 20,
30, 30, 30, 40, 40, 30, 30, 30,
90, 90, 90, 90, 90, 90, 90, 90, },
.capture = { [PAWN] = 105, [KNIGHT] = 205,
[BISHOP] = 305, [ROOK] = 405,
[QUEEN] = 505, [KING] = 605} },
[KNIGHT] = {
.value = 300,
.position = {
-5, -10 , 0, 0, 0, 0, -10, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 5, 20, 10, 10, 20, 5, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 5, 20, 20, 20, 20, 5, -5,
-5, 0, 0, 10, 10, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5, },
.capture = { [PAWN] = 104, [KNIGHT] = 204,
[BISHOP] = 304, [ROOK] = 404,
[QUEEN] = 504, [KING] = 604} },
[BISHOP] = {
.value = 350,
.position = {
0, 0, -10, 0, 0, -10, 0, 0,
0, 30, 0, 0, 0, 0, 30, 0,
0, 10, 0, 0, 0, 0, 10, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 0, 10, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 103, [KNIGHT] = 203,
[BISHOP] = 303, [ROOK] = 403,
[QUEEN] = 503, [KING] = 603} },
[ROOK] = {
.value = 500,
.position = {
0, 0, 0, 20, 20, 0, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, },
.capture = { [PAWN] = 102, [KNIGHT] = 202,
[BISHOP] = 302, [ROOK] = 402,
[QUEEN] = 502, [KING] = 602} },
[QUEEN] = {
.value = 1000,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 101, [KNIGHT] = 201,
[BISHOP] = 301, [ROOK] = 401,
[QUEEN] = 501, [KING] = 601} },
[KING] = {
.value = 10000,
.position = {
0, 0, 5, 0, -15, 0, 10, 0,
0, 5, 5, -5, -5, 0, 5, 0,
0, 0, 5, 10, 10, 5, 0, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 5, 10, 10, 5, 5, 0,
0, 0, 5, 5, 5, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 100, [KNIGHT] = 200,
[BISHOP] = 300, [ROOK] = 400,
[QUEEN] = 500, [KING] = 600} },
};
const int mirror_score[128] =
{
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1, no_sq,
};
// clang-format on
int Score_value(ePiece piece) { return Scores[piece].value; }
int Score_position(ePiece piece, eColor color, Square square) {
if (color == BLACK) square = mirror_score[square];
return Scores[piece].position[square];
}
int Score_capture(ePiece src, ePiece tgt) { return Scores[src].capture[tgt]; }
#define MAX_PLY 64
typedef struct Stats_T *Stats_T;

@@ -232,47 +63,6 @@ int Move_score(Stats_T stats, Move move) {

return 0;
}
void Move_print(Move move) {
printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n",
square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)],
Piece_unicode(Move_piece(move)),
Move_capture(move) ? Piece_unicode(Move_piece_capture(move)) : "X ",
Move_promote(move) ? Piece_unicode(Move_piece_promote(move)) : "X ",
Move_double(move) ? 1 : 0, Move_enpassant(move) ? 1 : 0,
Move_castle(move) ? 1 : 0, Move_capture(move) ? 1 : 0,
Move_promote(move) ? 1 : 0);
}
typedef struct MoveList_T *MoveList_T;
struct MoveList_T {
Move moves[256];
int count;
};
MoveList_T MoveList_new(void) {
MoveList_T p;
NEW0(p);
return p;
}
void MoveList_free(MoveList_T *p) { FREE(*p); }
Move MoveList_move(MoveList_T self, int index) { return self->moves[index]; }
int MoveList_size(MoveList_T self) { return self->count; }
void MoveList_reset(MoveList_T self) { self->count = 0; }
void MoveList_add(MoveList_T self, Move move) {
self->moves[self->count++] = move;
}
void MoveList_print(MoveList_T self) {
printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n");
for (int i = 0; i < self->count; i++)
Move_print(self->moves[i]);
printf("Total: %d\n", self->count);
}
void MoveList_sort(Stats_T stats, MoveList_T list) {
int score[list->count];
for (int i = 0; i < list->count; i++)

@@ -291,206 +81,6 @@ void MoveList_sort(Stats_T stats, MoveList_T list) {

}
}
#define pawn_canPromote(color, source) \
((color == WHITE && source >= a7 && source <= h7) || \
(color == BLACK && source >= a2 && source <= h2))
#define pawn_onStart(color, source) \
((color == BLACK && source >= a7 && source <= h7) || \
(color == WHITE && source >= a2 && source <= h2))
#define pawn_promote(source, target, Piece, Capture) \
for (int i = 1; i < 5; i++) { \
move = Move_encode(source, target, Piece, Capture, \
Piece_get(i, color), 0, 0, 0); \
MoveList_add(moves, move); \
}
MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board) {
Move move;
Square src, tgt;
eColor color = CBoard_side(board);
if (!moves) moves = MoveList_new();
{ // pawn moves
Piece_T Piece = Piece_get(PAWN, color);
U64 bitboard = CBoard_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
{ // quiet
int add = (color == WHITE) ? +8 : -8;
tgt = src + add;
if (tgt > a1 && tgt < h8 &&
!CBoard_square_isOccupied(board, tgt)) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece, 0);
} else {
MoveList_add(
moves, Move_encode(src, tgt, Piece, 0, 0, 0, 0, 0));
// two ahead
if (pawn_onStart(color, src) &&
!CBoard_square_isOccupied(board, tgt += add))
MoveList_add(moves, Move_encode(src, tgt, Piece, 0,
0, 1, 0, 0));
}
}
}
{ // capture
U64 attack = CBoard_piece_attacks(board, Piece, src) &
CBoard_colorBB(board, !color);
bitboard_for_each_bit(tgt, attack) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece,
CBoard_square_piece(board, tgt, !color));
} else {
MoveList_add(moves, Move_encode(src, tgt, Piece,
CBoard_square_piece(
board, tgt, !color),
0, 0, 0, 0));
}
}
}
{ // en passant
if (CBoard_enpassant(board) != no_sq &&
CBoard_piece_attacks(board, Piece, src) &
(C64(1) << CBoard_enpassant(board)))
MoveList_add(
moves,
Move_encode(src, CBoard_enpassant(board), Piece,
CBoard_square_piece(board, tgt, !color), 0,
0, 1, 0));
}
}
}
// All piece move
for (int piece = 1; piece < 6; piece++) {
Piece_T Piece = Piece_get(piece, color);
U64 bitboard = CBoard_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
U64 attack = CBoard_piece_attacks(board, Piece, src) &
~CBoard_colorBB(board, color);
bitboard_for_each_bit(tgt, attack) {
/* int take = bit_get(CBoard_colorBB(board, !color), tgt); */
MoveList_add(
moves, Move_encode(src, tgt, Piece,
CBoard_square_piece(board, tgt, !color),
0, 0, 0, 0));
}
}
}
// Castling
{
if (color == WHITE) {
Piece_T Piece = Piece_get(KING, WHITE);
if (CBoard_castle(board) & WK) {
if (!CBoard_square_isOccupied(board, f1) &&
!CBoard_square_isOccupied(board, g1) &&
!CBoard_square_isAttack(board, e1, BLACK) &&
!CBoard_square_isAttack(board, f1, BLACK))
MoveList_add(moves,
Move_encode(e1, g1, Piece, 0, 0, 0, 0, 1));
}
if (CBoard_castle(board) & WQ) {
if (!CBoard_square_isOccupied(board, d1) &&
!CBoard_square_isOccupied(board, c1) &&
!CBoard_square_isOccupied(board, b1) &&
!CBoard_square_isAttack(board, e1, BLACK) &&
!CBoard_square_isAttack(board, d1, BLACK))
MoveList_add(moves,
Move_encode(e1, c1, Piece, 0, 0, 0, 0, 1));
}
} else {
Piece_T Piece = Piece_get(KING, BLACK);
if (CBoard_castle(board) & BK) {
if (!CBoard_square_isOccupied(board, f8) &&
!CBoard_square_isOccupied(board, g8) &&
!CBoard_square_isAttack(board, e8, WHITE) &&
!CBoard_square_isAttack(board, f8, WHITE))
MoveList_add(moves,
Move_encode(e8, g8, Piece, 0, 0, 0, 0, 1));
}
if (CBoard_castle(board) & BQ) {
if (!CBoard_square_isOccupied(board, d8) &&
!CBoard_square_isOccupied(board, c8) &&
!CBoard_square_isOccupied(board, b8) &&
!CBoard_square_isAttack(board, e8, WHITE) &&
!CBoard_square_isAttack(board, d8, WHITE))
MoveList_add(moves,
Move_encode(e8, c8, Piece, 0, 0, 0, 0, 1));
}
}
}
return moves;
}
int Move_make(Move move, CBoard_T board, int flag) {
if (flag == 0) {
Square source = Move_source(move);
Square target = Move_target(move);
Piece_T Piece = Move_piece(move);
eColor color = CBoard_side(board);
if (!Move_capture(move))
CBoard_piece_move(board, Piece, source, target);
else
CBoard_piece_capture(board, Piece, Move_piece_capture(move), source,
target);
if (Move_promote(move)) {
CBoard_piece_pop(board, Piece, target);
CBoard_piece_set(board, Move_piece_promote(move), target);
}
{
int ntarget = target + (color == WHITE ? -8 : +8);
if (Move_enpassant(move))
CBoard_piece_pop(board, Piece_get(PAWN, !color), ntarget);
CBoard_enpassant_set(board, Move_double(move) ? ntarget : no_sq);
}
if (Move_castle(move)) {
Piece_T Rook = Piece_get(ROOK, CBoard_side(board));
switch (target) {
case g1:
CBoard_piece_move(board, Rook, h1, f1);
break;
case c1:
CBoard_piece_move(board, Rook, a1, d1);
break;
case g8:
CBoard_piece_move(board, Rook, h8, f8);
break;
case c8:
CBoard_piece_move(board, Rook, a8, d8);
break;
default:
break;
}
}
CBoard_castle_and(board, castling_rights[source]);
CBoard_castle_and(board, castling_rights[target]);
if (!CBoard_isCheck(board)) {
CBoard_side_switch(board);
return 1;
} else
return 0;
} else {
if (Move_capture(move))
return Move_make(move, board, 0);
else
return 0;
}
}
/* SEARCHING */
int evaluate(CBoard_T board) {

@@ -629,8 +219,6 @@ int negamax(Stats_T stats, CBoard_T board, int alpha, int beta, int depth) {

return alpha;
}
/* UCI */
void Move_print_UCI(Move move) {
printf("%s%s", square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)]);

@@ -840,132 +428,6 @@ void uci_loop(void) {

CBoard_free(&board);
}
/* PERFT */
struct MoveList_T moveList[10];
long nodes;
void perft_driver(CBoard_T board, struct MoveList_T *moveList, int depth,
unsigned long long *nodes) {
if (depth == 0) {
(*nodes)++;
return;
}
MoveList_T list = MoveList_generate(&moveList[depth], board);
CBoard_T copy = CBoard_new();
for (int i = 0; i < MoveList_size(list); i++) {
CBoard_copy(board, copy);
if (!Move_make(MoveList_move(list, i), copy, 0)) continue;
perft_driver(copy, moveList, depth - 1, nodes);
}
MoveList_reset(list);
CBoard_free(&copy);
}
void perft_test(CBoard_T board, int depth) {
MoveList_T list = MoveList_generate(&moveList[depth], board);
CBoard_T copy = CBoard_new();
long start = get_time_ms();
printf("\n Performance test\n\n");
nodes = 0;
for (int i = 0; i < MoveList_size(list); i++) {
CBoard_copy(board, copy);
Move move = MoveList_move(list, i);
if (!Move_make(MoveList_move(list, i), copy, 0)) continue;
unsigned long long node = 0;
perft_driver(copy, moveList, depth - 1, &node);
printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)], node);
nodes += node;
}
MoveList_reset(list);
CBoard_free(&copy);
printf("\nNodes searched: %ld\n\n", nodes);
printf("\n Depth: %d\n", depth);
printf(" Nodes: %ld\n", nodes);
printf(" Time: %ld\n\n", get_time_ms() - start);
}
#include <pthread.h>
#include <semaphore.h>
typedef struct perf_shared perf_shared;
struct perf_shared {
CBoard_T board;
MoveList_T list;
int depth;
sem_t *mutex;
int *index;
sem_t *finish;
unsigned long long *total;
};
void *perft_thread(void *arg) {
perf_shared *shared = (perf_shared *)arg;
CBoard_T board = CBoard_new();
unsigned long long node_count = 0;
struct MoveList_T moveList[10];
while (1) {
sem_wait(shared->mutex);
*shared->total += node_count;
if (*shared->index >= MoveList_size(shared->list)) {
sem_post(shared->mutex);
break;
}
Move move = MoveList_move(shared->list, (*shared->index)++);
sem_post(shared->mutex);
CBoard_copy(shared->board, board);
if (!Move_make(move, board, 0)) continue;
node_count = 0;
perft_driver(board, moveList, shared->depth, &node_count);
printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)], node_count);
}
CBoard_free(&board);
sem_post(shared->finish);
return NULL;
}
void perft_test_threaded(CBoard_T board, int depth) {
MoveList_T list = MoveList_generate(NULL, board);
int size = 8;
unsigned long long total = 0;
perf_shared shared[size];
pthread_t threads[size];
sem_t mutex, finish;
int index = 0;
sem_init(&mutex, 0, 1);
sem_init(&finish, 0, 0);
for (int i = 0; i < size; i++) {
shared[i].board = board;
shared[i].list = list;
shared[i].depth = depth - 1;
shared[i].mutex = &mutex;
shared[i].index = &index;
shared[i].finish = &finish;
shared[i].total = &total;
pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
}
for (int i = 0; i < size; i++)
sem_wait(&finish);
MoveList_free(&list);
printf("Nodes processed: %llu\n", total);
}
/* MAIN */
void init_all() {

diff --git a/src/moves.c b/src/moves.c

@@ -0,0 +1,272 @@

#include <stdio.h>
#include <stdlib.h>
#include <cul/mem.h>
#include "moves.h"
int Move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; }
Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture,
Piece_T Promote, int dbl, int enpassant, int castle) {
return (Move){
.source = src,
.target = tgt,
.dbl = dbl,
.enpassant = enpassant,
.castle = castle,
.capture = Capture != NULL,
.promote = Promote != NULL,
.piece = Piece_index(Piece),
.piece_capture = Capture ? Piece_index(Capture) : 0,
.piece_promote = Promote ? Piece_index(Promote) : 0,
};
}
void Move_print(Move move) {
printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n",
square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)],
Piece_unicode(Move_piece(move)),
Move_capture(move) ? Piece_unicode(Move_piece_capture(move)) : "X ",
Move_promote(move) ? Piece_unicode(Move_piece_promote(move)) : "X ",
Move_double(move) ? 1 : 0, Move_enpassant(move) ? 1 : 0,
Move_castle(move) ? 1 : 0, Move_capture(move) ? 1 : 0,
Move_promote(move) ? 1 : 0);
}
MoveList_T MoveList_new(void) {
MoveList_T p;
NEW0(p);
return p;
}
void MoveList_free(MoveList_T *p) { FREE(*p); }
Move MoveList_move(MoveList_T self, int index) { return self->moves[index]; }
int MoveList_size(MoveList_T self) { return self->count; }
void MoveList_reset(MoveList_T self) { self->count = 0; }
void MoveList_add(MoveList_T self, Move move) {
self->moves[self->count++] = move;
}
void MoveList_print(MoveList_T self) {
printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n");
for (int i = 0; i < self->count; i++)
Move_print(self->moves[i]);
printf("Total: %d\n", self->count);
}
#define pawn_canPromote(color, source) \
((color == WHITE && source >= a7 && source <= h7) || \
(color == BLACK && source >= a2 && source <= h2))
#define pawn_onStart(color, source) \
((color == BLACK && source >= a7 && source <= h7) || \
(color == WHITE && source >= a2 && source <= h2))
#define pawn_promote(source, target, Piece, Capture) \
for (int i = 1; i < 5; i++) { \
move = Move_encode(source, target, Piece, Capture, \
Piece_get(i, color), 0, 0, 0); \
MoveList_add(moves, move); \
}
MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board) {
Move move;
Square src, tgt;
eColor color = CBoard_side(board);
if (!moves) moves = MoveList_new();
{ // pawn moves
Piece_T Piece = Piece_get(PAWN, color);
U64 bitboard = CBoard_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
{ // quiet
int add = (color == WHITE) ? +8 : -8;
tgt = src + add;
if (tgt > a1 && tgt < h8 &&
!CBoard_square_isOccupied(board, tgt)) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece, 0);
} else {
MoveList_add(
moves, Move_encode(src, tgt, Piece, 0, 0, 0, 0, 0));
// two ahead
if (pawn_onStart(color, src) &&
!CBoard_square_isOccupied(board, tgt += add))
MoveList_add(moves, Move_encode(src, tgt, Piece, 0,
0, 1, 0, 0));
}
}
}
{ // capture
U64 attack = CBoard_piece_attacks(board, Piece, src) &
CBoard_colorBB(board, !color);
bitboard_for_each_bit(tgt, attack) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece,
CBoard_square_piece(board, tgt, !color));
} else {
MoveList_add(moves, Move_encode(src, tgt, Piece,
CBoard_square_piece(
board, tgt, !color),
0, 0, 0, 0));
}
}
}
{ // en passant
if (CBoard_enpassant(board) != no_sq &&
CBoard_piece_attacks(board, Piece, src) &
(C64(1) << CBoard_enpassant(board)))
MoveList_add(
moves,
Move_encode(src, CBoard_enpassant(board), Piece,
CBoard_square_piece(board, tgt, !color), 0,
0, 1, 0));
}
}
}
// All piece move
for (int piece = 1; piece < 6; piece++) {
Piece_T Piece = Piece_get(piece, color);
U64 bitboard = CBoard_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
U64 attack = CBoard_piece_attacks(board, Piece, src) &
~CBoard_colorBB(board, color);
bitboard_for_each_bit(tgt, attack) {
/* int take = bit_get(CBoard_colorBB(board, !color), tgt); */
MoveList_add(
moves, Move_encode(src, tgt, Piece,
CBoard_square_piece(board, tgt, !color),
0, 0, 0, 0));
}
}
}
// Castling
{
if (color == WHITE) {
Piece_T Piece = Piece_get(KING, WHITE);
if (CBoard_castle(board) & WK) {
if (!CBoard_square_isOccupied(board, f1) &&
!CBoard_square_isOccupied(board, g1) &&
!CBoard_square_isAttack(board, e1, BLACK) &&
!CBoard_square_isAttack(board, f1, BLACK))
MoveList_add(moves,
Move_encode(e1, g1, Piece, 0, 0, 0, 0, 1));
}
if (CBoard_castle(board) & WQ) {
if (!CBoard_square_isOccupied(board, d1) &&
!CBoard_square_isOccupied(board, c1) &&
!CBoard_square_isOccupied(board, b1) &&
!CBoard_square_isAttack(board, e1, BLACK) &&
!CBoard_square_isAttack(board, d1, BLACK))
MoveList_add(moves,
Move_encode(e1, c1, Piece, 0, 0, 0, 0, 1));
}
} else {
Piece_T Piece = Piece_get(KING, BLACK);
if (CBoard_castle(board) & BK) {
if (!CBoard_square_isOccupied(board, f8) &&
!CBoard_square_isOccupied(board, g8) &&
!CBoard_square_isAttack(board, e8, WHITE) &&
!CBoard_square_isAttack(board, f8, WHITE))
MoveList_add(moves,
Move_encode(e8, g8, Piece, 0, 0, 0, 0, 1));
}
if (CBoard_castle(board) & BQ) {
if (!CBoard_square_isOccupied(board, d8) &&
!CBoard_square_isOccupied(board, c8) &&
!CBoard_square_isOccupied(board, b8) &&
!CBoard_square_isAttack(board, e8, WHITE) &&
!CBoard_square_isAttack(board, d8, WHITE))
MoveList_add(moves,
Move_encode(e8, c8, Piece, 0, 0, 0, 0, 1));
}
}
}
return moves;
}
// clang-format off
const int castling_rights[64] = {
13, 15, 15, 15, 12, 15, 15, 14,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
7, 15, 15, 15, 3, 15, 15, 11,
};
// clang-format on
int Move_make(Move move, CBoard_T board, int flag) {
if (flag == 0) {
Square source = Move_source(move);
Square target = Move_target(move);
Piece_T Piece = Move_piece(move);
eColor color = CBoard_side(board);
if (!Move_capture(move))
CBoard_piece_move(board, Piece, source, target);
else
CBoard_piece_capture(board, Piece, Move_piece_capture(move), source,
target);
if (Move_promote(move)) {
CBoard_piece_pop(board, Piece, target);
CBoard_piece_set(board, Move_piece_promote(move), target);
}
{
int ntarget = target + (color == WHITE ? -8 : +8);
if (Move_enpassant(move))
CBoard_piece_pop(board, Piece_get(PAWN, !color), ntarget);
CBoard_enpassant_set(board, Move_double(move) ? ntarget : no_sq);
}
if (Move_castle(move)) {
Piece_T Rook = Piece_get(ROOK, CBoard_side(board));
switch (target) {
case g1:
CBoard_piece_move(board, Rook, h1, f1);
break;
case c1:
CBoard_piece_move(board, Rook, a1, d1);
break;
case g8:
CBoard_piece_move(board, Rook, h8, f8);
break;
case c8:
CBoard_piece_move(board, Rook, a8, d8);
break;
default:
break;
}
}
CBoard_castle_and(board, castling_rights[source]);
CBoard_castle_and(board, castling_rights[target]);
if (!CBoard_isCheck(board)) {
CBoard_side_switch(board);
return 1;
} else
return 0;
} else {
if (Move_capture(move))
return Move_make(move, board, 0);
else
return 0;
}
}

diff --git a/src/perft.c b/src/perft.c

@@ -0,0 +1,127 @@

#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include "moves.h"
#include "perft.h"
struct MoveList_T moveList[10];
long nodes;
void perft_driver(CBoard_T board, struct MoveList_T *moveList, int depth,
unsigned long long *nodes) {
if (depth == 0) {
(*nodes)++;
return;
}
MoveList_T list = MoveList_generate(&moveList[depth], board);
CBoard_T copy = CBoard_new();
for (int i = 0; i < MoveList_size(list); i++) {
CBoard_copy(board, copy);
if (!Move_make(MoveList_move(list, i), copy, 0)) continue;
perft_driver(copy, moveList, depth - 1, nodes);
}
MoveList_reset(list);
CBoard_free(&copy);
}
void perft_test(CBoard_T board, int depth) {
MoveList_T list = MoveList_generate(&moveList[depth], board);
CBoard_T copy = CBoard_new();
long start = get_time_ms();
printf("\n Performance test\n\n");
nodes = 0;
for (int i = 0; i < MoveList_size(list); i++) {
CBoard_copy(board, copy);
Move move = MoveList_move(list, i);
if (!Move_make(MoveList_move(list, i), copy, 0)) continue;
unsigned long long node = 0;
perft_driver(copy, moveList, depth - 1, &node);
printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)], node);
nodes += node;
}
MoveList_reset(list);
CBoard_free(&copy);
printf("\nNodes searched: %ld\n\n", nodes);
printf("\n Depth: %d\n", depth);
printf(" Nodes: %ld\n", nodes);
printf(" Time: %ld\n\n", get_time_ms() - start);
}
typedef struct perf_shared perf_shared;
struct perf_shared {
CBoard_T board;
MoveList_T list;
int depth;
sem_t *mutex;
int *index;
sem_t *finish;
unsigned long long *total;
};
void *perft_thread(void *arg) {
perf_shared *shared = (perf_shared *)arg;
CBoard_T board = CBoard_new();
unsigned long long node_count = 0;
struct MoveList_T moveList[10];
while (1) {
sem_wait(shared->mutex);
*shared->total += node_count;
if (*shared->index >= MoveList_size(shared->list)) {
sem_post(shared->mutex);
break;
}
Move move = MoveList_move(shared->list, (*shared->index)++);
sem_post(shared->mutex);
CBoard_copy(shared->board, board);
if (!Move_make(move, board, 0)) continue;
node_count = 0;
perft_driver(board, moveList, shared->depth, &node_count);
printf("%s%s: %llu\n", square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)], node_count);
}
CBoard_free(&board);
sem_post(shared->finish);
return NULL;
}
void perft_test_threaded(CBoard_T board, int depth) {
MoveList_T list = MoveList_generate(NULL, board);
int size = 8;
unsigned long long total = 0;
perf_shared shared[size];
pthread_t threads[size];
sem_t mutex, finish;
int index = 0;
sem_init(&mutex, 0, 1);
sem_init(&finish, 0, 0);
for (int i = 0; i < size; i++) {
shared[i].board = board;
shared[i].list = list;
shared[i].depth = depth - 1;
shared[i].mutex = &mutex;
shared[i].index = &index;
shared[i].finish = &finish;
shared[i].total = &total;
pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
}
for (int i = 0; i < size; i++)
sem_wait(&finish);
MoveList_free(&list);
printf("Nodes processed: %llu\n", total);
}

diff --git a/src/score.c b/src/score.c

@@ -0,0 +1,117 @@

#include "score.h"
// clang-format off
struct Score_T {
int value;
int position[64];
int capture[6];
};
struct Score_T Scores[] = {
[PAWN] = {
.value = 100,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -10, -10, 0, 0, 0,
0, 0, 0, 5, 5, 0, 0, 0,
5, 5, 10, 20, 20, 5, 5, 5,
10, 10, 10, 20, 20, 10, 10, 10,
20, 20, 20, 30, 30, 30, 20, 20,
30, 30, 30, 40, 40, 30, 30, 30,
90, 90, 90, 90, 90, 90, 90, 90, },
.capture = { [PAWN] = 105, [KNIGHT] = 205,
[BISHOP] = 305, [ROOK] = 405,
[QUEEN] = 505, [KING] = 605} },
[KNIGHT] = {
.value = 300,
.position = {
-5, -10 , 0, 0, 0, 0, -10, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 5, 20, 10, 10, 20, 5, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 5, 20, 20, 20, 20, 5, -5,
-5, 0, 0, 10, 10, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5, },
.capture = { [PAWN] = 104, [KNIGHT] = 204,
[BISHOP] = 304, [ROOK] = 404,
[QUEEN] = 504, [KING] = 604} },
[BISHOP] = {
.value = 350,
.position = {
0, 0, -10, 0, 0, -10, 0, 0,
0, 30, 0, 0, 0, 0, 30, 0,
0, 10, 0, 0, 0, 0, 10, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 0, 10, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 103, [KNIGHT] = 203,
[BISHOP] = 303, [ROOK] = 403,
[QUEEN] = 503, [KING] = 603} },
[ROOK] = {
.value = 500,
.position = {
0, 0, 0, 20, 20, 0, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, },
.capture = { [PAWN] = 102, [KNIGHT] = 202,
[BISHOP] = 302, [ROOK] = 402,
[QUEEN] = 502, [KING] = 602} },
[QUEEN] = {
.value = 1000,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 101, [KNIGHT] = 201,
[BISHOP] = 301, [ROOK] = 401,
[QUEEN] = 501, [KING] = 601} },
[KING] = {
.value = 10000,
.position = {
0, 0, 5, 0, -15, 0, 10, 0,
0, 5, 5, -5, -5, 0, 5, 0,
0, 0, 5, 10, 10, 5, 0, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 5, 10, 10, 5, 5, 0,
0, 0, 5, 5, 5, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 100, [KNIGHT] = 200,
[BISHOP] = 300, [ROOK] = 400,
[QUEEN] = 500, [KING] = 600} },
};
const int mirror_score[128] =
{
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1, no_sq,
};
// clang-format on
int Score_value(ePiece piece) { return Scores[piece].value; }
int Score_position(ePiece piece, eColor color, Square square) {
if (color == BLACK) square = mirror_score[square];
return Scores[piece].position[square];
}
int Score_capture(ePiece src, ePiece tgt) { return Scores[src].capture[tgt]; }