stellarUCI Chess engine written in C++20 |
git clone git://git.dimitrijedobrota.com/stellar.git |
Log | Files | Refs | README | LICENSE | |
commit | a6e157ec1fd48ba4c9bc6103836a136039c15def |
parent | 564362bd70ae4b35fc956fd5c97e8dd672c4341a |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Thu, 29 Sep 2022 22:43:02 +0200 |
Basic Evaluation Scheme and improved UCI instructions
Diffstat:M | include/CBoard.h | | | +++++----- |
M | src/CBoard.c | | | +++++++++++++++++++------------------- |
M | src/engine.c | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------- |
3 files changed, 270 insertions(+), 80 deletions(-)
diff --git a/include/CBoard.h b/include/CBoard.h
@@ -8,11 +8,11 @@ typedef enum enumCastle eCastle;
typedef struct Piece_T *Piece_T;
char Piece_asci(Piece_T pt);
char Piece_code(Piece_T pt);
char *Piece_unicode(Piece_T pt);
eColor Piece_color(Piece_T pt);
ePiece Piece_piece(Piece_T pt);
char Piece_asci(Piece_T self);
char Piece_code(Piece_T self);
char *Piece_unicode(Piece_T self);
eColor Piece_color(Piece_T self);
ePiece Piece_piece(Piece_T self);
int Piece_index(Piece_T self);
Piece_T Piece_fromCode(char code);
diff --git a/src/CBoard.c b/src/CBoard.c
@@ -24,30 +24,30 @@ struct Piece_T {
// clang-format off
struct Piece_T Pieces[2][6] = {
{
{.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks},
{.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks},
{.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks},
{.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks},
{.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks},
{.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks},
[PAWN] = {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks},
[KNIGHT] = {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks},
},
{
{.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks},
{.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks},
{.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks},
{.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks},
{.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks},
{.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks},
[PAWN] = {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks},
[KNIGHT] = {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks},
},
};
// clang-format on
attack_f Piece_attacks(Piece_T pt) { return pt->attacks; }
char Piece_asci(Piece_T pt) { return pt->asci; }
char Piece_code(Piece_T pt) { return pt->code; }
char *Piece_unicode(Piece_T pt) { return pt->unicode; }
eColor Piece_color(Piece_T pt) { return pt->color; }
ePiece Piece_piece(Piece_T pt) { return pt->piece; }
attack_f Piece_attacks(Piece_T self) { return self->attacks; }
char Piece_asci(Piece_T self) { return self->asci; }
char Piece_code(Piece_T self) { return self->code; }
char *Piece_unicode(Piece_T self) { return self->unicode; }
eColor Piece_color(Piece_T self) { return self->color; }
ePiece Piece_piece(Piece_T self) { return self->piece; }
int Piece_index(Piece_T self) { return self->color * 8 + self->piece; }
Piece_T Piece_fromCode(char code) {
@@ -215,7 +215,7 @@ CBoard_T CBoard_fromFEN(CBoard_T board, char *fen) {
if (!board)
NEW(board);
memset(board, C64(0), sizeof(*board));
memset(board, 0, sizeof(*board));
board->side = -1;
board->enpassant = no_sq;
diff --git a/src/engine.c b/src/engine.c
@@ -162,35 +162,35 @@ MoveList_T generate_moves(CBoard_T cboard, MoveList_T moves) {
int index = Piece_index(Piece_get(KING, WHITE));
if (CBoard_castle(cboard) & WK) {
if (!CBoard_square_isOccupied(cboard, f1) &&
!CBoard_square_isOccupied(cboard, g1))
if (!CBoard_square_isAttack(cboard, e1, BLACK) &&
!CBoard_square_isAttack(cboard, f1, BLACK))
MoveList_add(moves, Move_encode(e1, g1, index, 0, 0, 0, 0, 1));
!CBoard_square_isOccupied(cboard, g1) &&
!CBoard_square_isAttack(cboard, e1, BLACK) &&
!CBoard_square_isAttack(cboard, f1, BLACK))
MoveList_add(moves, Move_encode(e1, g1, index, 0, 0, 0, 0, 1));
}
if (CBoard_castle(cboard) & WQ) {
if (!CBoard_square_isOccupied(cboard, d1) &&
!CBoard_square_isOccupied(cboard, c1) &&
!CBoard_square_isOccupied(cboard, b1))
if (!CBoard_square_isAttack(cboard, e1, BLACK) &&
!CBoard_square_isAttack(cboard, d1, BLACK))
MoveList_add(moves, Move_encode(e1, c1, index, 0, 0, 0, 0, 1));
!CBoard_square_isOccupied(cboard, b1) &&
!CBoard_square_isAttack(cboard, e1, BLACK) &&
!CBoard_square_isAttack(cboard, d1, BLACK))
MoveList_add(moves, Move_encode(e1, c1, index, 0, 0, 0, 0, 1));
}
} else {
int index = Piece_index(Piece_get(KING, BLACK));
if (CBoard_castle(cboard) & BK) {
if (!CBoard_square_isOccupied(cboard, f8) &&
!CBoard_square_isOccupied(cboard, g8))
if (!CBoard_square_isAttack(cboard, e8, WHITE) &&
!CBoard_square_isAttack(cboard, f8, WHITE))
MoveList_add(moves, Move_encode(e8, g8, index, 0, 0, 0, 0, 1));
!CBoard_square_isOccupied(cboard, g8) &&
!CBoard_square_isAttack(cboard, e8, WHITE) &&
!CBoard_square_isAttack(cboard, f8, WHITE))
MoveList_add(moves, Move_encode(e8, g8, index, 0, 0, 0, 0, 1));
}
if (CBoard_castle(cboard) & BQ) {
if (!CBoard_square_isOccupied(cboard, d8) &&
!CBoard_square_isOccupied(cboard, c8) &&
!CBoard_square_isOccupied(cboard, b8))
if (!CBoard_square_isAttack(cboard, e8, WHITE) &&
!CBoard_square_isAttack(cboard, d8, WHITE))
MoveList_add(moves, Move_encode(e8, c8, index, 0, 0, 0, 0, 1));
!CBoard_square_isOccupied(cboard, b8) &&
!CBoard_square_isAttack(cboard, e8, WHITE) &&
!CBoard_square_isAttack(cboard, d8, WHITE))
MoveList_add(moves, Move_encode(e8, c8, index, 0, 0, 0, 0, 1));
}
}
}
@@ -318,34 +318,170 @@ void init_all() {
/* UCI */
typedef struct Position_T *Position_T;
struct Position_T {
struct Score_T {
int value;
int score[64];
};
// clang-format off
struct Score_T Scores[] = {
[PAWN] = {
.value = 100,
.score = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -10, -10, 0, 0, 0,
0, 0, 0, 5, 5, 0, 0, 0,
5, 5, 10, 20, 20, 5, 5, 5,
10, 10, 10, 20, 20, 10, 10, 10,
20, 20, 20, 30, 30, 30, 20, 20,
30, 30, 30, 40, 40, 30, 30, 30,
90, 90, 90, 90, 90, 90, 90, 90,
}},
[KNIGHT] = {
.value = 300,
.score = {
-5, -10 , 0, 0, 0, 0, -10, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 5, 20, 10, 10, 20, 5, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 5, 20, 20, 20, 20, 5, -5,
-5, 0, 0, 10, 10, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
}},
[BISHOP] = {
.value = 350,
.score = {
0, 0, -10, 0, 0, -10, 0, 0,
0, 30, 0, 0, 0, 0, 30, 0,
0, 10, 0, 0, 0, 0, 10, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 0, 10, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
}},
[ROOK] = {
.value = 500,
.score = {
0, 0, 0, 20, 20, 0, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50,
}},
[QUEEN] = {
.value = 1000,
.score = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
}},
[KING] = {
.value = 10000,
.score = {
0, 0, 5, 0, -15, 0, 10, 0,
0, 5, 5, -5, -5, 0, 5, 0,
0, 0, 5, 10, 10, 5, 0, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 5, 10, 10, 5, 5, 0,
0, 0, 5, 5, 5, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
}},
};
const int mirror_score[128] =
{
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1, no_sq,
};
// clang-format on
int Score_value(ePiece piece) { return Scores[piece].value; }
int Score_score(ePiece piece, eColor color, Square square) {
if (color == BLACK)
square = mirror_score[square];
return Scores[piece].score[square];
}
int evaluate(CBoard_T board) {
Square square;
eColor side = CBoard_side(board);
U64 occupancy = CBoard_colorBB(board, side);
int score = 0;
for (int i = 0; i < 6; i++) {
U64 bitboard = CBoard_pieceBB(board, i);
bitboard_for_each_bit(square, bitboard) {
if (bit_get(occupancy, square)) {
score += Score_value(i);
score += Score_score(i, side, square);
} else {
score -= Score_value(i);
score -= Score_score(i, !side, square);
}
}
}
return side == WHITE ? score : -score;
}
void search_position(CBoard_T cboard, int depth) {
(void)cboard;
(void)depth;
printf("bestmove d7d5\n");
}
void print_info(void) {
printf("id name chessTrainer\n");
printf("id author Dimitrije Dobrota\n");
printf("uciok\n");
}
typedef struct Instruction_T *Instruction_T;
struct Instruction_T {
char *command;
char *token;
char *crnt;
};
char *Position_token_next(Position_T self);
char *Instruction_token_next(Instruction_T self);
Position_T Position_new(char *command) {
Position_T p;
Instruction_T Instruction_new(char *command) {
Instruction_T p;
NEW0(p);
p->command = ALLOC(strlen(command) + 1);
p->token = ALLOC(strlen(command) + 1);
strcpy(p->command, command);
p->crnt = command;
Position_token_next(p);
Instruction_token_next(p);
return p;
}
void Position_free(Position_T *p) {
void Instruction_free(Instruction_T *p) {
FREE((*p)->command);
FREE((*p)->token);
FREE(*p);
}
char *Position_token(Position_T self) { return self->token; }
char *Position_token_n(Position_T self, int n) {
char *Instruction_token(Instruction_T self) { return self->token; }
char *Instruction_token_n(Instruction_T self, int n) {
while (isspace(*self->crnt) && *self->crnt != '\0')
self->crnt++;
@@ -356,7 +492,7 @@ char *Position_token_n(Position_T self, int n) {
char *p = self->token;
while (n--) {
while (!isspace(*self->crnt) && *self->crnt != '\0')
while (!isspace(*self->crnt) && *self->crnt != '\0' && *self->crnt != ';')
*p++ = *self->crnt++;
if (*self->crnt == '\0') {
p++;
@@ -370,7 +506,9 @@ char *Position_token_n(Position_T self, int n) {
return self->token;
}
char *Position_token_next(Position_T self) { return Position_token_n(self, 1); }
char *Instruction_token_next(Instruction_T self) {
return Instruction_token_n(self, 1);
}
Move parse_move(CBoard_T self, char *move_string) {
Move result = 0;
@@ -398,62 +536,114 @@ Move parse_move(CBoard_T self, char *move_string) {
return result;
}
CBoard_T Position_parse(Position_T self, CBoard_T board) {
printf("Commands: %s\n", self->command);
int count = 0;
char *token = Position_token(self);
CBoard_T Instruction_parse(Instruction_T self, CBoard_T board) {
char *token = Instruction_token(self);
if (!board)
board = CBoard_new();
do {
printf("Token %d: %s\n", ++count, token);
if (strcmp(token, "ucinewgame") == 0) {
board = CBoard_fromFEN(board, start_position);
continue;
}
if (strcmp(token, "quit") == 0)
return NULL;
if (strcmp(token, "position") == 0) {
printf("FOUND position\n");
token = Position_token_next(self);
if (strcmp(token, "startpos") == 0) {
printf("FOUND startpos\n");
token = Instruction_token_next(self);
if (token && strcmp(token, "startpos") == 0) {
board = CBoard_fromFEN(board, start_position);
} else if (strcmp(token, "fen") == 0) {
token = Position_token_n(self, 6);
printf("fen: %s\n", token);
} else if (token && strcmp(token, "fen") == 0) {
token = Instruction_token_n(self, 6);
board = CBoard_fromFEN(board, token);
continue;
} else {
printf("Unknown argument after position\n");
assert(0);
}
CBoard_print(board);
continue;
}
if (strcmp(token, "moves") == 0) {
printf("FOUND moves\n");
CBoard_print(board);
while ((token = Position_token_next(self))) {
while ((token = Instruction_token_next(self))) {
Move move = parse_move(board, token);
if (move) {
make_move(board, move, 0);
CBoard_print(board);
} else {
printf("Invalid move %s!\n", token);
assert(0);
}
}
CBoard_print(board);
return board;
}
if (strcmp(token, "go") == 0) {
token = Instruction_token_next(self);
if (token && strcmp(token, "depth") == 0) {
token = Instruction_token_next(self);
search_position(board, atoi(token));
} else {
search_position(board, 6);
printf("Unknown argument after go\n");
}
continue;
}
} while ((token = Position_token_next(self)));
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;
}
int main(void) {
init_all();
void uci_loop(void) {
CBoard_T board = NULL;
Instruction_T instruction;
char input[2000];
setbuf(stdin, NULL);
setbuf(stdout, NULL);
CBoard_T board = NULL;
Position_T position;
print_info();
while (1) {
memset(input, 0, sizeof(input));
fflush(stdout);
if (!fgets(input, sizeof(input), stdin))
continue;
position =
Position_new(" position \t fen " start_position " moves e2e4 e7e5");
board = Position_parse(position, board);
Position_free(&position);
instruction = Instruction_new(input);
if (!(board = Instruction_parse(instruction, board)))
break;
Instruction_free(&instruction);
}
Instruction_free(&instruction);
CBoard_free(&board);
}
int main(void) {
init_all();
int debug = 1;
if (debug) {
printf("debugging!\n");
CBoard_T board = NULL;
board = CBoard_fromFEN(board, start_position);
make_move(board, parse_move(board, "e2e4"), 0);
make_move(board, parse_move(board, "b7b6"), 0);
make_move(board, parse_move(board, "d2d4"), 0);
make_move(board, parse_move(board, "c8b7"), 0);
CBoard_print(board);
printf("Evaluation: %d\n", evaluate(board));
} else
uci_loop();
return 0;
}