stellar

Stellar - UCI 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 11:24:59 +0200

Improve Move and MoveList

Diffstat:
M.clang-format | 2+-
Msrc/board/board.cpp | 44++++++++++++++++++++------------------------
Msrc/engine/engine.cpp | 151++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/engine/score.cpp | 31++++++++++++-------------------
Msrc/engine/score.hpp | 7++++---
Msrc/engine/stats.hpp | 2+-
Msrc/engine/transposition.hpp | 9+++------
Msrc/include/attack.hpp | 23+++++++----------------
Msrc/include/board.hpp | 6++++--
Asrc/include/move.hpp | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/movelist.hpp | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/include/moves.hpp | 49-------------------------------------------------
Msrc/include/piece.hpp | 29+++++++++--------------------
Msrc/include/random.hpp | 14++++----------
Msrc/include/utils_cpp.hpp | 62++++++++++++++++----------------------------------------------
Msrc/include/zobrist.hpp | 12+++---------
Asrc/move/CMakeLists.txt | 8++++++++
Asrc/move/move.cpp | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/move/movelist.cpp | 120+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/moves/CMakeLists.txt | 9---------
Dsrc/moves/moves.cpp | 51---------------------------------------------------
Dsrc/moves/moves_generate.cpp | 137-------------------------------------------------------------------------------
Dsrc/moves/moves_make.cpp | 88-------------------------------------------------------------------------------
Msrc/perft/perft.cpp | 36++++++++++++++++++------------------
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 &copy, 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; }