From e3c028afbefd40cb3210210934a0c54a5cc3dcbe Mon Sep 17 00:00:00 2001 From: Fabian Fichter Date: Sat, 6 Nov 2021 13:59:42 +0100 Subject: [PATCH] Validate gating information in FEN (#288) --- src/apiutil.h | 47 ++++++++++++++++++++++++++++++----------------- test.py | 13 +++++++++---- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/apiutil.h b/src/apiutil.h index 1934276..243d8b3 100644 --- a/src/apiutil.h +++ b/src/apiutil.h @@ -436,13 +436,6 @@ public: squares.emplace_back(CharSquare(r, c)); return squares; } - /// Checks if a given character is on a given rank index - bool is_piece_on_rank(char piece, int rowIdx) const { - for (int f = 0; f < nbFiles; ++f) - if (get_piece(rowIdx, f) == piece) - return true; - return false; - } friend std::ostream& operator<<(std::ostream& os, const CharBoard& board); }; @@ -609,19 +602,39 @@ inline std::string castling_rights_to_string(CastlingRights castlingRights) { } } -inline Validation check_castling_rank(const std::array& castlingInfoSplitted, const CharBoard& board, const Variant* v) { +inline Validation check_castling_rank(const std::array& castlingInfoSplitted, const CharBoard& board, + const std::array& kingPositions, const Variant* v) { for (Color c : {WHITE, BLACK}) { - for (char charPiece : {v->pieceToChar[make_piece(c, v->castlingKingPiece)], - v->pieceToChar[make_piece(c, v->castlingRookPiece)]}) + const Rank castlingRank = relative_rank(c, v->castlingRank, v->maxRank); + char rookChar = v->pieceToChar[make_piece(c, v->castlingRookPiece)]; + for (char castlingFlag : castlingInfoSplitted[c]) { - if (castlingInfoSplitted[c].size() == 0) - continue; - const Rank castlingRank = relative_rank(c, v->castlingRank, v->maxRank); - if (!board.is_piece_on_rank(charPiece, castlingRank)) + if (tolower(castlingFlag) == 'k' || tolower(castlingFlag) == 'q') + { + if (kingPositions[c].rowIdx != castlingRank) + { + std::cerr << "The " << color_to_string(c) << " king must be on rank " << castlingRank << " if castling is enabled for " << color_to_string(c) << "." << std::endl; + return NOK; + } + bool kingside = tolower(castlingFlag) == 'k'; + bool castlingRook = false; + for (int f = kingside ? board.get_nb_files() - 1 : 0; f != kingPositions[c].fileIdx; kingside ? f-- : f++) + if (board.get_piece(castlingRank, f) == rookChar) + { + castlingRook = true; + break; + } + if (!castlingRook) + { + std::cerr << "No castling rook for flag " << castlingFlag << std::endl; + return NOK; + } + } + else if (board.get_piece(castlingRank, tolower(castlingFlag) - 'a') == ' ') { - std::cerr << "The " << color_to_string(c) << " king and rook must be on rank " << castlingRank << " if castling is enabled for " << color_to_string(c) << "." << std::endl; + std::cerr << "No gating piece for flag " << castlingFlag << std::endl; return NOK; } } @@ -879,8 +892,8 @@ inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool CharBoard startBoard(board.get_nb_ranks(), board.get_nb_files()); fill_char_board(startBoard, v->startFen, validSpecialCharacters, v); - // skip check for gating variants to avoid confusion with gating squares - if (!v->gating && check_castling_rank(castlingInfoSplitted, board, v) == NOK) + // Check pieces present on castling rank against castling/gating rights + if (check_castling_rank(castlingInfoSplitted, board, kingPositions, v) == NOK) return FEN_INVALID_CASTLING_INFO; // only check exact squares if starting position of castling pieces is known diff --git a/test.py b/test.py index a260dfe..7d8fc84 100644 --- a/test.py +++ b/test.py @@ -189,10 +189,11 @@ invalid_variant_positions = { "rnbqkbnr/pppppppp/7/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # invalid file count "rnbqkbnr/pppppppp/9/7/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # invalid file count "rnbqkbnr/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # invalid rank count - "1nbqkbn1/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # missing castling rook - "rnbqkbnr/pppppppp/8/8/4K3/PPPPPPPP/RNBQ1BNR w KQkq - 0 1", # king not on castling rank - "rnbqkbnr/pppppppp/8/8/RNBQKBNR/PPPPPPPP/8 w KQkq - 0 1", # not on castling rank - "8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # not on castling rank + "rnbqkbn1/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # missing castling rook + "1nbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # missing castling rook + "rnbqkbnr/pppppppp/8/8/8/4K3/PPPPPPPP/RNBQ1BNR w KQkq - 0 1", # king not on castling rank + "rnbqkbnr/pppppppp/8/8/8/RNBQKBNR/PPPPPPPP/8 w KQkq - 0 1", # not on castling rank + "8/pppppppp/rnbqkbnr/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", # not on castling rank ), "atomic": ( "rnbqkbnr/pppppppp/8/8/8/RNBQKBNR/PPPPPPPP/8 w KQkq - 0 1", # wrong castling rank @@ -209,6 +210,10 @@ invalid_variant_positions = { ), "shako": { "c8c/ernbqkbnre/pppppppppp/10/10/10/10/PPPPPPPPPP/C8C/ERNBQKBNRE w KQkq - 0 1", # not on castling rank + }, + "seirawan": { + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQK1NR[HEhe] w KQBCDFGkqbcdfg - 0 1", # white gating flag + "rnbqkb1r/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[HEhe] w KQBCDFGkqbcdfg - 0 1", # black gating flag } } -- 1.7.0.4