Support validation of lichess 3check FENs
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Nov 2021 14:21:38 +0000 (15:21 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sat, 6 Nov 2021 15:31:58 +0000 (16:31 +0100)
Closes #288.

src/apiutil.h
test.py

index 243d8b3..c1e717d 100644 (file)
@@ -786,6 +786,23 @@ inline Validation check_check_count(const std::string& checkCountInfo) {
     return OK;
 }
 
+inline Validation check_lichess_check_count(const std::string& checkCountInfo) {
+    if (checkCountInfo.size() != 4)
+    {
+        std::cerr << "Invalid check count '" << checkCountInfo << "'. Expects 4 characters. Actual: " << checkCountInfo.size() << " character(s)." << std::endl;
+        return NOK;
+    }
+    if (!isdigit(checkCountInfo[1]) || checkCountInfo[1] - '0' > 3)
+    {
+        std::cerr << "Invalid check count '" << checkCountInfo << "'. Expects 2nd character to be a digit up to 3." << std::endl;
+        return NOK;
+    }
+    if (!isdigit(checkCountInfo[3]) || checkCountInfo[3] - '0' > 3) {
+        std::cerr << "Invalid check count '" << checkCountInfo << "'. Expects 4th character to be a digit up to 3." << std::endl;
+        return NOK;
+    }
+    return OK;
+}
 
 inline Validation check_digit_field(const std::string& field) {
     if (field.size() == 1 && field[0] == '-')
@@ -927,17 +944,25 @@ inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool
 
     // 5) Part
     // check check count
-    unsigned int optionalFields = 2 * !skipCastlingAndEp;
-    if (fenParts.size() >= 3 + optionalFields && v->checkCounting && fenParts.size() % 2)
+    unsigned int optionalInbetweenFields = 2 * !skipCastlingAndEp;
+    unsigned int optionalTrailingFields = 0;
+    if (fenParts.size() >= 3 + optionalInbetweenFields && v->checkCounting && fenParts.size() % 2)
     {
-        if (check_check_count(fenParts[2 + optionalFields]) == NOK)
-            return FEN_INVALID_CHECK_COUNT;
-        optionalFields++;
+        if (check_check_count(fenParts[2 + optionalInbetweenFields]) == NOK)
+        {
+            // allow valid lichess style check as alternative
+            if (fenParts.size() < 5 + optionalInbetweenFields || check_lichess_check_count(fenParts[fenParts.size() - 1]) == NOK)
+                return FEN_INVALID_CHECK_COUNT;
+            else
+                optionalTrailingFields++;
+        }
+        else
+            optionalInbetweenFields++;
     }
 
     // 6) Part
     // check half move counter
-    if (fenParts.size() >= 3 + optionalFields && !check_digit_field(fenParts[fenParts.size()-2]))
+    if (fenParts.size() >= 3 + optionalInbetweenFields && !check_digit_field(fenParts[fenParts.size() - 2 - optionalTrailingFields]))
     {
         std::cerr << "Invalid half move counter: '" << fenParts[fenParts.size()-2] << "'." << std::endl;
         return FEN_INVALID_HALF_MOVE_COUNTER;
@@ -945,7 +970,7 @@ inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool
 
     // 7) Part
     // check move counter
-    if (fenParts.size() >= 4 + optionalFields && !check_digit_field(fenParts[fenParts.size()-1]))
+    if (fenParts.size() >= 4 + optionalInbetweenFields && !check_digit_field(fenParts[fenParts.size() - 1 - optionalTrailingFields]))
     {
         std::cerr << "Invalid move counter: '" << fenParts[fenParts.size()-1] << "'." << std::endl;
         return FEN_INVALID_MOVE_COUNTER;
diff --git a/test.py b/test.py
index 7d8fc84..ff195a0 100644 (file)
--- a/test.py
+++ b/test.py
@@ -98,8 +98,12 @@ variant_positions = {
         "k7/p7/8/8/8/8/8/K7 w - - 0 1": (True, False),  # K vs KP
         "k7/q7/8/8/8/8/8/K7 w - - 0 1": (True, False),  # K vs KQ
     },
+    "crazyhouse": {
+        "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR/ w KQkq - 0 1": (False, False),  # lichess style startpos
+    },
     "3check": {
         "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 3+3 0 1": (False, False),  # startpos
+        "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 +0+2": (False, False),  # lichess style check count
         "k7/n7/8/8/8/8/8/K7 w - - 1+2 0 1": (True, False),  # K vs KN
         "k7/b7/8/8/8/8/8/K7 w - - 3+1 0 1": (True, False),  # K vs KB
     },
@@ -200,6 +204,8 @@ invalid_variant_positions = {
     ),
     "3check": (
         "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 3+a 0 1",  # invalid check count
+        "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 +a+2",  # invalid lichess check count
+        "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 +1+4",  # invalid lichess check count
     ),
     "horde": (
         "rnbqkbnr/pppppppp/8/1PP2PP1/PPPPPPPP/PPPPPPPP/PPPPPPPP/PPPPPPPK w kq - 0 1",  # wrong king count