Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .github/workflows/clang-format.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
name: clang-format

on:
pull_request:
branches: [ "main" ]

workflow_run:
workflows: ["CMake on multiple platforms"]
types:
- completed

jobs:
format:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-latest
permissions:
contents: write # required to push back into PR
contents: write

steps:
- uses: actions/checkout@v4
Expand Down
5 changes: 4 additions & 1 deletion attacks.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include "fwd_decl.h"
#include "bitboard.h"
#include "types.h"
#include <array>
Expand Down Expand Up @@ -288,4 +289,6 @@ template <PieceType pt> [[nodiscard]] constexpr Bitboard slider(Square sq, Bitbo
return queen(sq, occupied);
}

} // namespace chess::attacks

} // namespace chess::attacks

29 changes: 14 additions & 15 deletions movegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,33 +267,32 @@ template <typename T, Color c, bool capturesOnly> void movegen::genKingMoves(con
if (pos.checkers())
return;

// Precompute all castling masks
constexpr Bitboard WHITE_OO_EMPTY = (1ULL << SQ_F1) | (1ULL << SQ_G1);
constexpr Bitboard WHITE_OO_SAFE = (1ULL << SQ_F1) | (1ULL << SQ_G1);
constexpr Bitboard WHITE_OOO_EMPTY = (1ULL << SQ_B1) | (1ULL << SQ_C1) | (1ULL << SQ_D1);
constexpr Bitboard WHITE_OOO_SAFE = (1ULL << SQ_C1) | (1ULL << SQ_D1);

constexpr Bitboard BLACK_OO_EMPTY = (1ULL << SQ_F8) | (1ULL << SQ_G8);
constexpr Bitboard BLACK_OO_SAFE = (1ULL << SQ_F8) | (1ULL << SQ_G8);
constexpr Bitboard BLACK_OOO_EMPTY = (1ULL << SQ_B8) | (1ULL << SQ_C8) | (1ULL << SQ_D8);
constexpr Bitboard BLACK_OOO_SAFE = (1ULL << SQ_C8) | (1ULL << SQ_D8);

Bitboard occupancy = pos.occ();
Bitboard enemy_attacks = enemyAttacks;

if constexpr (c == WHITE) {
Bitboard WHITE_OO_EMPTY = pos.getCastlingPath(WHITE, true);
Bitboard WHITE_OO_SAFE = between(kingSq, castling_king_square(WHITE, true));
Bitboard WHITE_OOO_EMPTY = pos.getCastlingPath(WHITE, false);
Bitboard WHITE_OOO_SAFE = between(kingSq, castling_king_square(WHITE, false));

if ((pos.castlingRights() & WHITE_OO) && !(occupancy & WHITE_OO_EMPTY) && !(enemy_attacks & WHITE_OO_SAFE)) {
out.push_back(Move::make<CASTLING>(SQ_E1, SQ_H1));
out.push_back(Move::make<CASTLING>(kingSq, SQ_H1));
}
if ((pos.castlingRights() & WHITE_OOO) && !(occupancy & WHITE_OOO_EMPTY) && !(enemy_attacks & WHITE_OOO_SAFE)) {
out.push_back(Move::make<CASTLING>(SQ_E1, SQ_A1));
out.push_back(Move::make<CASTLING>(kingSq, SQ_A1));
}
} else {
Bitboard BLACK_OO_EMPTY = pos.getCastlingPath(BLACK, true);
Bitboard BLACK_OO_SAFE = between(kingSq, castling_king_square(BLACK, true));
Bitboard BLACK_OOO_EMPTY = pos.getCastlingPath(BLACK, false);
Bitboard BLACK_OOO_SAFE = between(kingSq, castling_king_square(BLACK, false));

if ((pos.castlingRights() & BLACK_OO) && !(occupancy & BLACK_OO_EMPTY) && !(enemy_attacks & BLACK_OO_SAFE)) {
out.push_back(Move::make<CASTLING>(SQ_E8, SQ_H8));
out.push_back(Move::make<CASTLING>(kingSq, SQ_H8));
}
if ((pos.castlingRights() & BLACK_OOO) && !(occupancy & BLACK_OOO_EMPTY) && !(enemy_attacks & BLACK_OOO_SAFE)) {
out.push_back(Move::make<CASTLING>(SQ_E8, SQ_A8));
out.push_back(Move::make<CASTLING>(kingSq, SQ_A8));
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ template <typename PieceC, typename T> template <bool Strict> void _Position<Pie
case CASTLING: {
removePiece(target_piecetype, to_sq, target_color);
bool is_king_side = from_sq < to_sq;
Square rook_dest = relative_square(us, is_king_side ? SQ_F1 : Square::SQ_D1),
king_dest = relative_square(us, is_king_side ? SQ_G1 : Square::SQ_C1);
Square rook_dest = castling_rook_square(us, is_king_side),
king_dest = castling_king_square(us, is_king_side);
placePiece<ROOK>(rook_dest, us);
placePiece<KING>(king_dest, us);
current_state.incr_sqs[0] = from_sq;
Expand Down Expand Up @@ -629,7 +629,6 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::refresh_attack
}
template <typename PieceC, typename T> uint64_t _Position<PieceC, T>::zobrist() const {
uint64_t hash = 0;
#pragma unroll(64)
for (int sq = 0; sq < 64; ++sq) {
auto p = piece_on((Square)sq);
hash ^= (p == PieceC::NO_PIECE) ? 0 : zobrist::RandomPiece[enum_idx<PieceC>()][(int)p][sq];
Expand Down Expand Up @@ -674,6 +673,7 @@ template <typename PieceC, typename T> Square _Position<PieceC, T>::_valid_ep_sq
Bitboard mask = 1ULL << ep_square();
Bitboard pawn_mask = mask << 8;
Bitboard org_pawn_mask = mask >> 8;
if (sideToMove() == BLACK) std::swap(pawn_mask, org_pawn_mask);
// rank 3 or rank 6, depending on color
if (rank_of(ep_square()) != ep_rank)
return SQ_NONE;
Expand Down Expand Up @@ -787,3 +787,5 @@ INSTANTIATE(EnginePiece)
INSTANTIATE(ContiguousMappingPiece)
#undef INSTANTIATE
} // namespace chess


78 changes: 62 additions & 16 deletions position.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,11 @@ template <typename Piece> struct alignas(64) HistoryEntry {
Color turn; // true if white to move
Move mv;
Key hash;
struct {
uint8_t halfMoveClock; // Half-move clock for 50/75-move rule
uint16_t fullMoveNumber; // Full-move number (starts at 1)
bool epIncluded;
int8_t repetition = 0;
uint8_t pliesFromNull = 0;
};
uint8_t halfMoveClock; // Half-move clock for 50/75-move rule
uint16_t fullMoveNumber; // Full-move number (starts at 1)
bool epIncluded;
int8_t repetition = 0;
uint8_t pliesFromNull = 0;
Square enPassant = SQ_NONE; // En passant target square
Square kings[COLOR_NB] = { SQ_NONE };
CastlingRights castlingRights; // Castling rights bitmask
Expand Down Expand Up @@ -160,9 +158,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en

// Move history stack
HeapAllocatedValueList<HistoryEntry<PieceC>, 6144>
history; // ahh, but i hope it fulfils before I manages to find the absolute limit of a game
// Move generation functions, but INTERNAL. (they're kind of long so i put them into a source
// file) Pawns (fully extensively tested)
history;
Bitboard _rook_pin;
Bitboard _bishop_pin;
Bitboard _checkers;
Expand All @@ -181,6 +177,12 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE,
PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE, PieceC::NO_PIECE
};
// Castling path, [color][king_side]
static constexpr std::array<std::array<Bitboard, 2>, 2> castling_path =
{{
{{ 0xe, 0x60 }},
{{ 0xe00000000000000ULL, 0x6000000000000000ULL }}
}};

public:
// Legal move generation functions
Expand Down Expand Up @@ -393,7 +395,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
}

[[nodiscard]] __FORCEINLINE Bitboard attackers(Color color, Square square) const { return attackers(color, square, occ()); }
// Compile-time piece type and color, runtime square

template <PieceType pt> __FORCEINLINE void placePiece(Square sq, Color c) {
if constexpr (pt != NO_PIECE_TYPE) {
Bitboard v = 1ULL << sq;
Expand Down Expand Up @@ -448,6 +450,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
}
__FORCEINLINE Bitboard occ() const { return current_state.occ[0] | current_state.occ[1]; }
PieceC piece_on(Square s) const {
assert(chess::is_valid(s));
#if !defined(_DEBUG) || defined(NDEBUG)
return pieces_list[s];
#else
Expand All @@ -470,8 +473,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
assert(p == _p2 && "Inconsistient piece map");
#else
if (p != _p2)
// throw std::invalid_argument("Inconsistient piece map");
exit(-1);
throw std::invalid_argument("Inconsistient piece map");
#endif
return p;
#endif
Expand Down Expand Up @@ -504,7 +506,12 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
__FORCEINLINE const HistoryEntry<PieceC> &state() const { return current_state; }
uint64_t zobrist() const;
__FORCEINLINE PieceC piece_at(Square sq) const { return piece_on(sq); }
__FORCEINLINE PieceC at(Square sq) const { return piece_at(sq); }
template <typename T = PieceC>
__FORCEINLINE PieceC at(Square sq) const {
assert(chess::is_valid(sq));
if constexpr (std::is_same_v<T, PieceType>) return piece_of(piece_at(sq));
else return piece_at(sq);
}
__FORCEINLINE Square enpassantSq() const { return ep_square(); }
CastlingRights clean_castling_rights() const;
void setFEN(const std::string &str);
Expand All @@ -524,6 +531,8 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
__FORCEINLINE bool is_insufficient_material() const {
return has_insufficient_material(WHITE) && has_insufficient_material(BLACK);
}
__FORCEINLINE bool isInsufficientMaterial() const { return is_insufficient_material(); }
__FORCEINLINE bool hasNonPawnMaterial(Color c) const { return bool(us(c) ^ (pieces(PAWN, KING) & us(c))); }
__FORCEINLINE bool inCheck() const { return checkers() != 0; }
__FORCEINLINE bool is_check() const { return checkers() != 0; }
__FORCEINLINE bool has_castling_rights(Color c) const { return castlingRights(c) != 0; }
Expand Down Expand Up @@ -583,14 +592,33 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
}
return b != 0;
}
__FORCEINLINE bool is_checkmate() const {
Movelist moves;
legals(moves);
return inCheck() && !moves.size();
}
__FORCEINLINE bool is_stalemate() const {
Movelist moves;
legals(moves);
return !inCheck() && !moves.size();
}
// Material-only key (note: Zobrist=Zpieces^Zep^Zcastling^Zturn, we just XORs the remaining, it's trivial)
__FORCEINLINE Key material_key() const {
return hash() ^ (zobrist::RandomTurn * ~sideToMove()) ^ (zobrist::RandomCastle[castlingRights()]) ^
(zobrist::RandomEP[ep_square() == SQ_NONE ? file_of(ep_square()) : FILE_NB]);
}
template <bool Strict = false> bool is_valid() const;
CheckType givesCheck(Move move) const;

/**
* @brief Checks if the current position is a draw by 50 move rule.
* Keep in mind that by the rules of chess, if the position has 50 half
* moves it's not necessarily a draw, since checkmate has higher priority,
* <del>call getHalfMoveDrawType,
* to determine whether the position is a draw or checkmate.</del>
* @return
*/
[[nodiscard]] __FORCEINLINE bool isHalfMoveDraw() const noexcept { return halfmoveClock() >= 100; }
[[nodiscard]] __FORCEINLINE Bitboard getCastlingPath(Color c, bool isKingSide) const { return castling_path[c][isKingSide]; }
private:
template <PieceType pt> [[nodiscard]] __FORCEINLINE Bitboard pinMask(Color c, Square sq) const {
static_assert(pt == BISHOP || pt == ROOK, "Only bishop or rook allowed!");
Expand Down Expand Up @@ -684,5 +712,23 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
refresh_attacks();
}
};
using Position = _Position<EnginePiece>; // for some fun because I HATE HARDCODING
namespace attacks{
/**
* @brief Returns the attacks for a given piece on a given square
* @param board
* @param color
* @param square
* @return
*/
template <typename T, typename = std::enable_if_t<is_piece_enum<T>::value>>
[[nodiscard]] __FORCEINLINE Bitboard attackers(const _Position<T> &board, Color color, Square square) noexcept {
return board.attackers(color, square);
}
}
// Aliases
using Position = _Position<EnginePiece>;
using Board = _Position<EnginePiece>;
}; // namespace chess



5 changes: 4 additions & 1 deletion types.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,14 @@ enum Direction : int8_t {
};
// clang-format on
inline constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); }
inline constexpr Square castling_rook_square(Color c, bool is_king_side) { return relative_square(c, is_king_side ? SQ_F1 : Square::SQ_D1); }
inline constexpr Square castling_king_square(Color c, bool is_king_side) { return relative_square(c, is_king_side ? SQ_G1 : Square::SQ_C1); }

inline constexpr Rank relative_rank(Color c, Rank r) { return Rank(r ^ (c * 7)); }

inline constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_of(s)); }

inline constexpr Direction relative_direction(Color c, Direction d) { return static_cast<Direction>(c == WHITE ? d : -d); }

inline constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; }

#define ENABLE_INCR_OPERATORS_ON(T) \
Expand Down Expand Up @@ -437,3 +438,5 @@ constexpr PieceType parse_pt(unsigned char c) {
}

} // namespace chess