Skip to content

Commit d020de1

Browse files
Multiple castling field parsers (#39)
* implemented (non-)permissive FEN parsers * added MSan because tests kept failing for no reason * use correct FEN format in test * bugfix wrong square --------- Co-authored-by: GitHub Actions <actions@github.com>
1 parent 2c0ce79 commit d020de1

6 files changed

Lines changed: 88 additions & 24 deletions

File tree

.github/workflows/test.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,18 @@ jobs:
5151
- name: Configure CMake
5252
shell: bash
5353
run: |
54+
if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
55+
if "${{ matrix.cpp_compiler }}" == "clang++" ]]; then
56+
SANITIZERS="memory,undefined"
57+
else
58+
SANITIZERS="address,undefined"
59+
fi
60+
fi
5461
cmake -B "${{ steps.vars.outputs.dir }}" \
5562
-DCMAKE_C_COMPILER=${{ matrix.c_compiler }} \
5663
-DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} \
5764
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
58-
-DSANITIZERS="address,undefined" \
65+
-DSANITIZERS=${SANITIZERS} \
5966
-DDART_TESTING_TIMEOUT=0 \
6067
-S "${{ github.workspace }}"
6168

.github/workflows/try_compile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: Compilation
22

33
on:
4-
pull_request:
4+
push:
55

66
jobs:
77
build:

movegen.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,39 @@
44
namespace chess {
55

66
namespace _chess {
7-
#if defined(__AVX512BW__)
7+
8+
#if defined(USE_AVX512ICL)
9+
10+
// clang-format off
11+
const __m512i AllSquares = _mm512_set_epi8(
12+
63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41,
13+
40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
14+
17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
15+
// clang-format on
16+
17+
template <Direction offset> inline Move *splat_pawn_moves(Move *moveList, Bitboard to_bb) {
18+
assert(popcount(to_bb) <= 8); // <= 8 pawns per side
19+
20+
const __m128i toSquares = _mm_cvtepi8_epi16(_mm512_castsi512_si128(_mm512_maskz_compress_epi8(to_bb, AllSquares)));
21+
const __m128i fromSquares = _mm_subs_epi16(toSquares, _mm_set1_epi16(offset));
22+
const __m128i moves = _mm_or_si128(_mm_slli_epi16(fromSquares, 6), _mm_slli_epi16(toSquares, 0));
23+
24+
_mm_storeu_si128(reinterpret_cast<__m128i *>(moveList), moves);
25+
return moveList + popcount(to_bb);
26+
}
27+
28+
inline Move *splat_moves(Move *moveList, Square from, Bitboard to_bb) {
29+
assert(popcount(to_bb) <= 32); // Q can attack up to 27 squares
30+
31+
const __m512i fromVec = _mm512_set1_epi16(Move(from, SQUARE_ZERO).raw());
32+
const __m512i toSquares = _mm512_cvtepi8_epi16(_mm512_castsi512_si256(_mm512_maskz_compress_epi8(to_bb, AllSquares)));
33+
const __m512i moves = _mm512_or_si512(fromVec, _mm512_slli_epi16(toSquares, Move::ToSqShift));
34+
35+
_mm512_storeu_si512(moveList, moves);
36+
return moveList + popcount(to_bb);
37+
}
38+
39+
#elif defined(__AVX512BW__)
840
template <int Offset = 0> struct alignas(64) SplatTable {
941
std::array<uint16_t, 64> data;
1042
constexpr int clamp64(int x) { return (x < 0) ? 0 : (x > 63 ? 63 : x); }
@@ -213,7 +245,7 @@ void movegen::genPawnSingleMoves(
213245
}
214246
template <typename T, Color c, bool capturesOnly>
215247
void movegen::genKnightMoves(const _Position<T, void> &pos, Movelist &list, Bitboard _pin_mask, Bitboard _check_mask) {
216-
Bitboard knights = pos.template pieces<KNIGHT, c>() & ~_pin_mask; // yes, unconditionally.
248+
Bitboard knights = pos.template pieces<KNIGHT, c>() & ~_pin_mask;
217249
while (knights) {
218250
Square x = static_cast<Square>(pop_lsb(knights));
219251
Bitboard moves = attacks::knight(x) & ~pos.occ(c);
@@ -253,7 +285,6 @@ void movegen::genKingMoves(const _Position<T, void> &pos, Movelist &out, Bitboar
253285
// Enemy king (adjacent control squares)
254286
enemyAttacks |= attacks::king(pos.kingSq(them));
255287

256-
// Candidate king moves = legal squares not attacked by enemy
257288
Bitboard moves = attacks::king(kingSq) & ~myOcc & ~enemyAttacks;
258289
if constexpr (capturesOnly)
259290
moves &= pos.occ(~c);
@@ -289,8 +320,7 @@ void movegen::genSlidingMoves(
289320
static_assert(pt == BISHOP || pt == ROOK || pt == QUEEN, "Sliding pieces only.");
290321
Bitboard sliders = pos.template pieces<pt, c>();
291322
Bitboard occ_all = pos.occ();
292-
// Square king_sq = current_state.kings[c];
293-
Bitboard rook_pinners = _rook_pin; // bitboard of enemy rooks/queens pinning
323+
Bitboard rook_pinners = _rook_pin;
294324
Bitboard bishop_pinners = _bishop_pin;
295325
if constexpr (pt == BISHOP)
296326
sliders &= ~rook_pinners;
@@ -305,7 +335,6 @@ void movegen::genSlidingMoves(
305335
Bitboard bishop_hit = bishop_pinners & from_bb;
306336
Bitboard pin_mask = rook_hit ? rook_pinners : bishop_hit ? bishop_pinners : ~0ULL;
307337

308-
// Bitboard blockers = occ() ^ from_bb; // remove piece temporarily
309338
auto func = attacks::queen;
310339
if constexpr (pt == BISHOP)
311340
func = attacks::bishop;

non_core_tests.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -893,9 +893,13 @@ TEST_SUITE("SAN Parser") {
893893
}
894894

895895
TEST_SUITE("misc tests") {
896-
TEST_CASE("FEN reconstruction") {
896+
TEST_CASE("FEN reconstruction (Chess960)") {
897897
Position pos(Position::START_CHESS960_FEN, true);
898-
REQUIRE(pos.fen() == Position::START_CHESS960_FEN);
898+
REQUIRE(pos.fen(false) == Position::START_CHESS960_FEN);
899+
REQUIRE(pos.fen() == Position::START_FEN);
900+
pos.setFEN(Position::START_CHESS960_FEN, true, chess::MODE_SMK);
901+
REQUIRE(pos.fen(false) == Position::START_CHESS960_FEN);
902+
REQUIRE(pos.fen() == Position::START_FEN);
899903
}
900904
}
901905
int main(int argc, char **argv) {

position.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ template <typename PieceC, typename T> template <bool Strict> void _Position<Pie
197197
}
198198
}
199199

200-
template <typename PieceC, typename T> void _Position<PieceC, T>::setFEN(const std::string &str, bool chess960) {
200+
template <typename PieceC, typename T>
201+
void _Position<PieceC, T>::setFEN(const std::string &str, bool chess960, FENParsingMode mode) {
201202
current_state = HistoryEntry<PieceC>();
202203
history.clear();
203204
_chess960 = chess960;
@@ -329,6 +330,8 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::setFEN(const s
329330
}
330331
return SQ_NONE;
331332
};
333+
bool allow_xfen = (mode == MODE_XFEN || mode == MODE_AUTO);
334+
bool allow_smk = (mode == MODE_SMK || mode == MODE_AUTO);
332335
auto apply = [&](char c) {
333336
Square king_sq = findKing();
334337
if (king_sq == SQ_NONE)
@@ -368,24 +371,28 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::setFEN(const s
368371
};
369372

370373
if (c == 'K' && color == WHITE) {
374+
INVALID_ARG_IF(chess960 && !allow_xfen, "shredder fen into xfen parser");
371375
if (rook_ks == SQ_NONE)
372376
return;
373377
if (rank_of(king_sq) != rank_of(rook_ks))
374378
return;
375379
setKS(rook_ks);
376380
} else if (c == 'Q' && color == WHITE) {
381+
INVALID_ARG_IF(chess960 && !allow_xfen, "shredder fen into xfen parser");
377382
if (rook_qs == SQ_NONE)
378383
return;
379384
if (rank_of(king_sq) != rank_of(rook_qs))
380385
return;
381386
setQS(rook_qs);
382387
} else if (c == 'k' && color == BLACK) {
388+
INVALID_ARG_IF(chess960 && !allow_xfen, "shredder fen into xfen parser");
383389
if (rook_ks == SQ_NONE)
384390
return;
385391
if (rank_of(king_sq) != rank_of(rook_ks))
386392
return;
387393
setKS(rook_ks);
388394
} else if (c == 'q' && color == BLACK) {
395+
INVALID_ARG_IF(chess960 && !allow_xfen, "shredder fen into xfen parser");
389396
if (rook_qs == SQ_NONE)
390397
return;
391398
if (rank_of(king_sq) != rank_of(rook_qs))
@@ -394,6 +401,7 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::setFEN(const s
394401
}
395402

396403
else if (c >= 'A' && c <= 'H' && color == WHITE) {
404+
INVALID_ARG_IF(chess960 && !allow_smk, "xfen into shredder fen parser");
397405
File f = static_cast<File>(c - 'A');
398406
Square rook_sq = make_sq(RANK_1, f);
399407

@@ -403,6 +411,7 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::setFEN(const s
403411

404412
(f > file_of(king_sq)) ? setKS(rook_sq) : setQS(rook_sq);
405413
} else if (c >= 'a' && c <= 'h' && color == BLACK) {
414+
INVALID_ARG_IF(chess960 && !allow_smk, "xfen into shredder fen parser");
406415
File f = static_cast<File>(c - 'a');
407416
Square rook_sq = make_sq(RANK_8, f);
408417

@@ -474,7 +483,7 @@ template <typename PieceC, typename T> void _Position<PieceC, T>::setFEN(const s
474483
current_state.repetition = current_state.pliesFromNull = 0;
475484
}
476485

477-
template <typename PieceC, typename T> std::string _Position<PieceC, T>::fen() const {
486+
template <typename PieceC, typename T> std::string _Position<PieceC, T>::fen(bool xfen) const {
478487
std::ostringstream ss;
479488

480489
// 1) Piece placement
@@ -508,13 +517,21 @@ template <typename PieceC, typename T> std::string _Position<PieceC, T>::fen() c
508517
std::string castlingStr;
509518
if (chess960()) {
510519
if (castlingRights() & WHITE_OO)
511-
castlingStr += static_cast<char>('A' + file_of(current_state.castlingMetadata[WHITE].rook_start_ks));
520+
castlingStr += (xfen && current_state.castlingMetadata[WHITE].rook_start_ks == SQ_H1)
521+
? 'K'
522+
: static_cast<char>('A' + file_of(current_state.castlingMetadata[WHITE].rook_start_ks));
512523
if (castlingRights() & WHITE_OOO)
513-
castlingStr += static_cast<char>('A' + file_of(current_state.castlingMetadata[WHITE].rook_start_qs));
524+
castlingStr += (xfen && current_state.castlingMetadata[WHITE].rook_start_qs == SQ_A1)
525+
? 'Q'
526+
: static_cast<char>('A' + file_of(current_state.castlingMetadata[WHITE].rook_start_qs));
514527
if (castlingRights() & BLACK_OO)
515-
castlingStr += static_cast<char>('a' + file_of(current_state.castlingMetadata[BLACK].rook_start_ks));
528+
castlingStr += (xfen && current_state.castlingMetadata[BLACK].rook_start_ks == SQ_H8)
529+
? 'k'
530+
: static_cast<char>('a' + file_of(current_state.castlingMetadata[BLACK].rook_start_ks));
516531
if (castlingRights() & BLACK_OOO)
517-
castlingStr += static_cast<char>('a' + file_of(current_state.castlingMetadata[BLACK].rook_start_qs));
532+
castlingStr += (xfen && current_state.castlingMetadata[BLACK].rook_start_qs == SQ_A8)
533+
? 'q'
534+
: static_cast<char>('a' + file_of(current_state.castlingMetadata[BLACK].rook_start_qs));
518535
} else {
519536
if (castlingRights() & WHITE_OO)
520537
castlingStr += 'K';
@@ -858,8 +875,8 @@ template <typename PieceC, typename T> CastlingRights _Position<PieceC, T>::clea
858875
}
859876
// clang-format off
860877
#define INSTANTIATE(PieceC) \
861-
template void _Position<PieceC, void>::setFEN(const std::string &, bool); \
862-
template std::string _Position<PieceC, void>::fen() const; \
878+
template void _Position<PieceC, void>::setFEN(const std::string &, bool, FENParsingMode); \
879+
template std::string _Position<PieceC, void>::fen(bool) const; \
863880
template void _Position<PieceC, void>::doMove<false>(const Move &move); \
864881
template void _Position<PieceC, void>::doMove<true>(const Move &move); \
865882
template void _Position<PieceC, void>::refresh_attacks(); \

position.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ template <typename Piece> struct alignas(64) HistoryEntry {
3636
};
3737

3838
enum class CheckType { NO_CHECK, DIRECT_CHECK, DISCOVERY_CHECK };
39+
40+
enum FENParsingMode { MODE_XFEN, MODE_SMK, MODE_AUTO };
41+
3942
enum class MoveGenType : uint16_t {
4043
NONE = 0,
4144

@@ -396,16 +399,16 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
396399
[[nodiscard]] inline Square kingSq(Color c) const { return current_state.kings[c]; }
397400
[[nodiscard]] inline Bitboard checkers() const { return _checkers; }
398401
[[nodiscard]] inline Bitboard pin_mask() const { return _pin_mask; }
399-
inline _Position(std::string fen = START_FEN, bool chess960 = false) {
402+
inline _Position(std::string fen = START_FEN, bool chess960 = false, FENParsingMode xfen = MODE_AUTO) {
400403
history.reserve(6144);
401-
setFEN(fen, chess960);
404+
setFEN(fen, chess960, xfen);
402405
}
403406
[[nodiscard]] inline bool isCapture(Move mv) const {
404407
return mv.type_of() == EN_PASSANT || (mv.type_of() != CASTLING && piece_on(mv.to_sq()) != PieceC::NO_PIECE);
405408
}
406409
[[nodiscard]] inline bool is_capture(Move mv) const { return isCapture(mv); }
407410
[[nodiscard]] inline bool is_zeroing(Move mv) const { return isCapture(mv) || at<PieceType>(mv.from_sq()) == PAWN; }
408-
[[nodiscard]] std::string fen() const;
411+
[[nodiscard]] std::string fen(bool xfen = true) const;
409412
[[nodiscard]] inline uint8_t halfmoveClock() const { return current_state.halfMoveClock; }
410413
[[nodiscard]] inline uint16_t fullmoveNumber() const { return current_state.fullMoveNumber; }
411414
[[nodiscard]] inline uint8_t rule50_count() const { return current_state.halfMoveClock; }
@@ -428,9 +431,13 @@ template <typename PieceC = EnginePiece, typename = std::enable_if_t<is_piece_en
428431
}
429432
inline Square enpassantSq() const { return ep_square(); }
430433
CastlingRights clean_castling_rights() const;
431-
void setFEN(const std::string &str, bool chess960 = false);
432-
inline void set_fen(const std::string &str, bool chess960 = false) { setFEN(str, chess960); }
433-
inline void setFen(const std::string &str, bool chess960 = false) { setFEN(str, chess960); }
434+
void setFEN(const std::string &str, bool chess960 = false, FENParsingMode xfen = MODE_AUTO);
435+
inline void set_fen(const std::string &str, bool chess960 = false, FENParsingMode xfen = MODE_AUTO) {
436+
setFEN(str, chess960, xfen);
437+
}
438+
inline void setFen(const std::string &str, bool chess960 = false, FENParsingMode xfen = MODE_AUTO) {
439+
setFEN(str, chess960, xfen);
440+
}
434441
Move parse_uci(std::string) const;
435442
Move push_uci(std::string);
436443
Square _valid_ep_square() const;

0 commit comments

Comments
 (0)