Skip to content

Commit 29cefb4

Browse files
authored
More APIs integration
* Refactor attacks.h with additional includes and functions * remove the problematic attackers() * add some aliases and API wrappers * added more apis * Update position.h * fixed some bugs about stm in _valid_ep_square * more api for castling path (note: upcoming chess960 support) * more api for compatibility * modularize from castling_(rook|king)_square * modularized (a bit) castling code for upcoming Chess960 * Update position.h * Update clang-format workflow triggers Modify clang-format workflow to trigger on successful completion of another workflow. * Update position.h * fix compile error * fix compile errors * Fix castling move generation with correct king square * Update position.h * fix inversed squares * fixes incorrect castling path * Fixes #17
1 parent 9942ae8 commit 29cefb4

6 files changed

Lines changed: 98 additions & 40 deletions

File tree

.github/workflows/clang-format.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
name: clang-format
22

33
on:
4-
pull_request:
5-
branches: [ "main" ]
6-
4+
workflow_run:
5+
workflows: ["CMake on multiple platforms"]
6+
types:
7+
- completed
8+
79
jobs:
810
format:
11+
if: >
12+
github.event.workflow_run.conclusion == 'success' &&
13+
github.event.workflow_run.event == 'pull_request'
914
runs-on: ubuntu-latest
1015
permissions:
11-
contents: write # required to push back into PR
16+
contents: write
1217

1318
steps:
1419
- uses: actions/checkout@v4

attacks.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include "fwd_decl.h"
23
#include "bitboard.h"
34
#include "types.h"
45
#include <array>
@@ -288,4 +289,6 @@ template <PieceType pt> [[nodiscard]] constexpr Bitboard slider(Square sq, Bitbo
288289
return queen(sq, occupied);
289290
}
290291

291-
} // namespace chess::attacks
292+
293+
} // namespace chess::attacks
294+

movegen.cpp

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -267,33 +267,32 @@ template <typename T, Color c, bool capturesOnly> void movegen::genKingMoves(con
267267
if (pos.checkers())
268268
return;
269269

270-
// Precompute all castling masks
271-
constexpr Bitboard WHITE_OO_EMPTY = (1ULL << SQ_F1) | (1ULL << SQ_G1);
272-
constexpr Bitboard WHITE_OO_SAFE = (1ULL << SQ_F1) | (1ULL << SQ_G1);
273-
constexpr Bitboard WHITE_OOO_EMPTY = (1ULL << SQ_B1) | (1ULL << SQ_C1) | (1ULL << SQ_D1);
274-
constexpr Bitboard WHITE_OOO_SAFE = (1ULL << SQ_C1) | (1ULL << SQ_D1);
275-
276-
constexpr Bitboard BLACK_OO_EMPTY = (1ULL << SQ_F8) | (1ULL << SQ_G8);
277-
constexpr Bitboard BLACK_OO_SAFE = (1ULL << SQ_F8) | (1ULL << SQ_G8);
278-
constexpr Bitboard BLACK_OOO_EMPTY = (1ULL << SQ_B8) | (1ULL << SQ_C8) | (1ULL << SQ_D8);
279-
constexpr Bitboard BLACK_OOO_SAFE = (1ULL << SQ_C8) | (1ULL << SQ_D8);
280-
281270
Bitboard occupancy = pos.occ();
282271
Bitboard enemy_attacks = enemyAttacks;
283272

284273
if constexpr (c == WHITE) {
274+
Bitboard WHITE_OO_EMPTY = pos.getCastlingPath(WHITE, true);
275+
Bitboard WHITE_OO_SAFE = between(kingSq, castling_king_square(WHITE, true));
276+
Bitboard WHITE_OOO_EMPTY = pos.getCastlingPath(WHITE, false);
277+
Bitboard WHITE_OOO_SAFE = between(kingSq, castling_king_square(WHITE, false));
278+
285279
if ((pos.castlingRights() & WHITE_OO) && !(occupancy & WHITE_OO_EMPTY) && !(enemy_attacks & WHITE_OO_SAFE)) {
286-
out.push_back(Move::make<CASTLING>(SQ_E1, SQ_H1));
280+
out.push_back(Move::make<CASTLING>(kingSq, SQ_H1));
287281
}
288282
if ((pos.castlingRights() & WHITE_OOO) && !(occupancy & WHITE_OOO_EMPTY) && !(enemy_attacks & WHITE_OOO_SAFE)) {
289-
out.push_back(Move::make<CASTLING>(SQ_E1, SQ_A1));
283+
out.push_back(Move::make<CASTLING>(kingSq, SQ_A1));
290284
}
291285
} else {
286+
Bitboard BLACK_OO_EMPTY = pos.getCastlingPath(BLACK, true);
287+
Bitboard BLACK_OO_SAFE = between(kingSq, castling_king_square(BLACK, true));
288+
Bitboard BLACK_OOO_EMPTY = pos.getCastlingPath(BLACK, false);
289+
Bitboard BLACK_OOO_SAFE = between(kingSq, castling_king_square(BLACK, false));
290+
292291
if ((pos.castlingRights() & BLACK_OO) && !(occupancy & BLACK_OO_EMPTY) && !(enemy_attacks & BLACK_OO_SAFE)) {
293-
out.push_back(Move::make<CASTLING>(SQ_E8, SQ_H8));
292+
out.push_back(Move::make<CASTLING>(kingSq, SQ_H8));
294293
}
295294
if ((pos.castlingRights() & BLACK_OOO) && !(occupancy & BLACK_OOO_EMPTY) && !(enemy_attacks & BLACK_OOO_SAFE)) {
296-
out.push_back(Move::make<CASTLING>(SQ_E8, SQ_A8));
295+
out.push_back(Move::make<CASTLING>(kingSq, SQ_A8));
297296
}
298297
}
299298
}

position.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ template <typename PieceC, typename T> template <bool Strict> void _Position<Pie
101101
case CASTLING: {
102102
removePiece(target_piecetype, to_sq, target_color);
103103
bool is_king_side = from_sq < to_sq;
104-
Square rook_dest = relative_square(us, is_king_side ? SQ_F1 : Square::SQ_D1),
105-
king_dest = relative_square(us, is_king_side ? SQ_G1 : Square::SQ_C1);
104+
Square rook_dest = castling_rook_square(us, is_king_side),
105+
king_dest = castling_king_square(us, is_king_side);
106106
placePiece<ROOK>(rook_dest, us);
107107
placePiece<KING>(king_dest, us);
108108
current_state.incr_sqs[0] = from_sq;
@@ -629,7 +629,6 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::refresh_attack
629629
}
630630
template <typename PieceC, typename T> uint64_t _Position<PieceC, T>::zobrist() const {
631631
uint64_t hash = 0;
632-
#pragma unroll(64)
633632
for (int sq = 0; sq < 64; ++sq) {
634633
auto p = piece_on((Square)sq);
635634
hash ^= (p == PieceC::NO_PIECE) ? 0 : zobrist::RandomPiece[enum_idx<PieceC>()][(int)p][sq];
@@ -674,6 +673,7 @@ template <typename PieceC, typename T> Square _Position<PieceC, T>::_valid_ep_sq
674673
Bitboard mask = 1ULL << ep_square();
675674
Bitboard pawn_mask = mask << 8;
676675
Bitboard org_pawn_mask = mask >> 8;
676+
if (sideToMove() == BLACK) std::swap(pawn_mask, org_pawn_mask);
677677
// rank 3 or rank 6, depending on color
678678
if (rank_of(ep_square()) != ep_rank)
679679
return SQ_NONE;
@@ -787,3 +787,5 @@ INSTANTIATE(EnginePiece)
787787
INSTANTIATE(ContiguousMappingPiece)
788788
#undef INSTANTIATE
789789
} // namespace chess
790+
791+

position.h

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,11 @@ template <typename Piece> struct alignas(64) HistoryEntry {
1919
Color turn; // true if white to move
2020
Move mv;
2121
Key hash;
22-
struct {
23-
uint8_t halfMoveClock; // Half-move clock for 50/75-move rule
24-
uint16_t fullMoveNumber; // Full-move number (starts at 1)
25-
bool epIncluded;
26-
int8_t repetition = 0;
27-
uint8_t pliesFromNull = 0;
28-
};
22+
uint8_t halfMoveClock; // Half-move clock for 50/75-move rule
23+
uint16_t fullMoveNumber; // Full-move number (starts at 1)
24+
bool epIncluded;
25+
int8_t repetition = 0;
26+
uint8_t pliesFromNull = 0;
2927
Square enPassant = SQ_NONE; // En passant target square
3028
Square kings[COLOR_NB] = { SQ_NONE };
3129
CastlingRights castlingRights; // Castling rights bitmask
@@ -160,9 +158,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
160158

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

185187
public:
186188
// Legal move generation functions
@@ -393,7 +395,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
393395
}
394396

395397
[[nodiscard]] __FORCEINLINE Bitboard attackers(Color color, Square square) const { return attackers(color, square, occ()); }
396-
// Compile-time piece type and color, runtime square
398+
397399
template <PieceType pt> __FORCEINLINE void placePiece(Square sq, Color c) {
398400
if constexpr (pt != NO_PIECE_TYPE) {
399401
Bitboard v = 1ULL << sq;
@@ -448,6 +450,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
448450
}
449451
__FORCEINLINE Bitboard occ() const { return current_state.occ[0] | current_state.occ[1]; }
450452
PieceC piece_on(Square s) const {
453+
assert(chess::is_valid(s));
451454
#if !defined(_DEBUG) || defined(NDEBUG)
452455
return pieces_list[s];
453456
#else
@@ -470,8 +473,7 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
470473
assert(p == _p2 && "Inconsistient piece map");
471474
#else
472475
if (p != _p2)
473-
// throw std::invalid_argument("Inconsistient piece map");
474-
exit(-1);
476+
throw std::invalid_argument("Inconsistient piece map");
475477
#endif
476478
return p;
477479
#endif
@@ -504,7 +506,12 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
504506
__FORCEINLINE const HistoryEntry<PieceC> &state() const { return current_state; }
505507
uint64_t zobrist() const;
506508
__FORCEINLINE PieceC piece_at(Square sq) const { return piece_on(sq); }
507-
__FORCEINLINE PieceC at(Square sq) const { return piece_at(sq); }
509+
template <typename T = PieceC>
510+
__FORCEINLINE PieceC at(Square sq) const {
511+
assert(chess::is_valid(sq));
512+
if constexpr (std::is_same_v<T, PieceType>) return piece_of(piece_at(sq));
513+
else return piece_at(sq);
514+
}
508515
__FORCEINLINE Square enpassantSq() const { return ep_square(); }
509516
CastlingRights clean_castling_rights() const;
510517
void setFEN(const std::string &str);
@@ -524,6 +531,8 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
524531
__FORCEINLINE bool is_insufficient_material() const {
525532
return has_insufficient_material(WHITE) && has_insufficient_material(BLACK);
526533
}
534+
__FORCEINLINE bool isInsufficientMaterial() const { return is_insufficient_material(); }
535+
__FORCEINLINE bool hasNonPawnMaterial(Color c) const { return bool(us(c) ^ (pieces(PAWN, KING) & us(c))); }
527536
__FORCEINLINE bool inCheck() const { return checkers() != 0; }
528537
__FORCEINLINE bool is_check() const { return checkers() != 0; }
529538
__FORCEINLINE bool has_castling_rights(Color c) const { return castlingRights(c) != 0; }
@@ -583,14 +592,33 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
583592
}
584593
return b != 0;
585594
}
595+
__FORCEINLINE bool is_checkmate() const {
596+
Movelist moves;
597+
legals(moves);
598+
return inCheck() && !moves.size();
599+
}
600+
__FORCEINLINE bool is_stalemate() const {
601+
Movelist moves;
602+
legals(moves);
603+
return !inCheck() && !moves.size();
604+
}
586605
// Material-only key (note: Zobrist=Zpieces^Zep^Zcastling^Zturn, we just XORs the remaining, it's trivial)
587606
__FORCEINLINE Key material_key() const {
588607
return hash() ^ (zobrist::RandomTurn * ~sideToMove()) ^ (zobrist::RandomCastle[castlingRights()]) ^
589608
(zobrist::RandomEP[ep_square() == SQ_NONE ? file_of(ep_square()) : FILE_NB]);
590609
}
591610
template <bool Strict = false> bool is_valid() const;
592611
CheckType givesCheck(Move move) const;
593-
612+
/**
613+
* @brief Checks if the current position is a draw by 50 move rule.
614+
* Keep in mind that by the rules of chess, if the position has 50 half
615+
* moves it's not necessarily a draw, since checkmate has higher priority,
616+
* <del>call getHalfMoveDrawType,
617+
* to determine whether the position is a draw or checkmate.</del>
618+
* @return
619+
*/
620+
[[nodiscard]] __FORCEINLINE bool isHalfMoveDraw() const noexcept { return halfmoveClock() >= 100; }
621+
[[nodiscard]] __FORCEINLINE Bitboard getCastlingPath(Color c, bool isKingSide) const { return castling_path[c][isKingSide]; }
594622
private:
595623
template <PieceType pt> [[nodiscard]] __FORCEINLINE Bitboard pinMask(Color c, Square sq) const {
596624
static_assert(pt == BISHOP || pt == ROOK, "Only bishop or rook allowed!");
@@ -684,5 +712,23 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
684712
refresh_attacks();
685713
}
686714
};
687-
using Position = _Position<EnginePiece>; // for some fun because I HATE HARDCODING
715+
namespace attacks{
716+
/**
717+
* @brief Returns the attacks for a given piece on a given square
718+
* @param board
719+
* @param color
720+
* @param square
721+
* @return
722+
*/
723+
template <typename T, typename = std::enable_if_t<is_piece_enum<T>::value>>
724+
[[nodiscard]] __FORCEINLINE Bitboard attackers(const _Position<T> &board, Color color, Square square) noexcept {
725+
return board.attackers(color, square);
726+
}
727+
}
728+
// Aliases
729+
using Position = _Position<EnginePiece>;
730+
using Board = _Position<EnginePiece>;
688731
}; // namespace chess
732+
733+
734+

types.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,14 @@ enum Direction : int8_t {
123123
};
124124
// clang-format on
125125
inline constexpr Square relative_square(Color c, Square s) { return Square(s ^ (c * 56)); }
126+
inline constexpr Square castling_rook_square(Color c, bool is_king_side) { return relative_square(c, is_king_side ? SQ_F1 : Square::SQ_D1); }
127+
inline constexpr Square castling_king_square(Color c, bool is_king_side) { return relative_square(c, is_king_side ? SQ_G1 : Square::SQ_C1); }
126128

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

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

131133
inline constexpr Direction relative_direction(Color c, Direction d) { return static_cast<Direction>(c == WHITE ? d : -d); }
132-
133134
inline constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; }
134135

135136
#define ENABLE_INCR_OPERATORS_ON(T) \
@@ -437,3 +438,5 @@ constexpr PieceType parse_pt(unsigned char c) {
437438
}
438439

439440
} // namespace chess
441+
442+

0 commit comments

Comments
 (0)