Support petrified (#713)
authorFabian Fichter <ianfab@users.noreply.github.com>
Fri, 15 Sep 2023 17:26:24 +0000 (19:26 +0200)
committerGitHub <noreply@github.com>
Fri, 15 Sep 2023 17:26:24 +0000 (19:26 +0200)
src/parser.cpp
src/position.cpp
src/variant.cpp
src/variant.h
src/variants.ini

index f222a6b..4871446 100644 (file)
@@ -383,7 +383,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("blastOnCapture", v->blastOnCapture);
     parse_attribute("blastImmuneTypes", v->blastImmuneTypes, v->pieceToChar);
     parse_attribute("mutuallyImmuneTypes", v->mutuallyImmuneTypes, v->pieceToChar);
-    parse_attribute("petrifyOnCapture", v->petrifyOnCapture);
+    parse_attribute("petrifyOnCaptureTypes", v->petrifyOnCaptureTypes, v->pieceToChar);
     parse_attribute("petrifyBlastPieces", v->petrifyBlastPieces);
     parse_attribute("doubleStep", v->doubleStep);
     parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]);
index a55d50b..7091285 100644 (file)
@@ -1129,6 +1129,9 @@ bool Position::legal(Move m) const {
           occupied &= ~square_bb(capture_square(kto));
       if (capture(m) && blast_on_capture())
           occupied &= ~((attacks_bb<KING>(kto) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | kto);
+      // Petrifying a pseudo-royal piece is illegal
+      if (capture(m) && (var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && (st->pseudoRoyals & from))
+          return false;
       Bitboard pseudoRoyals = st->pseudoRoyals & pieces(sideToMove);
       Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(~sideToMove);
       if (is_ok(from) && (pseudoRoyals & from))
@@ -1145,6 +1148,10 @@ bool Position::legal(Move m) const {
       // Self-explosions are illegal
       if (pseudoRoyals & ~occupied)
           return false;
+      // Petrifiable pseudo-royals can't capture
+      Bitboard attackerCandidatesTheirs = occupied & ~square_bb(kto);
+      for (PieceSet ps = var->petrifyOnCaptureTypes & extinction_piece_types(); ps;)
+          attackerCandidatesTheirs &= ~pieces(~us, pop_lsb(ps));
       // Check for legality unless we capture a pseudo-royal piece
       if (!(pseudoRoyalsTheirs & ~occupied))
           while (pseudoRoyals)
@@ -1152,7 +1159,7 @@ bool Position::legal(Move m) const {
               Square sr = pop_lsb(pseudoRoyals);
               // Touching pseudo-royal pieces are immune
               if (  !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
-                  && (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto))))
+                  && (attackers_to(sr, occupied, ~us) & attackerCandidatesTheirs))
                   return false;
           }
       // Look for duple check
@@ -1169,7 +1176,7 @@ bool Position::legal(Move m) const {
               Square sr = pop_lsb(pseudoRoyalCandidates);
               // Touching pseudo-royal pieces are immune
               if (!(  !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
-                    && (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto)))))
+                    && (attackers_to(sr, occupied, ~us) & attackerCandidatesTheirs)))
                   allCheck = false;
           }
           if (allCheck)
@@ -1177,10 +1184,6 @@ bool Position::legal(Move m) const {
       }
   }
 
-  // Petrifying the king is illegal
-  if (var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) == KING)
-      return false;
-
   // mutuallyImmuneTypes (diplomacy in Atomar)-- In no-check Atomic, kings can be beside each other, but in Atomar, this prevents them from actually taking.
   // Generalized to allow a custom set of pieces that can't capture a piece of the same type.
   if (capture(m) &&
@@ -1405,7 +1408,7 @@ bool Position::gives_check(Move m) const {
 
   // Is there a direct check?
   if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && type_of(m) != PIECE_DEMOTION && type_of(m) != CASTLING
-      && !(var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) != PAWN))
+      && !((var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && capture(m)))
   {
       PieceType pt = type_of(moved_piece(m));
       if (pt == JANGGI_CANNON)
@@ -1433,7 +1436,7 @@ bool Position::gives_check(Move m) const {
       return true;
 
   // Petrified piece can't give check
-  if (var->petrifyOnCapture && capture(m) && type_of(moved_piece(m)) != PAWN)
+  if ((var->petrifyOnCaptureTypes & type_of(moved_piece(m))) && capture(m))
       return false;
 
   // Is there a check by special diagonal moves?
@@ -1946,7 +1949,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
 
 
   // Remove the blast pieces
-  if (captured && (blast_on_capture() || var->petrifyOnCapture))
+  if (captured && (blast_on_capture() || var->petrifyOnCaptureTypes))
   {
       std::memset(st->unpromotedBycatch, 0, sizeof(st->unpromotedBycatch));
       st->demotedBycatch = st->promotedBycatch = 0;
@@ -1956,7 +1959,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
           blastImmune |= pieces(pt);
       };
       Bitboard blast = blast_on_capture() ? ((attacks_bb<KING>(to) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | to)
-                       & (pieces() ^ blastImmune) : type_of(pc) != PAWN ? square_bb(to) : Bitboard(0);
+                       & (pieces() ^ blastImmune) : var->petrifyOnCaptureTypes & type_of(pc) ? square_bb(to) : Bitboard(0);
       while (blast)
       {
           Square bsq = pop_lsb(blast);
@@ -2018,7 +2021,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
           }
 
           // Make a wall square where the piece was
-          if (bsq == to ? var->petrifyOnCapture : var->petrifyBlastPieces)
+          if (bsq == to ? bool(var->petrifyOnCaptureTypes & type_of(bpc)) : var->petrifyBlastPieces)
           {
               st->wallSquares |= bsq;
               byTypeBB[ALL_PIECES] |= bsq;
@@ -2116,7 +2119,7 @@ void Position::undo_move(Move m) {
   byTypeBB[ALL_PIECES] ^= st->wallSquares ^ st->previous->wallSquares;
 
   // Add the blast pieces
-  if (st->capturedPiece && (blast_on_capture() || var->petrifyOnCapture))
+  if (st->capturedPiece && (blast_on_capture() || var->petrifyOnCaptureTypes))
   {
       Bitboard blast = attacks_bb<KING>(to) | to;
       while (blast)
@@ -2435,6 +2438,10 @@ bool Position::see_ge(Move m, Value threshold) const {
   if (swap <= 0)
       return true;
 
+  // Petrification ends SEE
+  if (var->petrifyOnCaptureTypes & type_of(moved_piece(m)) && capture(m))
+      return false;
+
   Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces()) ^ to;
   Color stm = color_of(moved_piece(m));
   Bitboard attackers = attackers_to(to, occupied);
index e25d7ba..2fc1c9c 100644 (file)
@@ -460,6 +460,20 @@ namespace {
         v->extinctionPieceTypes = piece_set(ALL_PIECES);
         return v;
     }
+    // Petrified
+    // Sideways pawns + petrification on capture
+    // https://www.chess.com/variants/petrified
+    Variant* petrified_variant() {
+        Variant* v = pawnsideways_variant()->init();
+        v->remove_piece(KING);
+        v->add_piece(COMMONER, 'k');
+        v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
+        v->extinctionValue = -VALUE_MATE;
+        v->extinctionPieceTypes = piece_set(COMMONER);
+        v->extinctionPseudoRoyal = true;
+        v->petrifyOnCaptureTypes = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
+        return v;
+    }
     // Atomic chess without checks (ICC rules)
     // https://www.chessclub.com/help/atomic
     Variant* nocheckatomic_variant() {
@@ -493,6 +507,7 @@ namespace {
 
 #ifdef ALLVARS
     // Duck chess
+    // https://duckchess.com/
     Variant* duck_variant() {
         Variant* v = chess_variant_base()->init();
         v->remove_piece(KING);
@@ -1805,6 +1820,7 @@ void VariantMap::init() {
     add("kinglet", kinglet_variant());
     add("threekings", threekings_variant());
     add("horde", horde_variant());
+    add("petrified", petrified_variant());
     add("nocheckatomic", nocheckatomic_variant());
     add("atomic", atomic_variant());
     add("atomar", atomar_variant());
@@ -2013,6 +2029,7 @@ Variant* Variant::conclude() {
                   && !makpongRule
                   && !connectN
                   && !blastOnCapture
+                  && !petrifyOnCaptureTypes
                   && !capturesToHand
                   && !twoBoards
                   && !restrictedMobility
index dd6036a..2a02695 100644 (file)
@@ -66,7 +66,7 @@ struct Variant {
   bool blastOnCapture = false;
   PieceSet blastImmuneTypes = NO_PIECE_SET;
   PieceSet mutuallyImmuneTypes = NO_PIECE_SET;
-  bool petrifyOnCapture = false;
+  PieceSet petrifyOnCaptureTypes = NO_PIECE_SET;
   bool petrifyBlastPieces = false;
   bool doubleStep = true;
   Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB};
index b5c0353..8afb556 100644 (file)
 # blastOnCapture: captures explode all adjacent non-pawn pieces (e.g., atomic chess) [bool] (default: false)
 # blastImmuneTypes: pieces completely immune to explosions (even at ground zero) [PieceSet] (default: none)
 # mutuallyImmuneTypes: pieces that can't capture another piece of same types (e.g., kings (commoners) in atomar) [PieceSet] (default: none)
-# petrifyOnCapture: non-pawn pieces are turned into wall squares when capturing [bool] (default: false)
+# petrifyOnCaptureTypes: defined pieces are turned into wall squares when capturing [PieceSet] (default: -)
 # petrifyBlastPieces: if petrify and blast combined, should pieces destroyed in the blast be petrified? [bool] (default: false)
 # doubleStep: enable pawn double step [bool] (default: true)
 # doubleStepRegionWhite: region where pawn double steps are allowed for white [Bitboard] (default: *2)
@@ -1656,7 +1656,7 @@ startFen = lnsgkgsnl/1rci1uab1/p1p1p1p1p/9/9/9/P1P1P1P1P/1BAU1ICR1/LNSGKGSNL[-]
 pawn = -
 customPiece1 = p:fmWfceFifmnD
 pawnTypes = p
-petrifyOnCapture = true
+petrifyOnCaptureTypes = pnbrq
 enPassantRegion = -
 
 #https://en.wikipedia.org/wiki/Nim