Fix shako castling when rook in corner (#617)
authorFabian Fichter <ianfab@users.noreply.github.com>
Sat, 1 Apr 2023 10:40:24 +0000 (12:40 +0200)
committerGitHub <noreply@github.com>
Sat, 1 Apr 2023 10:40:24 +0000 (12:40 +0200)
src/parser.cpp
src/position.cpp
src/variant.cpp
src/variant.h
src/variants.ini
test.py
tests/perft.sh

index a4142cd..dbde7ed 100644 (file)
@@ -378,6 +378,8 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("castlingKingPiece", v->castlingKingPiece[BLACK], v->pieceToChar);
     parse_attribute("castlingKingPieceWhite", v->castlingKingPiece[WHITE], v->pieceToChar);
     parse_attribute("castlingKingPieceBlack", v->castlingKingPiece[BLACK], v->pieceToChar);
+    parse_attribute("castlingRookKingsideFile", v->castlingRookKingsideFile);
+    parse_attribute("castlingRookQueensideFile", v->castlingRookQueensideFile);
     parse_attribute("castlingRookPieces", v->castlingRookPieces[WHITE], v->pieceToChar);
     parse_attribute("castlingRookPieces", v->castlingRookPieces[BLACK], v->pieceToChar);
     parse_attribute("castlingRookPiecesWhite", v->castlingRookPieces[WHITE], v->pieceToChar);
index d9e798f..6053429 100644 (file)
@@ -349,10 +349,10 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960,
           token = char(toupper(token));
 
           if (token == 'K')
-              for (rsq = make_square(FILE_MAX, castling_rank(c)); !(castling_rook_pieces(c) & type_of(piece_on(rsq))) || color_of(piece_on(rsq)) != c; --rsq) {}
+              for (rsq = make_square(var->castlingRookKingsideFile, castling_rank(c)); !(castling_rook_pieces(c) & type_of(piece_on(rsq))) || color_of(piece_on(rsq)) != c; --rsq) {}
 
           else if (token == 'Q')
-              for (rsq = make_square(FILE_A, castling_rank(c)); !(castling_rook_pieces(c) & type_of(piece_on(rsq))) || color_of(piece_on(rsq)) != c; ++rsq) {}
+              for (rsq = make_square(var->castlingRookQueensideFile, castling_rank(c)); !(castling_rook_pieces(c) & type_of(piece_on(rsq))) || color_of(piece_on(rsq)) != c; ++rsq) {}
 
           else if (token >= 'A' && token <= 'A' + max_file())
               rsq = make_square(File(token - 'A'), castling_rank(c));
index 9bdc17e..265afe8 100644 (file)
@@ -1579,6 +1579,8 @@ namespace {
         v->promotionRegion[BLACK] = Rank1BB;
         v->castlingKingsideFile = FILE_H;
         v->castlingQueensideFile = FILE_D;
+        v->castlingRookKingsideFile = FILE_I;
+        v->castlingRookQueensideFile = FILE_B;
         v->castlingRank = RANK_2;
         v->doubleStepRegion[WHITE] = Rank3BB;
         v->doubleStepRegion[BLACK] = Rank8BB;
index d316779..e1a460a 100644 (file)
@@ -77,6 +77,8 @@ struct Variant {
   Rank castlingRank = RANK_1;
   File castlingKingFile = FILE_E;
   PieceType castlingKingPiece[COLOR_NB] = {KING, KING};
+  File castlingRookKingsideFile = FILE_MAX; // only has to match if rook is not in corner in non-960 variants
+  File castlingRookQueensideFile = FILE_A; // only has to match if rook is not in corner in non-960 variants
   PieceSet castlingRookPieces[COLOR_NB] = {piece_set(ROOK), piece_set(ROOK)};
   PieceType kingType = KING;
   bool checking = true;
index 409d424..85be25b 100644 (file)
 # castlingRank: relative rank of castling [Rank] (default: 1)
 # castlingKingFile: starting file of the castlingKingPiece if there can be more than one of that type [File] (default: e)
 # castlingKingPiece: first piece type that participates in castling [PieceType] (default: k)
+# castlingRookKingsideFile: starting file of castlinRookPieces on kingside (if not in corner) [File] (default: l)
+# castlingRookQueensideFile: starting file of castlinRookPieces on queenside (if not in corner) [File] (default: a)
 # castlingRookPieces: second piece type that participates in castling [PieceSet] (default: r)
 # checking: allow checks [bool] (default: true)
 # dropChecks: allow checks by piece drops [bool] (default: true)
diff --git a/test.py b/test.py
index f1c4cdc..3ffd61a 100644 (file)
--- a/test.py
+++ b/test.py
@@ -312,6 +312,11 @@ class TestPyffish(unittest.TestCase):
         self.assertIn("d6d7m", result)
         self.assertNotIn("d6d7", result)
 
+        # Test configurable piece perft
+        legals = ['a3a4', 'b3b4', 'c3c4', 'd3d4', 'e3e4', 'f3f4', 'g3g4', 'e1e2', 'f1f2', 'b1a2', 'b1b2', 'b1c2', 'c1b2', 'c1c2', 'c1d2', 'a1a2', 'g1g2', 'd1c2', 'd1d2', 'd1e2']
+        result = sf.legal_moves("yarishogi", sf.start_fen("yarishogi"), [])
+        self.assertCountEqual(legals, result)
+
     def test_short_castling(self):
         legals = ['f5f4', 'a7a6', 'b7b6', 'c7c6', 'd7d6', 'e7e6', 'i7i6', 'j7j6', 'a7a5', 'b7b5', 'c7c5', 'e7e5', 'i7i5', 'j7j5', 'b8a6', 'b8c6', 'h6g4', 'h6i4', 'h6j5', 'h6f7', 'h6g8', 'h6i8', 'd5a2', 'd5b3', 'd5f3', 'd5c4', 'd5e4', 'd5c6', 'd5e6', 'd5f7', 'd5g8', 'j8g8', 'j8h8', 'j8i8', 'e8f7', 'c8b6', 'c8d6', 'g6g2', 'g6g3', 'g6f4', 'g6g4', 'g6h4', 'g6e5', 'g6g5', 'g6i5', 'g6a6', 'g6b6', 'g6c6', 'g6d6', 'g6e6', 'g6f6', 'g6h8', 'f8f7', 'f8g8', 'f8i8']
         moves = ['b2b4', 'f7f5', 'c2c3', 'g8d5', 'a2a4', 'h8g6', 'f2f3', 'i8h6', 'h2h3']
@@ -332,10 +337,12 @@ class TestPyffish(unittest.TestCase):
         result = sf.legal_moves("diana", "rbnk1r/pppbpp/3p2/5P/PPPPPB/RBNK1R w KQkq - 2 3", [])
         self.assertIn("d1f1", result)
 
-        # Test configurable piece perft
-        legals = ['a3a4', 'b3b4', 'c3c4', 'd3d4', 'e3e4', 'f3f4', 'g3g4', 'e1e2', 'f1f2', 'b1a2', 'b1b2', 'b1c2', 'c1b2', 'c1c2', 'c1d2', 'a1a2', 'g1g2', 'd1c2', 'd1d2', 'd1e2']
-        result = sf.legal_moves("yarishogi", sf.start_fen("yarishogi"), [])
-        self.assertCountEqual(legals, result)
+        # Check that in variants where castling rooks are not in the corner
+        # the castling rook is nevertheless assigned correctly
+        result = sf.legal_moves("shako", "c8c/ernbqkbnre/pppppppppp/10/10/10/10/PPPPPPPPPP/5K2RR/10 w Kkq - 0 1", [])
+        self.assertIn("f2h2", result)
+        result = sf.legal_moves("shako", "c8c/ernbqkbnre/pppppppppp/10/10/10/10/PPPPPPPPPP/RR3K4/10 w Qkq - 0 1", [])
+        self.assertIn("f2d2", result)
 
     def test_get_fen(self):
         result = sf.get_fen("chess", CHESS, [])
index ec08d5f..92d6ebb 100755 (executable)
@@ -172,6 +172,7 @@ if [[ $1 == "all" ||  $1 == "largeboard" ]]; then
   expect perft.exp shako "fen 4kc3c/ernbq1b1re/ppp3p1pp/3p2pp2/4p5/5P4/2PN2P3/PP1PP2PPP/ER1BQKBNR1/5C3C w KQ - 0 9" 3 26325 > /dev/null
   expect perft.exp shako "fen 4ncr1k1/1cr2P4/pp2p2pp1/P7PN/2Ep1p4/B3P1eN2/2P1n1P3/1B1P1K4/9p/5C2CR w - - 0 1" 3 180467 > /dev/null
   expect perft.exp shako "fen r5k3/4q2c2/1ebppnp3/1pp3BeEQ/10/2PE2P3/1P3P4/5NP2P/rR3KB3/7C2 w Q - 3 35" 2 4940 > /dev/null
+  expect perft.exp shako "fen 10/rr3k4/ppppp5/10/10/10/10/6PPPP/5K2RR/10 w Kq - 0 1" 2 460 > /dev/null
   expect perft.exp xiangqi startpos 4 3290240 > /dev/null
   expect perft.exp xiangqi "fen 1rbaka2R/5r3/6n2/2p1p1p2/4P1bP1/PpC3Bc1/1nPR2P2/2N2AN2/1c2K1p2/2BAC4 w - - 0 1" 4 4485547 > /dev/null
   expect perft.exp xiangqi "fen 4kcP1N/8n/3rb4/9/9/9/9/3p1A3/4K4/5CB2 w - - 0 1" 4 92741 > /dev/null