commit 3d9fb77e4f91af7bf2fc59cfb14c012007cb9ae0
parent 11aa12bf92ffa2c1f685a8ec4f9dfe4135223e7a
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Fri, 28 Jul 2023 23:21:40 +0200
Split engine into multiple files for easy refactor
Diffstat:
A | include/moves.h | | | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | include/perft.h | | | 9 | +++++++++ |
A | include/score.h | | | 10 | ++++++++++ |
M | src/engine.c | | | 544 | +------------------------------------------------------------------------------ |
A | src/moves.c | | | 272 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/perft.c | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/score.c | | | 117 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 592 insertions(+), 541 deletions(-)
diff --git a/include/moves.h b/include/moves.h
@@ -0,0 +1,54 @@
+#ifndef MOVES_H
+#define MOVES_H
+
+#include <stdint.h>
+
+#include "CBoard.h"
+
+typedef struct Move Move;
+struct Move {
+ unsigned source : 6;
+ unsigned target : 6;
+ unsigned piece : 5;
+ unsigned piece_capture : 5;
+ unsigned piece_promote : 5;
+ unsigned dbl : 1;
+ unsigned enpassant : 1;
+ unsigned castle : 1;
+ unsigned capture : 1;
+ unsigned promote : 1;
+};
+
+typedef struct MoveList_T *MoveList_T;
+struct MoveList_T {
+ Move moves[256];
+ int count;
+};
+
+int Move_cmp(Move a, Move b);
+Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture,
+ Piece_T Promote, int dbl, int enpassant, int castle);
+void Move_print(Move move);
+MoveList_T MoveList_new(void);
+void MoveList_free(MoveList_T *p);
+Move MoveList_move(MoveList_T self, int index);
+int MoveList_size(MoveList_T self);
+void MoveList_reset(MoveList_T self);
+void MoveList_add(MoveList_T self, Move move);
+void MoveList_print(MoveList_T self);
+MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board);
+int Move_make(Move move, CBoard_T board, int flag);
+
+#define Move_source(move) (move.source)
+#define Move_target(move) (move.target)
+#define Move_double(move) (move.dbl)
+#define Move_enpassant(move) (move.enpassant)
+#define Move_castle(move) (move.castle)
+#define Move_capture(move) (move.capture)
+#define Move_promote(move) (move.promote)
+
+#define Move_piece(move) (Piece_fromIndex(move.piece))
+#define Move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
+#define Move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
+
+#endif
diff --git a/include/perft.h b/include/perft.h
@@ -0,0 +1,9 @@
+#ifndef PERFT_H
+#define PERFT_H
+
+#include "CBoard.h"
+
+void perft_test_threaded(CBoard_T board, int depth);
+void perft_test(CBoard_T board, int depth);
+
+#endif
diff --git a/include/score.h b/include/score.h
@@ -0,0 +1,10 @@
+#ifndef SCORE_H
+#define SCORE_H
+
+#include "CBoard.h"
+
+int Score_capture(ePiece src, ePiece tgt);
+int Score_position(ePiece piece, eColor color, Square square);
+int Score_value(ePiece piece);
+
+#endif
diff --git a/src/engine.c b/src/engine.c
@@ -1,11 +1,13 @@
#include <ctype.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "CBoard.h"
#include "attack.h"
+#include "moves.h"
+#include "perft.h"
+#include "score.h"
#include "utils.h"
#include <cul/assert.h>
@@ -24,177 +26,6 @@
#define cmk_position \
"r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
-typedef struct Move Move;
-struct Move {
- unsigned source : 6;
- unsigned target : 6;
- unsigned piece : 5;
- unsigned piece_capture : 5;
- unsigned piece_promote : 5;
- unsigned dbl : 1;
- unsigned enpassant : 1;
- unsigned castle : 1;
- unsigned capture : 1;
- unsigned promote : 1;
-};
-
-int Move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; }
-
-Move Move_encode(Square src, Square tgt, Piece_T Piece, Piece_T Capture,
- Piece_T Promote, int dbl, int enpassant, int castle) {
- return (Move){
- .source = src,
- .target = tgt,
- .dbl = dbl,
- .enpassant = enpassant,
- .castle = castle,
- .capture = Capture != NULL,
- .promote = Promote != NULL,
- .piece = Piece_index(Piece),
- .piece_capture = Capture ? Piece_index(Capture) : 0,
- .piece_promote = Promote ? Piece_index(Promote) : 0,
- };
-}
-
-#define Move_source(move) (move.source)
-#define Move_target(move) (move.target)
-#define Move_double(move) (move.dbl)
-#define Move_enpassant(move) (move.enpassant)
-#define Move_castle(move) (move.castle)
-#define Move_capture(move) (move.capture)
-#define Move_promote(move) (move.promote)
-
-#define Move_piece(move) (Piece_fromIndex(move.piece))
-#define Move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
-#define Move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
-
-// clang-format off
-const int castling_rights[64] = {
- 13, 15, 15, 15, 12, 15, 15, 14,
- 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15,
- 15, 15, 15, 15, 15, 15, 15, 15,
- 7, 15, 15, 15, 3, 15, 15, 11,
-};
-
-struct Score_T {
- int value;
- int position[64];
- int capture[6];
-};
-
-struct Score_T Scores[] = {
-[PAWN] = {
-.value = 100,
-.position = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, -10, -10, 0, 0, 0,
- 0, 0, 0, 5, 5, 0, 0, 0,
- 5, 5, 10, 20, 20, 5, 5, 5,
- 10, 10, 10, 20, 20, 10, 10, 10,
- 20, 20, 20, 30, 30, 30, 20, 20,
- 30, 30, 30, 40, 40, 30, 30, 30,
- 90, 90, 90, 90, 90, 90, 90, 90, },
-.capture = { [PAWN] = 105, [KNIGHT] = 205,
- [BISHOP] = 305, [ROOK] = 405,
- [QUEEN] = 505, [KING] = 605} },
-[KNIGHT] = {
-.value = 300,
-.position = {
- -5, -10 , 0, 0, 0, 0, -10, -5,
- -5, 0, 0, 0, 0, 0, 0, -5,
- -5, 5, 20, 10, 10, 20, 5, -5,
- -5, 10, 20, 30, 30, 20, 10, -5,
- -5, 10, 20, 30, 30, 20, 10, -5,
- -5, 5, 20, 20, 20, 20, 5, -5,
- -5, 0, 0, 10, 10, 0, 0, -5,
- -5, 0, 0, 0, 0, 0, 0, -5, },
-.capture = { [PAWN] = 104, [KNIGHT] = 204,
- [BISHOP] = 304, [ROOK] = 404,
- [QUEEN] = 504, [KING] = 604} },
-[BISHOP] = {
-.value = 350,
-.position = {
- 0, 0, -10, 0, 0, -10, 0, 0,
- 0, 30, 0, 0, 0, 0, 30, 0,
- 0, 10, 0, 0, 0, 0, 10, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 0, 0, 0, 10, 10, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, },
-.capture = { [PAWN] = 103, [KNIGHT] = 203,
- [BISHOP] = 303, [ROOK] = 403,
- [QUEEN] = 503, [KING] = 603} },
-[ROOK] = {
-.value = 500,
-.position = {
- 0, 0, 0, 20, 20, 0, 0, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 0, 0, 10, 20, 20, 10, 0, 0,
- 50, 50, 50, 50, 50, 50, 50, 50,
- 50, 50, 50, 50, 50, 50, 50, 50, },
-.capture = { [PAWN] = 102, [KNIGHT] = 202,
- [BISHOP] = 302, [ROOK] = 402,
- [QUEEN] = 502, [KING] = 602} },
-[QUEEN] = {
-.value = 1000,
-.position = {
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, },
-.capture = { [PAWN] = 101, [KNIGHT] = 201,
- [BISHOP] = 301, [ROOK] = 401,
- [QUEEN] = 501, [KING] = 601} },
-[KING] = {
-.value = 10000,
-.position = {
- 0, 0, 5, 0, -15, 0, 10, 0,
- 0, 5, 5, -5, -5, 0, 5, 0,
- 0, 0, 5, 10, 10, 5, 0, 0,
- 0, 5, 10, 20, 20, 10, 5, 0,
- 0, 5, 10, 20, 20, 10, 5, 0,
- 0, 5, 5, 10, 10, 5, 5, 0,
- 0, 0, 5, 5, 5, 5, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, },
-.capture = { [PAWN] = 100, [KNIGHT] = 200,
- [BISHOP] = 300, [ROOK] = 400,
- [QUEEN] = 500, [KING] = 600} },
-};
-
-const int mirror_score[128] =
-{
- a8, b8, c8, d8, e8, f8, g8, h8,
- a7, b7, c7, d7, e7, f7, g7, h7,
- a6, b6, c6, d6, e6, f6, g6, h6,
- a5, b5, c5, d5, e5, f5, g5, h5,
- a4, b4, c4, d4, e4, f4, g4, h4,
- a3, b3, c3, d3, e3, f3, g3, h3,
- a2, b2, c2, d2, e2, f2, g2, h2,
- a1, b1, c1, d1, e1, f1, g1, h1, no_sq,
-};
-// clang-format on
-
-int Score_value(ePiece piece) { return Scores[piece].value; }
-
-int Score_position(ePiece piece, eColor color, Square square) {
- if (color == BLACK) square = mirror_score[square];
- return Scores[piece].position[square];
-}
-
-int Score_capture(ePiece src, ePiece tgt) { return Scores[src].capture[tgt]; }
-
#define MAX_PLY 64
typedef struct Stats_T *Stats_T;
@@ -232,47 +63,6 @@ int Move_score(Stats_T stats, Move move) {
return 0;
}
-void Move_print(Move move) {
- printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n",
- square_to_coordinates[Move_source(move)],
- square_to_coordinates[Move_target(move)],
- Piece_unicode(Move_piece(move)),
- Move_capture(move) ? Piece_unicode(Move_piece_capture(move)) : "X ",
- Move_promote(move) ? Piece_unicode(Move_piece_promote(move)) : "X ",
- Move_double(move) ? 1 : 0, Move_enpassant(move) ? 1 : 0,
- Move_castle(move) ? 1 : 0, Move_capture(move) ? 1 : 0,
- Move_promote(move) ? 1 : 0);
-}
-
-typedef struct MoveList_T *MoveList_T;
-struct MoveList_T {
- Move moves[256];
- int count;
-};
-
-MoveList_T MoveList_new(void) {
- MoveList_T p;
- NEW0(p);
- return p;
-}
-
-void MoveList_free(MoveList_T *p) { FREE(*p); }
-
-Move MoveList_move(MoveList_T self, int index) { return self->moves[index]; }
-int MoveList_size(MoveList_T self) { return self->count; }
-void MoveList_reset(MoveList_T self) { self->count = 0; }
-
-void MoveList_add(MoveList_T self, Move move) {
- self->moves[self->count++] = move;
-}
-
-void MoveList_print(MoveList_T self) {
- printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n");
- for (int i = 0; i < self->count; i++)
- Move_print(self->moves[i]);
- printf("Total: %d\n", self->count);
-}
-
void MoveList_sort(Stats_T stats, MoveList_T list) {
int score[list->count];
for (int i = 0; i < list->count; i++)
@@ -291,206 +81,6 @@ void MoveList_sort(Stats_T stats, MoveList_T list) {
}
}
-#define pawn_canPromote(color, source) \
- ((color == WHITE && source >= a7 && source <= h7) || \
- (color == BLACK && source >= a2 && source <= h2))
-
-#define pawn_onStart(color, source) \
- ((color == BLACK && source >= a7 && source <= h7) || \
- (color == WHITE && source >= a2 && source <= h2))
-
-#define pawn_promote(source, target, Piece, Capture) \
- for (int i = 1; i < 5; i++) { \
- move = Move_encode(source, target, Piece, Capture, \
- Piece_get(i, color), 0, 0, 0); \
- MoveList_add(moves, move); \
- }
-
-MoveList_T MoveList_generate(MoveList_T moves, CBoard_T board) {
- Move move;
- Square src, tgt;
- eColor color = CBoard_side(board);
-
- if (!moves) moves = MoveList_new();
-
- { // pawn moves
- Piece_T Piece = Piece_get(PAWN, color);
- U64 bitboard = CBoard_pieceSet(board, Piece);
- bitboard_for_each_bit(src, bitboard) {
- { // quiet
- int add = (color == WHITE) ? +8 : -8;
- tgt = src + add;
- if (tgt > a1 && tgt < h8 &&
- !CBoard_square_isOccupied(board, tgt)) {
- if (pawn_canPromote(color, src)) {
- pawn_promote(src, tgt, Piece, 0);
- } else {
- MoveList_add(
- moves, Move_encode(src, tgt, Piece, 0, 0, 0, 0, 0));
-
- // two ahead
- if (pawn_onStart(color, src) &&
- !CBoard_square_isOccupied(board, tgt += add))
- MoveList_add(moves, Move_encode(src, tgt, Piece, 0,
- 0, 1, 0, 0));
- }
- }
- }
- { // capture
- U64 attack = CBoard_piece_attacks(board, Piece, src) &
- CBoard_colorBB(board, !color);
- bitboard_for_each_bit(tgt, attack) {
- if (pawn_canPromote(color, src)) {
- pawn_promote(src, tgt, Piece,
- CBoard_square_piece(board, tgt, !color));
- } else {
- MoveList_add(moves, Move_encode(src, tgt, Piece,
- CBoard_square_piece(
- board, tgt, !color),
- 0, 0, 0, 0));
- }
- }
- }
-
- { // en passant
- if (CBoard_enpassant(board) != no_sq &&
- CBoard_piece_attacks(board, Piece, src) &
- (C64(1) << CBoard_enpassant(board)))
- MoveList_add(
- moves,
- Move_encode(src, CBoard_enpassant(board), Piece,
- CBoard_square_piece(board, tgt, !color), 0,
- 0, 1, 0));
- }
- }
- }
-
- // All piece move
- for (int piece = 1; piece < 6; piece++) {
- Piece_T Piece = Piece_get(piece, color);
- U64 bitboard = CBoard_pieceSet(board, Piece);
- bitboard_for_each_bit(src, bitboard) {
- U64 attack = CBoard_piece_attacks(board, Piece, src) &
- ~CBoard_colorBB(board, color);
- bitboard_for_each_bit(tgt, attack) {
- /* int take = bit_get(CBoard_colorBB(board, !color), tgt); */
- MoveList_add(
- moves, Move_encode(src, tgt, Piece,
- CBoard_square_piece(board, tgt, !color),
- 0, 0, 0, 0));
- }
- }
- }
-
- // Castling
- {
- if (color == WHITE) {
- Piece_T Piece = Piece_get(KING, WHITE);
- if (CBoard_castle(board) & WK) {
- if (!CBoard_square_isOccupied(board, f1) &&
- !CBoard_square_isOccupied(board, g1) &&
- !CBoard_square_isAttack(board, e1, BLACK) &&
- !CBoard_square_isAttack(board, f1, BLACK))
- MoveList_add(moves,
- Move_encode(e1, g1, Piece, 0, 0, 0, 0, 1));
- }
- if (CBoard_castle(board) & WQ) {
- if (!CBoard_square_isOccupied(board, d1) &&
- !CBoard_square_isOccupied(board, c1) &&
- !CBoard_square_isOccupied(board, b1) &&
- !CBoard_square_isAttack(board, e1, BLACK) &&
- !CBoard_square_isAttack(board, d1, BLACK))
- MoveList_add(moves,
- Move_encode(e1, c1, Piece, 0, 0, 0, 0, 1));
- }
- } else {
- Piece_T Piece = Piece_get(KING, BLACK);
- if (CBoard_castle(board) & BK) {
- if (!CBoard_square_isOccupied(board, f8) &&
- !CBoard_square_isOccupied(board, g8) &&
- !CBoard_square_isAttack(board, e8, WHITE) &&
- !CBoard_square_isAttack(board, f8, WHITE))
- MoveList_add(moves,
- Move_encode(e8, g8, Piece, 0, 0, 0, 0, 1));
- }
- if (CBoard_castle(board) & BQ) {
- if (!CBoard_square_isOccupied(board, d8) &&
- !CBoard_square_isOccupied(board, c8) &&
- !CBoard_square_isOccupied(board, b8) &&
- !CBoard_square_isAttack(board, e8, WHITE) &&
- !CBoard_square_isAttack(board, d8, WHITE))
- MoveList_add(moves,
- Move_encode(e8, c8, Piece, 0, 0, 0, 0, 1));
- }
- }
- }
-
- return moves;
-}
-
-int Move_make(Move move, CBoard_T board, int flag) {
- if (flag == 0) {
-
- Square source = Move_source(move);
- Square target = Move_target(move);
- Piece_T Piece = Move_piece(move);
- eColor color = CBoard_side(board);
-
- if (!Move_capture(move))
- CBoard_piece_move(board, Piece, source, target);
- else
- CBoard_piece_capture(board, Piece, Move_piece_capture(move), source,
- target);
-
- if (Move_promote(move)) {
- CBoard_piece_pop(board, Piece, target);
- CBoard_piece_set(board, Move_piece_promote(move), target);
- }
-
- {
- int ntarget = target + (color == WHITE ? -8 : +8);
- if (Move_enpassant(move))
- CBoard_piece_pop(board, Piece_get(PAWN, !color), ntarget);
-
- CBoard_enpassant_set(board, Move_double(move) ? ntarget : no_sq);
- }
-
- if (Move_castle(move)) {
- Piece_T Rook = Piece_get(ROOK, CBoard_side(board));
- switch (target) {
- case g1:
- CBoard_piece_move(board, Rook, h1, f1);
- break;
- case c1:
- CBoard_piece_move(board, Rook, a1, d1);
- break;
- case g8:
- CBoard_piece_move(board, Rook, h8, f8);
- break;
- case c8:
- CBoard_piece_move(board, Rook, a8, d8);
- break;
- default:
- break;
- }
- }
-
- CBoard_castle_and(board, castling_rights[source]);
- CBoard_castle_and(board, castling_rights[target]);
-
- if (!CBoard_isCheck(board)) {
- CBoard_side_switch(board);
- return 1;
- } else
- return 0;
- } else {
- if (Move_capture(move))
- return Move_make(move, board, 0);
- else
- return 0;
- }
-}
-
/* SEARCHING */
int evaluate(CBoard_T board) {
@@ -629,8 +219,6 @@ int negamax(Stats_T stats, CBoard_T board, int alpha, int beta, int depth) {
return alpha;
}
-/* UCI */
-
void Move_print_UCI(Move move) {
printf("%s%s", square_to_coordinates[Move_source(move)],
square_to_coordinates[Move_target(move)]);
@@ -840,132 +428,6 @@ void uci_loop(void) {
CBoard_free(&board);
}
-/* PERFT */
-
-struct MoveList_T moveList[10];
-long nodes;
-
-void perft_driver(CBoard_T board, struct MoveList_T *moveList, int depth,
- unsigned long long *nodes) {
- if (depth == 0) {
- (*nodes)++;
- return;
- }
-
- MoveList_T list = MoveList_generate(&moveList[depth], board);
- CBoard_T copy = CBoard_new();
-
- for (int i = 0; i < MoveList_size(list); i++) {
- CBoard_copy(board, copy);
- if (!Move_make(MoveList_move(list, i), copy, 0)) continue;
- perft_driver(copy, moveList, depth - 1, nodes);
- }
-
- MoveList_reset(list);
- CBoard_free(©);
-}
-
-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(©);
-
- 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(©);
+}
+
+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(©);
+
+ 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]; }