add Edge Walling rule, suggesting refactor of Walling Rule, (#722)
authorRainRat <rainrat78@yahoo.ca>
Thu, 21 Sep 2023 16:47:31 +0000 (09:47 -0700)
committerGitHub <noreply@github.com>
Thu, 21 Sep 2023 16:47:31 +0000 (18:47 +0200)
src/movegen.cpp
src/parser.cpp
src/position.cpp
src/position.h
src/search.cpp
src/types.h
src/variant.cpp
src/variant.h
src/variants.ini

index 0c14dd7..b658619 100644 (file)
@@ -41,13 +41,23 @@ namespace {
         }
         if (T == EN_PASSANT)
             b ^= pos.capture_square(to);
-        if (pos.variant()->arrowWalling)
+
+        if (pos.walling_rule() == ARROW)
             b &= moves_bb(us, type_of(pos.piece_on(from)), to, pos.pieces() ^ from);
-        if ((pos.variant()->staticWalling)||(pos.variant()->duckWalling))
-            b &= pos.variant()->wallingRegion[us];
-        if (pos.variant()->pastWalling)
+
+        //Any current or future wall variant must follow the walling region rule if set:
+        b &= pos.variant()->wallingRegion[us];
+
+        if (pos.walling_rule() == PAST)
             b &= square_bb(from);
+        if (pos.walling_rule() == EDGE)
+        {
+            Bitboard wallsquares = pos.state()->wallSquares;
 
+            b &= (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) |
+               ( shift<NORTH     >(wallsquares) | shift<SOUTH     >(wallsquares)
+               | shift<EAST      >(wallsquares) | shift<WEST      >(wallsquares));
+        }
         while (b)
             *moveList++ = make_gating<T>(from, to, pt, pop_lsb(b));
         return moveList;
index 4871446..3d822eb 100644 (file)
@@ -111,6 +111,16 @@ namespace {
         return value == "reversi" || value == "ataxx" || value == "quadwrangle" || value =="snort" || value == "none";
     }
 
+    template <> bool set(const std::string& value, WallingRule& target) {
+        target =  value == "arrow"  ? ARROW
+                : value == "duck" ? DUCK
+                : value == "edge" ? EDGE
+                : value == "past" ? PAST
+                : value == "static" ? STATIC
+                : NO_WALLING;
+        return value == "arrow" || value == "duck" || value == "edge" || value =="past" || value == "static" || value == "none";
+    }
+
     template <> bool set(const std::string& value, Bitboard& target) {
         char file;
         int rank;
@@ -181,6 +191,7 @@ template <bool Current, class T> bool VariantParser<DoCheck>::parse_attribute(co
                                   : std::is_same<T, EnclosingRule>() ? "EnclosingRule"
                                   : std::is_same<T, Bitboard>() ? "Bitboard"
                                   : std::is_same<T, CastlingRights>() ? "CastlingRights"
+                                  : std::is_same<T, WallingRule>() ? "WallingRule"
                                   : typeid(T).name();
             std::cerr << key << " - Invalid value " << it->second << " for type " << typeName << std::endl;
         }
@@ -434,14 +445,11 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("dropNoDoubledCount", v->dropNoDoubledCount);
     parse_attribute("immobilityIllegal", v->immobilityIllegal);
     parse_attribute("gating", v->gating);
-    parse_attribute("arrowWalling", v->arrowWalling);
-    parse_attribute("duckWalling", v->duckWalling);
+    parse_attribute("wallingRule", v->wallingRule);
     parse_attribute("wallingRegionWhite", v->wallingRegion[WHITE]);
     parse_attribute("wallingRegionBlack", v->wallingRegion[BLACK]);
     parse_attribute("wallingRegion", v->wallingRegion[WHITE]);
     parse_attribute("wallingRegion", v->wallingRegion[BLACK]);
-    parse_attribute("staticWalling", v->staticWalling);
-    parse_attribute("pastWalling", v->pastWalling);
     parse_attribute("seirawanGating", v->seirawanGating);
     parse_attribute("cambodianMoves", v->cambodianMoves);
     parse_attribute("diagonalLines", v->diagonalLines);
@@ -553,8 +561,8 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
             std::cerr << "Inconsistent settings: castlingQueensideFile > castlingKingsideFile." << std::endl;
 
         // Check for limitations
-        if (v->pieceDrops && (v->arrowWalling || v->duckWalling || v->staticWalling || v->pastWalling))
-            std::cerr << "pieceDrops and arrowWalling/duckWalling are incompatible." << std::endl;
+        if (v->pieceDrops && v->wallingRule)
+            std::cerr << "pieceDrops and any walling are incompatible." << std::endl;
 
         // Options incompatible with royal kings
         if (v->pieceTypes & KING)
@@ -563,8 +571,8 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
                 std::cerr << "Can not use kings with blastOnCapture." << std::endl;
             if (v->flipEnclosedPieces)
                 std::cerr << "Can not use kings with flipEnclosedPieces." << std::endl;
-            if (v->duckWalling)
-                std::cerr << "Can not use kings with duckWalling." << std::endl;
+            if (v->wallingRule==DUCK)
+                std::cerr << "Can not use kings with wallingRule = duck." << std::endl;
             // We can not fully check support for custom king movements at this point,
             // since custom pieces are only initialized on loading of the variant.
             // We will assume this is valid, but it might cause problems later if it's not.
index 7091285..39f4888 100644 (file)
@@ -1104,7 +1104,7 @@ bool Position::legal(Move m) const {
   {
       Square kto = to;
       Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces());
-      if (var->duckWalling)
+      if (walling_rule() == DUCK)
           occupied ^= st->wallSquares;
       if (walling() || is_gating(m))
           occupied |= gating_square(m);
@@ -1303,15 +1303,29 @@ bool Position::pseudo_legal(const Move m) const {
       return checkers() ? MoveList<    EVASIONS>(*this).contains(m)
                         : MoveList<NON_EVASIONS>(*this).contains(m);
 
-  // Illegal wall square placement
-  if (walling() && !((board_bb() & ~((pieces() ^ from) | to)) & gating_square(m)))
-      return false;
-  if (var->arrowWalling && !(moves_bb(us, type_of(pc), to, pieces() ^ from) & gating_square(m)))
-      return false;
-  if (var->pastWalling && (from != gating_square(m)))
-      return false;
-  if ((var->staticWalling || var->duckWalling) && !(var->wallingRegion[us] & gating_square(m)))
-      return false;
+  if (walling())
+  {
+      Bitboard wallsquares = st->wallSquares;
+
+      // Illegal wall square placement
+      if (!((board_bb() & ~((pieces() ^ from) | to)) & gating_square(m)))
+          return false;
+      if (!(var->wallingRegion[us] & gating_square(m)) || //putting a wall on disallowed square
+          wallsquares & gating_square(m)) //or square already with a wall
+          return false;
+      if (walling_rule() == ARROW && !(moves_bb(us, type_of(pc), to, pieces() ^ from) & gating_square(m)))
+          return false;
+      if (walling_rule() == PAST && (from != gating_square(m)))
+          return false;
+      if (walling_rule() == EDGE)
+      {
+          Bitboard validsquares = board_bb() &
+                  ((FileABB | file_bb(max_file()) | Rank1BB | rank_bb(max_rank())) |
+                  ( shift<NORTH     >(wallsquares) | shift<SOUTH     >(wallsquares)
+                  | shift<EAST      >(wallsquares) | shift<WEST      >(wallsquares)));
+          if (!(validsquares & gating_square(m))) return false;
+      };
+  }
 
   // Handle the case where a mandatory piece promotion/demotion is not taken
   if (    mandatory_piece_promotion()
@@ -2034,7 +2048,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
   if (walling())
   {
       // Reset wall squares for duck walling
-      if (var->duckWalling)
+      if (walling_rule() == DUCK)
       {
           Bitboard b = st->previous->wallSquares;
           byTypeBB[ALL_PIECES] ^= b;
@@ -2476,7 +2490,7 @@ bool Position::see_ge(Move m, Value threshold) const {
           stmAttackers &= ~blockers_for_king(stm);
 
       // Ignore distant sliders
-      if (var->duckWalling)
+      if (walling_rule() == DUCK)
           stmAttackers &= attacks_bb<KING>(to) | ~(pieces(BISHOP, ROOK) | pieces(QUEEN));
 
       if (!stmAttackers)
@@ -2969,7 +2983,7 @@ bool Position::has_game_cycle(int ply) const {
 
   int end = captures_to_hand() ? st->pliesFromNull : std::min(st->rule50, st->pliesFromNull);
 
-  if (end < 3 || var->nFoldValue != VALUE_DRAW || var->perpetualCheckIllegal || var->materialCounting || var->moveRepetitionIllegal || var->duckWalling)
+  if (end < 3 || var->nFoldValue != VALUE_DRAW || var->perpetualCheckIllegal || var->materialCounting || var->moveRepetitionIllegal || walling_rule() == DUCK)
     return false;
 
   Key originalKey = st->key;
index 34b0976..2311776 100644 (file)
@@ -179,6 +179,7 @@ public:
   bool immobility_illegal() const;
   bool gating() const;
   bool walling() const;
+  WallingRule walling_rule() const;
   bool seirawan_gating() const;
   bool cambodian_moves() const;
   Bitboard diagonal_lines() const;
@@ -761,7 +762,12 @@ inline bool Position::gating() const {
 
 inline bool Position::walling() const {
   assert(var != nullptr);
-  return var->arrowWalling || var->duckWalling || var->staticWalling || var->pastWalling;
+  return var->wallingRule != NO_WALLING;
+}
+
+inline WallingRule Position::walling_rule() const {
+  assert(var != nullptr);
+  return var->wallingRule;
 }
 
 inline bool Position::seirawan_gating() const {
index 2502fcc..77686ed 100644 (file)
@@ -1185,7 +1185,7 @@ moves_loop: // When in check, search starts from here
                   continue;
 
               // Prune moves with negative SEE (~20 Elo)
-              if (!pos.variant()->duckWalling && !pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.flag_region(pos.side_to_move())) * lmrDepth * lmrDepth)))
+              if (!(pos.walling_rule() == DUCK) && !pos.see_ge(move, Value(-(30 - std::min(lmrDepth, 18) + 10 * !!pos.flag_region(pos.side_to_move())) * lmrDepth * lmrDepth)))
                   continue;
           }
       }
index 99c5e1a..2fa03e7 100644 (file)
@@ -305,6 +305,10 @@ enum EnclosingRule {
   NO_ENCLOSING, REVERSI, ATAXX, QUADWRANGLE, SNORT
 };
 
+enum WallingRule {
+  NO_WALLING, ARROW, DUCK, EDGE, PAST, STATIC
+};
+
 enum OptBool {
   NO_VALUE, VALUE_FALSE, VALUE_TRUE
 };
index 2fc1c9c..8a42468 100644 (file)
@@ -515,7 +515,7 @@ namespace {
         v->castlingKingPiece[WHITE] = v->castlingKingPiece[BLACK] = COMMONER;
         v->extinctionValue = -VALUE_MATE;
         v->extinctionPieceTypes = piece_set(COMMONER);
-        v->duckWalling = true;
+        v->wallingRule = DUCK;
         v->stalemateValue = VALUE_MATE;
         return v;
     }
@@ -529,7 +529,7 @@ namespace {
         v->add_piece(CUSTOM_PIECE_1, 'p', "mK"); //move as a King, but can't capture
         v->startFen = "3p2/6/6/6/6/6/6/2P3 w - - 0 1";
         v->stalemateValue = -VALUE_MATE;
-        v->staticWalling = true;
+        v->wallingRule = STATIC;
         v->wallingRegion[WHITE] = v->wallingRegion[BLACK] = AllSquares ^ make_bitboard(SQ_C1, SQ_D8);
         return v;
     }
@@ -551,7 +551,7 @@ namespace {
         v->add_piece(CUSTOM_PIECE_1, 'p', "mK"); //move as a King, but can't capture
         v->startFen = "6p/7/7/7/7/7/P6 w - - 0 1";
         v->stalemateValue = -VALUE_MATE;
-        v->pastWalling = true;
+        v->wallingRule = PAST;
         return v;
     }
 
@@ -562,7 +562,7 @@ namespace {
         v->add_piece(CUSTOM_PIECE_1, 'n', "mN"); //move as a Knight, but can't capture
         v->startFen = "8/8/8/4n3/3N4/8/8/8 w - - 0 1";
         v->stalemateValue = -VALUE_MATE;
-        v->pastWalling = true;
+        v->wallingRule = PAST;
         return v;
     }
 
@@ -1668,7 +1668,7 @@ namespace {
         v->add_piece(CUSTOM_PIECE_1, 'q', "mQ");
         v->startFen = "3q2q3/10/10/q8q/10/10/Q8Q/10/10/3Q2Q3 w - - 0 1";
         v->stalemateValue = -VALUE_MATE;
-        v->arrowWalling = true;
+        v->wallingRule = ARROW;
         return v;
     }
 #endif
index 2a02695..cb2fe05 100644 (file)
@@ -107,10 +107,7 @@ struct Variant {
   int dropNoDoubledCount = 1;
   bool immobilityIllegal = false;
   bool gating = false;
-  bool arrowWalling = false;
-  bool duckWalling = false;
-  bool staticWalling = false;
-  bool pastWalling = false;
+  WallingRule wallingRule = NO_WALLING;
   Bitboard wallingRegion[COLOR_NB] = {AllSquares, AllSquares};
   bool seirawanGating = false;
   bool cambodianMoves = false;
index 8afb556..e3bdf0d 100644 (file)
 # [CountingRule]: makruk, cambodian, or ASEAN counting rules [makruk, cambodian, asean, none]
 # [ChasingRule]: xiangqi chasing rules [axf, none]
 # [EnclosingRule]: reversi or ataxx enclosing rules [reversi, ataxx, quadwrangle, snort, none]
+# [WallingRule]: wall-placing rule [arrow, duck, edge, past, static, none]
+# - arrow: copies piece movement (ie. Game of the Amazons)
+# - duck: mobile square (ie. Duck chess)
+# - edge: edges of board, opening up new edges (ie. Atlantis)
+# - past: previous square (ie. Snailtrail)
+# - static: unchanging mask (ie. Isolation)
 
 ### Additional options relevant for usage in Winboard/XBoard
 # A few options only have the purpose of improving compatibility with Winboard/Xboard.
 # dropNoDoubledCount: specifies the count of already existing pieces for dropNoDoubled [int] (default: 1)
 # immobilityIllegal: pieces may not move to squares where they can never move from [bool] (default: false)
 # gating: maintain squares on backrank with extra rights in castling field of FEN [bool] (default: false)
-# arrowWalling: walling squares in Game of the Amazons style [bool] (default: false)
-# duckWalling: walling square in Duck chess style [bool] (default: false)
-# staticWalling: walling squares in Isolation style [bool] (default: false)
+# wallingRule: rule on where wall can be placed [WallingRule] (default: none)
 # wallingRegionWhite: mask where wall squares (including duck) can be placed by white [Bitboard] (default: all squares)
 # wallingRegionBlack: mask where wall squares (including duck) can be placed by black [Bitboard] (default: all squares)
-# pastWalling: walling of previous square in Snailtrail style [bool] (default: false)
 # seirawanGating: allow gating of pieces in hand like in S-Chess, requires "gating = true" [bool] (default: false)
 # cambodianMoves: enable special moves of cambodian chess, requires "gating = true" [bool] (default: false)
 # diagonalLines: enable special moves along diagonal for specific squares (Janggi) [Bitboard]
@@ -1349,7 +1352,7 @@ pieceToCharTable = P...Q..AH..ECTDY....LKp...q..ah..ectdy....lk
 # Atomic + duck chess hybrid.
 # Playable as a custom variant in chess.com
 [atomicduck:atomic]
-duckWalling = true
+wallingRule = duck
 stalemateValue = win
 
 #https://www.chessvariants.com/diffmove.dir/checkers.html
@@ -1769,3 +1772,10 @@ extinctionValue = loss
 extinctionPieceTypes = kq
 extinctionPseudoRoyal = true
 stalemateValue = loss
+
+#https://www.chessvariants.com/boardrules.dir/atlantis.html
+[atlantis:chess]
+wallingRule = edge
+#not ready yet. Other wall variants are "move and wall", this is "move or wall".
+#need to figure out way to do this ie. write code for:
+#wallOrMove = true
\ No newline at end of file