stellarUCI Chess engine written in C++20 |
git clone git://git.dimitrijedobrota.com/stellar.git |
Log | Files | Refs | README | LICENSE | |
commit | e611a16b9622e0b6831048b51a87cf51a2a01792 |
parent | 3be4123c8c5cbdadfab02532d2bfcb9bdb173083 |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Sun, 13 Aug 2023 09:24:59 +0200 |
Improve Move and MoveList
Diffstat:M | .clang-format | | | +- |
M | src/board/board.cpp | | | ++++++++++++++++++++------------------------ |
M | src/engine/engine.cpp | | | ++++++++++++++++++++++++++++++++++++++-------------------------------------------- |
M | src/engine/score.cpp | | | ++++++++++++------------------- |
M | src/engine/score.hpp | | | ++++--- |
M | src/engine/stats.hpp | | | +- |
M | src/engine/transposition.hpp | | | +++------ |
M | src/include/attack.hpp | | | +++++++---------------- |
M | src/include/board.hpp | | | ++++-- |
A | src/include/move.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/include/movelist.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/include/moves.hpp | | | ------------------------------------------------- |
M | src/include/piece.hpp | | | +++++++++-------------------- |
M | src/include/random.hpp | | | ++++---------- |
M | src/include/utils_cpp.hpp | | | ++++++++++++++++---------------------------------------------- |
M | src/include/zobrist.hpp | | | +++--------- |
A | src/move/CMakeLists.txt | | | ++++++++ |
A | src/move/move.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/move/movelist.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/moves/CMakeLists.txt | | | --------- |
D | src/moves/moves.cpp | | | --------------------------------------------------- |
D | src/moves/moves_generate.cpp | | | --------------------------------------------------------------------------------- |
D | src/moves/moves_make.cpp | | | --------------------------------------------------------------------------------- |
M | src/perft/perft.cpp | | | ++++++++++++++++++------------------ |
24 files changed, 523 insertions(+), 591 deletions(-)
diff --git a/.clang-format b/.clang-format
@@ -36,7 +36,7 @@ AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortFunctionsOnASingleLine: true
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: false
diff --git a/src/board/board.cpp b/src/board/board.cpp
@@ -10,30 +10,18 @@
/* Getters */
Color Board::get_side(void) const {
return side;
}
U64 Board::get_hash(void) const {
return hash;
}
uint8_t Board::get_castle(void) const {
return castle;
}
Square Board::get_enpassant(void) const {
return enpassant;
}
Color Board::get_side(void) const { return side; }
U64 Board::get_hash(void) const { return hash; }
uint8_t Board::get_castle(void) const { return castle; }
Square Board::get_enpassant(void) const { return enpassant; }
U64 Board::get_bitboard_color(Color side) const {
return colors[to_underlying(side)];
}
U64 Board::get_bitboard_color(Color side) const { return colors[to_underlying(side)]; }
U64 Board::get_bitboard_occupancy(void) const {
return colors[to_underlying(Color::WHITE)] | colors[to_underlying(Color::BLACK)];
}
U64 Board::get_bitboard_piece(piece::Type piece) const {
return pieces[to_underlying(piece)];
}
U64 Board::get_bitboard_piece(piece::Type piece) const { return pieces[to_underlying(piece)]; }
U64 Board::get_bitboard_piece(const piece::Piece &piece) const {
return get_bitboard_piece(piece.type, piece.color);
@@ -51,6 +39,14 @@ U64 Board::get_bitboard_piece_attacks(const piece::Piece &piece, Square square)
return piece(square, get_bitboard_occupancy());
}
U64 Board::get_bitboard_piece_moves(piece::Type piece, Color color, Square square) const {
return get_bitboard_piece_moves(piece::get(piece, color), square);
}
U64 Board::get_bitboard_piece_moves(const piece::Piece &piece, Square square) const {
return get_bitboard_piece_attacks(piece, square) & ~get_bitboard_color(piece.color);
}
Color Board::get_square_piece_color(Square square) const {
if (bit_get(colors[to_underlying(Color::WHITE)], to_underlying(square))) return Color::WHITE;
if (bit_get(colors[to_underlying(Color::BLACK)], to_underlying(square))) return Color::BLACK;
@@ -74,12 +70,8 @@ const piece::Piece *Board::get_square_piece(Square square) const {
/* Setters */
void Board::xor_hash(U64 op) {
hash ^= op;
}
void Board::and_castle(uint8_t right) {
castle &= 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;
@@ -137,6 +129,10 @@ bool Board::is_square_attacked(Square square, Color side) const {
return 0;
}
bool Board::is_piece_attack_square(const piece::Piece &piece, Square source, Square target) const {
return get_bitboard_piece_attacks(piece, source) & (C64(1) << to_underlying(target));
}
bool Board::is_check(void) const {
U64 king = pieces[to_underlying(piece::Type::KING)] & colors[to_underlying(side)];
Color side_other = (side == Color::BLACK) ? Color::WHITE : Color::BLACK;
diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp
@@ -5,7 +5,8 @@
#include "attack.hpp"
#include "board.hpp"
#include "moves.hpp"
#include "move.hpp"
#include "movelist.hpp"
#include "score.hpp"
#include "stats.hpp"
#include "transposition.hpp"
@@ -18,26 +19,6 @@
#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) {
@@ -65,13 +46,11 @@ int evaluate(const Board &board) {
return score;
}
int is_repetition() {
return 0;
}
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)) {
if (!move.make(stats.board, flag)) {
stats.board = copy;
return 0;
}
@@ -110,12 +89,20 @@ int quiescence(Stats &stats, int alpha, int 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, {0});
move_list_sort(list);
bool pv_flag = false;
const auto score_move = [&stats, &pv_flag](Move move) -> U32 {
if (stats.ply && stats.follow_pv) {
stats.follow_pv = 0;
if (stats.pv_table[0][stats.ply] == move) {
pv_flag = true;
return 20000;
}
}
return Score_move(stats, move);
};
stats.follow_pv = pv_flag;
for (const auto [move, _] : list) {
for (const auto [move, _] : MoveList(stats.board, score_move)) {
if (!stats_move_make(stats, copy, move, 1)) continue;
score = -quiescence(stats, -beta, -alpha);
stats_move_unmake(stats, copy);
@@ -133,8 +120,8 @@ int quiescence(Stats &stats, int alpha, int beta) {
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;
Move bestMove;
Board copy;
stats.pv_length[stats.ply] = stats.ply;
@@ -196,19 +183,28 @@ int negamax(Stats &stats, int alpha, int beta, int depth, bool null) {
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);
bool pv_flag = false;
const auto score_move = [&stats, &bestMove, &pv_flag](Move move) -> U32 {
if (move == bestMove) return 30000;
if (stats.ply && stats.follow_pv) {
stats.follow_pv = 0;
if (stats.pv_table[0][stats.ply] == move) {
pv_flag = true;
return 20000;
}
}
return Score_move(stats, move);
};
stats.follow_pv = pv_flag;
int legal_moves = 0;
int searched = 0;
for (const auto [move, _] : list) {
for (const auto [move, _] : MoveList(stats.board, score_move)) {
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()) {
if (futility && searched && !move.is_capture() && !move.is_promote() && !stats.board.is_check()) {
stats_move_unmake(stats, copy);
continue;
}
@@ -218,11 +214,11 @@ int negamax(Stats &stats, int alpha, int beta, int depth, bool null) {
} 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]))) {
!move.is_capture() && !move.is_promote() &&
(move.source() != stats.killer[0][stats.ply].source() ||
move.target() != stats.killer[0][stats.ply].target()) &&
(move.source() != stats.killer[1][stats.ply].source() ||
move.target() != stats.killer[1][stats.ply].target())) {
score = -negamax(stats, -alpha - 1, -alpha, depth - 2, true);
} else
score = alpha + 1;
@@ -242,8 +238,8 @@ int negamax(Stats &stats, int alpha, int beta, int depth, bool null) {
searched++;
if (score > alpha) {
if (!move_capture(move)) {
stats.history[move_piece(move).index][move_target(move)] += depth;
if (!move.is_capture()) {
stats.history[move.piece().index][to_underlying(move.target())] += depth;
}
alpha = score;
@@ -254,7 +250,7 @@ int negamax(Stats &stats, int alpha, int beta, int depth, bool null) {
if (score >= beta) {
stats.ttable.write(stats, bestMove, beta, depth, HasheFlag::Beta);
if (!move_capture(move)) {
if (!move.is_capture()) {
stats.killer[1][stats.ply] = stats.killer[0][stats.ply];
stats.killer[0][stats.ply] = move;
}
@@ -276,23 +272,22 @@ int negamax(Stats &stats, int alpha, int beta, int depth, bool null) {
}
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));
std::cout << square_to_coordinates(move.source()) << square_to_coordinates(move.target());
if (move.is_promote()) std::cout << move.piece_promote().code;
}
TTable ttable(C64(0x400000));
void search_position(Board &board, int depth) {
Stats stats = {ttable, board, 0};
Stats stats = {ttable, board};
int alpha = -INFINITY, beta = INFINITY;
int alpha = -SCORE_INFINITY, beta = SCORE_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;
alpha = -SCORE_INFINITY;
beta = SCORE_INFINITY;
continue;
}
alpha = score - WINDOW;
@@ -300,27 +295,28 @@ void search_position(Board &board, int depth) {
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);
std::cout << "info score mate " << -(score + MATE_VALUE) / 2 - 1;
} 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);
std::cout << "info score mate " << (MATE_VALUE - score) / 2 + 1;
} else {
printf("info score cp %d depth %d nodes %ld pv ", score, crnt, stats.nodes);
std::cout << "info score " << score;
}
std::cout << " depth " << crnt;
std::cout << " nodes " << stats.nodes;
std::cout << " pv ";
for (int i = 0; i < stats.pv_length[0]; i++) {
move_print_UCI(stats.pv_table[0][i]);
printf(" ");
std::cout << " ";
}
printf("\n");
std::cout << "\n";
}
crnt++;
}
printf("bestmove ");
std::cout << "bestmove ";
move_print_UCI(stats.pv_table[0][0]);
printf("\n");
std::cout << "\n";
}
void print_info(void) {
@@ -354,9 +350,7 @@ void Instruction_free(Instruction **p) {
delete (*p);
}
char *Instruction_token(Instruction *self) {
return self->token;
}
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++;
@@ -382,28 +376,21 @@ char *Instruction_token_n(Instruction *self, int n) {
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};
char *Instruction_token_next(Instruction *self) { return Instruction_token_n(self, 1); }
uint8_t source = to_underlying(square_from_coordinates(move_string));
uint8_t target = to_underlying(square_from_coordinates(move_string + 2));
Move parse_move(const Board &board, char *move_string) {
Square source = square_from_coordinates(move_string);
Square target = 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) {
for (const auto [move, _] : MoveList(board)) {
if (move.source() == source && move.target() == target) {
if (move_string[4]) {
if (tolower(move_piece_promote(move).code) != move_string[4]) continue;
if (tolower(move.piece_promote().code) != move_string[4]) continue;
}
result = move;
break;
return move;
}
}
return result;
return {};
}
Board *Instruction_parse(Instruction *self, Board &board) {
@@ -434,8 +421,8 @@ Board *Instruction_parse(Instruction *self, Board &board) {
if (strcmp(token, "moves") == 0) {
while ((token = Instruction_token_next(self))) {
Move move = parse_move(board, token);
if (!move_cmp(move, {0})) {
move_make(move, board, 0);
if (move == Move()) {
move.make(board, 0);
} else {
printf("Invalid move %s!\n", token);
}
diff --git a/src/engine/score.cpp b/src/engine/score.cpp
@@ -1,5 +1,5 @@
#include "score.hpp"
#include "moves.hpp"
#include "move.hpp"
#include "stats.hpp"
#include "utils_cpp.hpp"
@@ -98,33 +98,26 @@ const Square mirror_score[128] =
};
// clang-format on
int Score_value(piece::Type piece) {
return Scores[to_underlying(piece)].value;
}
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;
}
U32 Score_move(const Stats &stats, Move move) {
const int piece = to_underlying(move.piece().type);
const int capture = to_underlying(move.piece_capture().type);
if (move_cmp(stats.killer[0][stats.ply], move))
return 9000;
else if (move_cmp(stats.killer[1][stats.ply], move))
return 8000;
if (move.is_capture()) return Scores[piece].capture[capture] + 10000;
if (stats.killer[0][stats.ply] == move) return 9000;
if (stats.killer[1][stats.ply] == move) return 8000;
return stats.history[piece][move_target(move)];
return stats.history[piece][to_underlying(move.target())];
}
void Score_move_list(const Stats &stats, std::vector<MoveE> &list) {
for (MoveE &move : list) {
move.score = Score_move(stats, move.move);
void Score_move_list(const Stats &stats, MoveList &list) {
for (auto &moveE : list) {
moveE.score = Score_move(stats, moveE.move);
}
}
diff --git a/src/engine/score.hpp b/src/engine/score.hpp
@@ -2,14 +2,15 @@
#define STELLAR_SCORE_H
#include "board.hpp"
#include "moves.hpp"
#include "movelist.hpp"
#include "stats.hpp"
#define INFINITY 50000
#define SCORE_INFINITY 50000
#define MATE_VALUE 49000
#define MATE_SCORE 48000
void Score_move_list(const Stats &stats, std::vector<MoveE> &list);
U32 Score_move(const Stats &stats, Move move);
void Score_move_list(const Stats &stats, MoveList &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);
diff --git a/src/engine/stats.hpp b/src/engine/stats.hpp
@@ -1,7 +1,7 @@
#ifndef STELLAR_STATS_H
#define STELLAR_STATS_H
#include "moves.hpp"
#include "move.hpp"
#include "utils_cpp.hpp"
#define MAX_PLY 64
diff --git a/src/engine/transposition.hpp b/src/engine/transposition.hpp
@@ -1,7 +1,7 @@
#ifndef STELLAR_TRANSPOSITION_H
#define STELLAR_TRANSPOSITION_H
#include "moves.hpp"
#include "move.hpp"
#include "stats.hpp"
#include "utils_cpp.hpp"
@@ -28,12 +28,9 @@ struct Hashe {
class TTable {
public:
TTable(U64 size) : table(size, {0}) {
}
TTable(U64 size) : table(size, {0}) {}
void clear() {
table.clear();
};
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;
diff --git a/src/include/attack.hpp b/src/include/attack.hpp
@@ -50,9 +50,7 @@ class SliderRook : public Slider {
return (key * rook_magic_numbers[square_i]) >> (64 - relevant_bits[square_i]);
}
virtual constexpr U64 mask(Square square) const override {
return masks[to_underlying(square)];
}
virtual constexpr U64 mask(Square square) const override { return masks[to_underlying(square)]; }
virtual constexpr U64 mask_fly(Square square, U64 block) const override {
uint8_t square_i = to_underlying(square);
@@ -98,9 +96,7 @@ class SliderBishop : public Slider {
return (key * bishop_magic_numbers[square_i]) >> (64 - relevant_bits[square_i]);
}
virtual constexpr U64 mask(Square square) const override {
return masks[to_underlying(square)];
}
virtual constexpr U64 mask(Square square) const override { return masks[to_underlying(square)]; }
virtual constexpr U64 mask_fly(Square square, U64 block) const override {
uint8_t square_i = to_underlying(square);
@@ -197,8 +193,7 @@ class Bishop : public Attack {
class King : public Attack {
public:
constexpr King() {
}
constexpr King() {}
virtual constexpr U64 operator()(Square square, U64 occupancy) const override {
return attacks[to_underlying(square)];
@@ -229,8 +224,7 @@ class King : public Attack {
class Knight : public Attack {
public:
constexpr Knight() {
}
constexpr Knight() {}
virtual constexpr U64 operator()(Square square, U64 occupancy) const override {
return attacks[to_underlying(square)];
@@ -264,8 +258,7 @@ class Knight : public Attack {
class PawnW : public Attack {
public:
constexpr PawnW() {
}
constexpr PawnW() {}
virtual constexpr U64 operator()(Square square, U64 occupancy) const override {
return attacks[to_underlying(square)];
@@ -290,8 +283,7 @@ class PawnW : public Attack {
class PawnB : public Attack {
public:
constexpr PawnB() {
}
constexpr PawnB() {}
virtual constexpr U64 operator()(Square square, U64 occupancy) const override {
return attacks[to_underlying(square)];
@@ -323,8 +315,7 @@ inline constexpr const PawnB pawnB;
class Queen : public Attack {
public:
constexpr Queen() {
}
constexpr Queen() {}
virtual constexpr U64 operator()(Square square, U64 occupancy) const override {
return rook(square, occupancy) | bishop(square, occupancy);
diff --git a/src/include/board.hpp b/src/include/board.hpp
@@ -17,8 +17,7 @@ class Board {
BQ = 8
};
Board() {
}
Board() {}
Board(const std::string &fen);
friend std::ostream &operator<<(std::ostream &os, const Board &board);
@@ -39,6 +38,8 @@ class Board {
U64 get_bitboard_piece_attacks(piece::Type piece, Color color, Square square) const;
U64 get_bitboard_piece_attacks(const piece::Piece &piece, Square square) const;
U64 get_bitboard_piece_moves(piece::Type piece, Color color, Square square) const;
U64 get_bitboard_piece_moves(const piece::Piece &piece, Square square) const;
Color get_square_piece_color(Square square) const;
piece::Type get_square_piece_type(Square square) const;
@@ -64,6 +65,7 @@ class Board {
bool is_square_attacked(Square Square, Color side) const;
bool is_square_occupied(Square Square) const;
bool is_piece_attack_square(const piece::Piece &piece, Square source, Square target) const;
bool is_check(void) const;
private:
diff --git a/src/include/move.hpp b/src/include/move.hpp
@@ -0,0 +1,57 @@
#ifndef STELLAR_MOVES_H
#define STELLAR_MOVES_H
#include "board.hpp"
#include "piece.hpp"
#include "utils_cpp.hpp"
#include <iostream>
#include <vector>
struct Move {
Move(){};
Move(Square source, Square target, const piece::Piece *piece, const piece::Piece *capture,
const piece::Piece *promote, bool dbl, bool enpassant, bool castle)
: source_i(to_underlying(source)), target_i(to_underlying(target)), piece_i(piece->index),
piece_capture_i(capture ? capture->index : 0), piece_promote_i(promote ? promote->index : 0),
dbl(dbl), enpassant(enpassant), castle(castle), capture(capture != NULL), promote(promote != NULL) {
}
bool operator==(const Move &m) const = default;
Square source(void) const { return static_cast<Square>(source_i); }
Square target(void) const { return static_cast<Square>(target_i); }
bool is_double(void) const { return dbl; }
bool is_enpassant(void) const { return enpassant; }
bool is_castle(void) const { return castle; }
bool is_capture(void) const { return capture; }
bool is_promote(void) const { return promote; }
const piece::Piece &piece(void) const { return piece::get_from_index(piece_i); }
const piece::Piece &piece_capture(void) const { return piece::get_from_index(piece_capture_i); }
const piece::Piece &piece_promote(void) const { return piece::get_from_index(piece_promote_i); }
bool make(Board &board, bool attack_only) const;
friend std::ostream &operator<<(std::ostream &os, const Move &Move);
private:
void piece_remove(Board &board, const piece::Piece &piece, Square square) const;
void piece_set(Board &board, const piece::Piece &piece, Square square) const;
void piece_move(Board &board, const piece::Piece &piece, Square source, Square target) const;
unsigned source_i : 6;
unsigned target_i : 6;
unsigned piece_i : 5;
unsigned piece_capture_i : 5;
unsigned piece_promote_i : 5;
bool dbl : 1;
bool enpassant : 1;
bool castle : 1;
bool capture : 1;
bool promote : 1;
};
#endif
diff --git a/src/include/movelist.hpp b/src/include/movelist.hpp
@@ -0,0 +1,62 @@
#ifndef STELLAR_MOVE_LIST_H
#define STELLAR_MOVE_LIST_H
#include "board.hpp"
#include "move.hpp"
#include "utils_cpp.hpp"
#include <functional>
#include <iostream>
#include <vector>
class MoveList {
public:
typedef std::function<U32(const Move &)> score_f;
struct MoveListE {
Move move;
U32 score;
auto operator<=>(const MoveListE &mle) { return mle.score <=> score; }
};
private:
using list_t = std::vector<MoveListE>;
public:
MoveList(const Board &board, score_f score = nullptr) : list(), score_move(score) {
list.reserve(256);
generate(board);
sort(list.begin(), list.end());
}
auto size() const { return list.size(); }
MoveListE &operator[](size_t idx) { return list[idx]; }
const MoveListE &operator[](size_t idx) const { return list[idx]; }
friend std::ostream &operator<<(std::ostream &os, const MoveList &list);
using iterator = list_t::iterator;
using const_iterator = list_t::const_iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
const_iterator cbegin() const { return list.cbegin(); }
const_iterator cend() const { return list.cend(); }
private:
void push_back(const MoveListE &&mle) { list.push_back(mle); }
void push_back(const Move &&move) { push_back({move, score_move ? score_move(move) : 0}); }
void generate(const Board &board);
void clear() { list.clear(); }
list_t list;
score_f score_move;
};
#endif
diff --git a/src/include/moves.hpp b/src/include/moves.hpp
@@ -1,49 +0,0 @@
#ifndef STELLAR_MOVES_H
#define STELLAR_MOVES_H
#include "board.hpp"
#include "piece.hpp"
#include <iostream>
#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;
friend std::ostream &operator<<(std::ostream &os, const Move &Move);
};
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);
#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/piece.hpp b/src/include/piece.hpp
@@ -21,9 +21,7 @@ typedef Iterator<Type, Type::PAWN, Type::KING> TypeIter;
class Piece {
public:
constexpr U64 operator()(Square square, U64 occupancy) const {
return attack(square, occupancy);
}
constexpr U64 operator()(Square square, U64 occupancy) const { return attack(square, occupancy); }
const Type type;
const Color color;
@@ -34,8 +32,7 @@ class Piece {
protected:
constexpr Piece(Type type, Color color, char code, const char *symbol, const attack::Attack &attack)
: type(type), color(color), code(code), symbol(symbol), index(index_calc(color, type)),
attack(attack) {
}
attack(attack) {}
constexpr uint8_t index_calc(Color color, Type type) {
return to_underlying(color) * 6 + to_underlying(type);
@@ -50,48 +47,42 @@ class Pawn : public Piece {
constexpr Pawn(Color color)
: Piece(Type::PAWN, color, color == Color::WHITE ? 'P' : 'p', color == Color::WHITE ? "■ " : "■ ",
color == Color::WHITE ? *(attack::Attack *)&attack::pawnW
: *(attack::Attack *)&attack::pawnB) {
}
: *(attack::Attack *)&attack::pawnB) {}
};
class Knight : public Piece {
public:
constexpr Knight(Color color)
: Piece(Type::KNIGHT, color, color == Color::WHITE ? 'N' : 'n', color == Color::WHITE ? "■ " : "■ ",
attack::knight) {
}
attack::knight) {}
};
class Bishop : public Piece {
public:
constexpr Bishop(Color color)
: Piece(Type::BISHOP, color, color == Color::WHITE ? 'B' : 'b', color == Color::WHITE ? "■ " : "■ ",
attack::bishop) {
}
attack::bishop) {}
};
class Rook : public Piece {
public:
constexpr Rook(Color color)
: Piece(Type::ROOK, color, color == Color::WHITE ? 'R' : 'r', color == Color::WHITE ? "■ " : "■ ",
attack::rook) {
}
attack::rook) {}
};
class Queen : public Piece {
public:
constexpr Queen(Color color)
: Piece(Type::QUEEN, color, color == Color::WHITE ? 'Q' : 'q', color == Color::WHITE ? "■ " : "■ ",
attack::queen) {
}
attack::queen) {}
};
class King : public Piece {
public:
constexpr King(Color color)
: Piece(Type::KING, color, color == Color::WHITE ? 'K' : 'k', color == Color::WHITE ? "■ " : "■ ",
attack::king) {
}
attack::king) {}
};
const constexpr Piece table[2][6] = {
@@ -116,9 +107,7 @@ constexpr const Piece &get_from_code(char code) {
throw std::exception();
}
constexpr const Piece &get_from_index(uint8_t index) {
return table[index / 6][index % 6];
}
constexpr const Piece &get_from_index(uint8_t index) { return table[index / 6][index % 6]; }
} // namespace piece
diff --git a/src/include/random.hpp b/src/include/random.hpp
@@ -5,18 +5,12 @@
class Random {
public:
constexpr Random(void) {
}
constexpr Random(U64 seed) : state(seed) {
}
constexpr Random(void) {}
constexpr Random(U64 seed) : state(seed) {}
constexpr U64 operator()(void) {
return get_U64();
}
constexpr U64 operator()(void) { return get_U64(); }
constexpr void reset(void) {
state = seed;
}
constexpr void reset(void) { state = seed; }
constexpr U32 get_U32(void) {
U32 number = state;
diff --git a/src/include/utils_cpp.hpp b/src/include/utils_cpp.hpp
@@ -14,28 +14,20 @@ template <typename C, C beginVal, C endVal> class Iterator {
int val;
public:
constexpr Iterator(const C &f) : val(static_cast<val_t>(f)) {
}
constexpr Iterator() : val(static_cast<val_t>(beginVal)) {
}
constexpr Iterator(const C &f) : val(static_cast<val_t>(f)) {}
constexpr Iterator() : val(static_cast<val_t>(beginVal)) {}
constexpr Iterator operator++() {
++val;
return *this;
}
constexpr C operator*() {
return static_cast<C>(val);
}
constexpr Iterator begin() {
return *this;
}
constexpr C operator*() { return static_cast<C>(val); }
constexpr Iterator begin() { return *this; }
constexpr Iterator end() {
// static const Iterator endIter = ++Iterator(endVal);
// return endIter;
return ++Iterator(endVal);
}
constexpr bool operator!=(const Iterator &i) {
return val != i.val;
}
constexpr bool operator!=(const Iterator &i) { return val != i.val; }
};
#define C64(constantU64) constantU64##ULL
@@ -84,17 +76,11 @@ inline const char *square_to_coordinates(Square square) {
// clang-format on
// useful bit functions
constexpr bool bit_get(const U64 &bitboard, uint8_t square) {
return (bitboard >> (square)) & C64(1);
}
constexpr bool bit_get(const U64 &bitboard, uint8_t square) { return (bitboard >> (square)) & C64(1); }
constexpr void bit_set(U64 &bitboard, uint8_t square) {
bitboard |= (C64(1) << square);
}
constexpr void bit_set(U64 &bitboard, uint8_t square) { bitboard |= (C64(1) << square); }
constexpr void bit_pop(U64 &bitboard, uint8_t square) {
bitboard &= ~(C64(1) << (square));
}
constexpr void bit_pop(U64 &bitboard, uint8_t square) { bitboard &= ~(C64(1) << (square)); }
constexpr uint8_t bit_count(U64 bitboard) {
#if __has_builtin(__builtin_popcountll)
@@ -127,30 +113,14 @@ inline constexpr const U64 notAFile = C64(0xfefefefefefefefe);
inline constexpr const U64 notHFile = C64(0x7f7f7f7f7f7f7f7f);
typedef U64 (*direction_f)(U64);
inline constexpr U64 soutOne(U64 b) {
return b >> 8;
}
inline constexpr U64 nortOne(U64 b) {
return b << 8;
}
inline constexpr U64 eastOne(U64 b) {
return (b & notHFile) << 1;
}
inline constexpr U64 westOne(U64 b) {
return (b & notAFile) >> 1;
}
inline constexpr U64 soEaOne(U64 b) {
return (b & notHFile) >> 7;
}
inline constexpr U64 soWeOne(U64 b) {
return (b & notAFile) >> 9;
}
inline constexpr U64 noEaOne(U64 b) {
return (b & notHFile) << 9;
}
inline constexpr U64 noWeOne(U64 b) {
return (b & notAFile) << 7;
}
inline constexpr U64 soutOne(U64 b) { return b >> 8; }
inline constexpr U64 nortOne(U64 b) { return b << 8; }
inline constexpr U64 eastOne(U64 b) { return (b & notHFile) << 1; }
inline constexpr U64 westOne(U64 b) { return (b & notAFile) >> 1; }
inline constexpr U64 soEaOne(U64 b) { return (b & notHFile) >> 7; }
inline constexpr U64 soWeOne(U64 b) { return (b & notAFile) >> 9; }
inline constexpr U64 noEaOne(U64 b) { return (b & notHFile) << 9; }
inline constexpr U64 noWeOne(U64 b) { return (b & notAFile) << 7; }
#define start_position "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 "
diff --git a/src/include/zobrist.hpp b/src/include/zobrist.hpp
@@ -13,17 +13,11 @@ class Zobrist {
public:
Zobrist() = delete;
static inline constexpr U64 key_side(void) {
return keys_side;
}
static inline constexpr U64 key_side(void) { return keys_side; }
static inline constexpr U64 key_castle(int exp) {
return keys_castle[exp];
}
static inline constexpr U64 key_castle(int exp) { return keys_castle[exp]; }
static inline constexpr U64 key_enpassant(Square square) {
return keys_enpassant[to_underlying(square)];
}
static inline constexpr U64 key_enpassant(Square square) { return keys_enpassant[to_underlying(square)]; }
static inline constexpr U64 key_piece(const piece::Piece &piece, Square square) {
return keys_piece[piece.index][to_underlying(square)];
diff --git a/src/move/CMakeLists.txt b/src/move/CMakeLists.txt
@@ -0,0 +1,8 @@
add_library(moves OBJECT
move.cpp
movelist.cpp
)
target_include_directories(moves
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/move/move.cpp b/src/move/move.cpp
@@ -0,0 +1,105 @@
#include "move.hpp"
#include "utils_cpp.hpp"
#include "zobrist.hpp"
#include <algorithm>
#include <iomanip>
void Move::piece_remove(Board &board, const piece::Piece &piece, Square square) const {
board.pop_piece(piece, square);
board.xor_hash(Zobrist::key_piece(piece, square));
}
void Move::piece_set(Board &board, const piece::Piece &piece, Square square) const {
board.set_piece(piece, square);
board.xor_hash(Zobrist::key_piece(piece, square));
}
void Move::piece_move(Board &board, const piece::Piece &piece, Square source, Square target) const {
piece_remove(board, piece, source);
piece_set(board, piece, target);
}
bool Move::make(Board &board, bool attack_only) const {
static constexpr const int castling_rights[64] = {
// clang-format off
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
};
if (attack_only) {
if (is_capture()) return make(board, false);
return 0;
} else {
const piece::Piece &piece = this->piece();
const Color color = board.get_side();
const Square source = this->source();
const Square target = this->target();
const Square ntarget =
static_cast<Square>(to_underlying(this->target()) + (color == Color::WHITE ? -8 : +8));
if (!is_capture()) {
if (is_promote()) {
piece_remove(board, piece, source);
piece_set(board, piece_promote(), target);
} else {
piece_move(board, piece, source, target);
}
} else {
if (is_enpassant()) {
piece_move(board, piece, source, target);
piece_remove(board, piece_capture(), ntarget);
} else if (is_promote()) {
piece_remove(board, piece, source);
piece_remove(board, piece_capture(), target);
piece_set(board, piece_promote(), target);
} else {
piece_remove(board, piece_capture(), target);
piece_move(board, piece, source, target);
}
}
board.set_enpassant(is_double() ? ntarget : Square::no_sq);
if (is_castle()) {
static constexpr const piece::Piece &rook_white = piece::get(piece::Type::ROOK, Color::WHITE);
static constexpr const piece::Piece &rook_black = piece::get(piece::Type::ROOK, Color::BLACK);
if (target == Square::g1) piece_move(board, rook_white, Square::h1, Square::f1);
if (target == Square::c1) piece_move(board, rook_white, Square::a1, Square::d1);
if (target == Square::g8) piece_move(board, rook_black, Square::h8, Square::f8);
if (target == Square::c8) piece_move(board, rook_black, Square::a8, Square::d8);
}
board.xor_hash(Zobrist::key_castle(board.get_castle()));
board.and_castle(castling_rights[to_underlying(this->source())]);
board.and_castle(castling_rights[to_underlying(this->target())]);
board.xor_hash(Zobrist::key_castle(board.get_castle()));
if (!board.is_check()) {
board.switch_side();
return 1;
}
return 0;
}
}
std::ostream &operator<<(std::ostream &os, const Move &move) {
os << square_to_coordinates(move.source()) << " ";
os << square_to_coordinates(move.target()) << " ";
os << move.piece().code << " ";
os << (move.is_capture() ? move.piece_capture().code : '.') << " ";
os << (move.is_promote() ? move.piece_promote().code : '.') << " ";
os << move.is_double() << " ";
os << move.is_enpassant() << " ";
os << move.is_castle();
return os;
}
diff --git a/src/move/movelist.cpp b/src/move/movelist.cpp
@@ -0,0 +1,120 @@
#include "movelist.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))
void MoveList::generate(const Board &board) {
this->clear();
uint8_t src_i, tgt_i;
Color color = board.get_side();
Color colorOther = color == Color::BLACK ? Color::WHITE : Color::BLACK;
// pawn moves
const piece::Piece &pawn = piece::get(piece::Type::PAWN, color);
const int add = (color == Color::WHITE) ? +8 : -8;
U64 bitboard = board.get_bitboard_piece(pawn);
bitboard_for_each_bit(src_i, bitboard) {
const Square src = static_cast<Square>(src_i);
const Square tgt = static_cast<Square>(tgt_i = src_i + add);
if (!board.is_square_occupied(tgt)) {
if (pawn_canPromote(color, src)) {
push_back(Move(src, tgt, &pawn, nullptr, &piece::get(piece::Type::KNIGHT, color), 0, 0, 0));
push_back(Move(src, tgt, &pawn, nullptr, &piece::get(piece::Type::BISHOP, color), 0, 0, 0));
push_back(Move(src, tgt, &pawn, nullptr, &piece::get(piece::Type::ROOK, color), 0, 0, 0));
push_back(Move(src, tgt, &pawn, nullptr, &piece::get(piece::Type::QUEEN, color), 0, 0, 0));
} else {
push_back(Move(src, tgt, &pawn, 0, 0, 0, 0, 0));
// two ahead
const Square tgt = static_cast<Square>(tgt_i + add);
if (pawn_onStart(color, src) && !board.is_square_occupied(tgt))
push_back(Move(src, tgt, &pawn, 0, 0, 1, 0, 0));
}
}
// capture
U64 attack = board.get_bitboard_piece_attacks(pawn, src) & board.get_bitboard_color(colorOther);
bitboard_for_each_bit(tgt_i, attack) {
const Square tgt = static_cast<Square>(tgt_i);
const piece::Piece *capture = board.get_square_piece(tgt);
if (pawn_canPromote(color, src)) {
push_back(Move(src, tgt, &pawn, capture, &piece::get(piece::Type::KNIGHT, color), 0, 0, 0));
push_back(Move(src, tgt, &pawn, capture, &piece::get(piece::Type::BISHOP, color), 0, 0, 0));
push_back(Move(src, tgt, &pawn, capture, &piece::get(piece::Type::ROOK, color), 0, 0, 0));
push_back(Move(src, tgt, &pawn, capture, &piece::get(piece::Type::QUEEN, color), 0, 0, 0));
} else {
push_back(Move(src, tgt, &pawn, capture, 0, 0, 0, 0));
}
}
// en passant
const Square enpassant = board.get_enpassant();
if (enpassant != Square::no_sq && board.is_piece_attack_square(pawn, src, enpassant))
push_back(Move(src, enpassant, &pawn, &piece::get(piece::Type::PAWN, colorOther), 0, 0, 1, 0));
}
// All piece move
for (const piece::Type type : ++piece::TypeIter()) {
const piece::Piece &piece = piece::get(type, color);
U64 bitboard = board.get_bitboard_piece(piece);
bitboard_for_each_bit(src_i, bitboard) {
const Square src = static_cast<Square>(src_i);
U64 attack = board.get_bitboard_piece_moves(piece, src);
bitboard_for_each_bit(tgt_i, attack) {
const Square tgt = static_cast<Square>(tgt_i);
push_back(Move(src, tgt, &piece, board.get_square_piece(tgt), 0, 0, 0, 0));
}
}
}
// Castling
if (color == Color::WHITE) {
static const piece::Piece &piece = piece::get(piece::Type::KING, Color::WHITE);
if (!board.is_square_attacked(Square::e1, Color::BLACK)) {
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::f1, Color::BLACK))
push_back(Move(Square::e1, Square::g1, &piece, 0, 0, 0, 0, 1));
}
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::d1, Color::BLACK) &&
!board.is_square_attacked(Square::c1, Color::BLACK))
push_back(Move(Square::e1, Square::c1, &piece, 0, 0, 0, 0, 1));
}
}
} else {
static const piece::Piece &piece = piece::get(piece::Type::KING, Color::BLACK);
if (!board.is_square_attacked(Square::e8, Color::WHITE)) {
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::f8, Color::WHITE))
push_back(Move(Square::e8, Square::g8, &piece, 0, 0, 0, 0, 1));
}
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::d8, Color::WHITE) &&
!board.is_square_attacked(Square::c8, Color::WHITE))
push_back(Move(Square::e8, Square::c8, &piece, 0, 0, 0, 0, 1));
}
}
}
}
std::ostream &operator<<(std::ostream &os, const MoveList &list) {
os << "Size: " << list.list.size();
for (const auto &moveE : list.list) {
os << moveE.score << ": " << moveE.move << "\n";
}
return os;
}
diff --git a/src/moves/CMakeLists.txt b/src/moves/CMakeLists.txt
@@ -1,9 +0,0 @@
add_library(moves OBJECT
moves.cpp
moves_make.cpp
moves_generate.cpp
)
target_include_directories(moves
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/moves/moves.cpp b/src/moves/moves.cpp
@@ -1,51 +0,0 @@
#include "moves.hpp"
#include "utils_cpp.hpp"
#include <algorithm>
#include <iomanip>
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 {
.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_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());
}
std::ostream &operator<<(std::ostream &os, const Move &move) {
os << square_to_coordinates(static_cast<Square>(move_source(move))) << " ";
os << square_to_coordinates(static_cast<Square>(move_target(move))) << " ";
os << move_piece(move).code << " ";
os << (move_capture(move) ? move_piece_capture(move).code : '.') << " ";
os << (move_promote(move) ? move_piece_promote(move).code : '.') << " ";
os << move_double(move) << " ";
os << move_enpassant(move) << " ";
os << move_castle(move);
return os;
}
diff --git a/src/moves/moves_generate.cpp b/src/moves/moves_generate.cpp
@@ -1,137 +0,0 @@
#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, capture, &piece::get(piece::Type::KNIGHT, color), 0, 0, 0), \
0}); \
res.push_back( \
{move_encode(source, target, &piece, capture, &piece::get(piece::Type::BISHOP, color), 0, 0, 0), \
0}); \
res.push_back( \
{move_encode(source, target, &piece, capture, &piece::get(piece::Type::ROOK, color), 0, 0, 0), 0}); \
res.push_back( \
{move_encode(source, target, &piece, capture, &piece::get(piece::Type::QUEEN, color), 0, 0, 0), 0});
std::vector<MoveE> move_list_generate(const Board &board) {
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);
} else {
res.push_back({move_encode(src_i, tgt_i, &piece, 0, 0, 0, 0, 0), 0});
// two ahead
Square tgt = static_cast<Square>(tgt_i + add);
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) {
Square tgt = static_cast<Square>(tgt_i);
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
auto type_it = piece::TypeIter().begin();
for (++type_it; type_it != type_it.end(); ++type_it) {
const piece::Piece &piece = piece::get(*type_it, color);
U64 bitboard = board.get_bitboard_piece(piece);
bitboard_for_each_bit(src_i, bitboard) {
Square src = static_cast<Square>(src_i);
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) {
static 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 {
static 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});
}
}
res.resize(res.size());
return res;
}
diff --git a/src/moves/moves_make.cpp b/src/moves/moves_make.cpp
@@ -1,88 +0,0 @@
#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 constexpr const piece::Piece &rook_white = piece::get(piece::Type::ROOK, Color::WHITE);
static constexpr const piece::Piece &rook_black = piece::get(piece::Type::ROOK, Color::BLACK);
if (target == Square::g1) _piece_move(board, rook_white, Square::h1, Square::f1);
if (target == Square::c1) _piece_move(board, rook_white, Square::a1, Square::d1);
if (target == Square::g8) _piece_move(board, rook_black, Square::h8, Square::f8);
if (target == Square::c8) _piece_move(board, rook_black, 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/perft.cpp b/src/perft/perft.cpp
@@ -7,7 +7,8 @@
#include "attack.hpp"
#include "board.hpp"
#include "moves.hpp"
#include "move.hpp"
#include "movelist.hpp"
#include "perft.hpp"
#include "utils_cpp.hpp"
#include "zobrist.hpp"
@@ -46,15 +47,11 @@ void perft_result_add(PerftResult *base, PerftResult *add) {
#endif
}
typedef std::vector<MoveE> MoveList;
void perft_driver(Board &board, int depth, PerftResult *result) {
MoveList moveList = move_list_generate(board);
Board copy;
for (const auto [move, _] : moveList) {
for (const auto [move, _] : MoveList(board)) {
copy = board;
if (!move_make(move, copy, 0)) continue;
if (!move.make(copy, 0)) continue;
if (depth != 1) {
perft_driver(copy, depth - 1, result);
@@ -62,10 +59,10 @@ void perft_driver(Board &board, int depth, PerftResult *result) {
result->node++;
#ifdef USE_FULL_COUNT
if (copy.is_check()) 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++;
if (move.get_capture()) result->capture++;
if (move.get_enpassant()) result->enpassant++;
if (move.get_castle()) result->castle++;
if (move.get_promote()) result->promote++;
#endif
}
}
@@ -73,7 +70,7 @@ void perft_driver(Board &board, int depth, PerftResult *result) {
typedef struct perf_shared perf_shared;
struct perf_shared {
MoveList list;
const MoveList &list;
int depth;
const char *fen;
pthread_mutex_t mutex;
@@ -99,7 +96,7 @@ void *perft_thread(void *arg) {
result = {0};
copy = board;
if (!move_make(move, copy, 0)) continue;
if (!move.make(copy, 0)) continue;
if (shared->depth != 1) {
perft_driver(copy, shared->depth - 1, &result);
@@ -107,10 +104,10 @@ void *perft_thread(void *arg) {
result.node++;
#ifdef USE_FULL_COUNT
if (copy.is_check()) 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++;
if (move.get_capture()) result.capture++;
if (move.get_enpassant()) result.enpassant++;
if (move.get_castle()) result.castle++;
if (move.get_promote()) result.promote++;
#endif
}
}
@@ -119,8 +116,10 @@ void *perft_thread(void *arg) {
PerftResult perft_test(const char *fen, int depth, int thread_num) {
pthread_t *threads = new pthread_t(thread_num);
MoveList *list = new MoveList(Board(fen));
perf_shared shared = {
.list = move_list_generate(Board(fen)),
.list = *list,
.depth = depth,
.fen = fen,
};
@@ -132,6 +131,7 @@ PerftResult perft_test(const char *fen, int depth, int thread_num) {
for (int i = 0; i < thread_num; i++)
pthread_join(threads[i], NULL);
delete list;
delete threads;
return shared.result;
}