Validate gating information in FEN (#288)
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Nov 2021 12:59:42 +0000 (13:59 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Nov 2021 14:24:18 +0000 (15:24 +0100)
src/apiutil.h
test.py

index 1934276..243d8b3 100644 (file)
@@ -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<std::string, 2>& castlingInfoSplitted, const CharBoard& board, const Variant* v) {
+inline Validation check_castling_rank(const std::array<std::string, 2>& castlingInfoSplitted, const CharBoard& board,
+                            const std::array<CharSquare, 2>& 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 (file)
--- 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
     }
 }