commit d02e946f5b524acd6871c196d4ef505cfa0b5e0a
parent 12abec85ea91fce6605ed4f197efa5aa75d9ca88
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date: Wed, 9 Aug 2023 23:36:20 +0200
Rest of the rewrite, - debugging...
Diffstat:
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 @@
#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;
-}