Implement Variant::freeDrops (#486)
authorkz04px <6536046+kz04px@users.noreply.github.com>
Tue, 24 May 2022 21:58:22 +0000 (22:58 +0100)
committerGitHub <noreply@github.com>
Tue, 24 May 2022 21:58:22 +0000 (23:58 +0200)
For some games like Ataxx and Othello, specifying the pieces in hand is unnecessary. Setting freeDrops to true removes this from the FEN string.

src/movegen.cpp
src/position.cpp
src/position.h
src/variant.cpp
src/variant.h
tests/perft.sh

index 57d647d..ee9039a 100644 (file)
@@ -32,7 +32,7 @@ namespace {
     if (pos.arrow_gating())
     {
         for (PieceType pt_gating : pos.piece_types())
-            if (pos.count_in_hand(us, pt_gating) > 0)
+            if (pos.can_drop(us, pt_gating))
             {
                 Bitboard b = pos.drop_region(us, pt_gating) & moves_bb(us, type_of(pos.piece_on(from)), to, pos.pieces() ^ from) & ~(pos.pieces() ^ from);
                 while (b)
@@ -46,11 +46,11 @@ namespace {
     // Gating moves
     if (pos.seirawan_gating() && (pos.gates(us) & from))
         for (PieceType pt_gating : pos.piece_types())
-            if (pos.count_in_hand(us, pt_gating) > 0 && (pos.drop_region(us, pt_gating) & from))
+            if (pos.can_drop(us, pt_gating) && (pos.drop_region(us, pt_gating) & from))
                 *moveList++ = make_gating<T>(from, to, pt_gating, from);
     if (pos.seirawan_gating() && T == CASTLING && (pos.gates(us) & to))
         for (PieceType pt_gating : pos.piece_types())
-            if (pos.count_in_hand(us, pt_gating) > 0 && (pos.drop_region(us, pt_gating) & to))
+            if (pos.can_drop(us, pt_gating) && (pos.drop_region(us, pt_gating) & to))
                 *moveList++ = make_gating<T>(from, to, pt_gating, to);
 
     return moveList;
@@ -76,7 +76,7 @@ namespace {
   ExtMove* generate_drops(const Position& pos, ExtMove* moveList, PieceType pt, Bitboard b) {
     assert(Type != CAPTURES);
     // Do not generate virtual drops for perft and at root
-    if (pos.count_in_hand(Us, pt) > 0 || (Type != NON_EVASIONS && pos.two_boards() && pos.allow_virtual_drop(Us, pt)))
+    if (pos.can_drop(Us, pt) || (Type != NON_EVASIONS && pos.two_boards() && pos.allow_virtual_drop(Us, pt)))
     {
         // Restrict to valid target
         b &= pos.drop_region(Us, pt);
@@ -90,7 +90,7 @@ namespace {
             while (b2)
                 *moveList++ = make_drop(pop_lsb(b2), pt, pos.promoted_piece_type(pt));
         }
-        if (Type == QUIET_CHECKS || pos.count_in_hand(Us, pt) <= 0)
+        if (Type == QUIET_CHECKS || !pos.can_drop(Us, pt))
             b &= pos.check_squares(pt);
         while (b)
             *moveList++ = make_drop(pop_lsb(b), pt, pt);
@@ -342,7 +342,7 @@ namespace {
             if (pt != PAWN && pt != KING)
                 moveList = generate_moves<Us, Checks>(pos, moveList, pt, target);
         // generate drops
-        if (pos.piece_drops() && Type != CAPTURES && (pos.count_in_hand(Us, ALL_PIECES) > 0 || pos.two_boards()))
+        if (pos.piece_drops() && Type != CAPTURES && (pos.can_drop(Us, ALL_PIECES) || pos.two_boards()))
             for (PieceType pt : pos.piece_types())
                 moveList = generate_drops<Us, Type>(pos, moveList, pt, target & ~pos.pieces(~Us));
 
index 14dc975..1e0d2fc 100644 (file)
@@ -72,7 +72,7 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
               os << " *";
           else
               os << "  ";
-          if (pos.piece_drops() || pos.seirawan_gating() || pos.arrow_gating())
+          if (!pos.variant()->freeDrops && (pos.piece_drops() || pos.seirawan_gating() || pos.arrow_gating()))
           {
               os << " [";
               for (PieceType pt = KING; pt >= PAWN; --pt)
@@ -693,7 +693,7 @@ string Position::fen(bool sfen, bool showPromoted, int countStarted, std::string
   }
 
   // pieces in hand
-  if (piece_drops() || seirawan_gating() || arrow_gating())
+  if (!variant()->freeDrops && (piece_drops() || seirawan_gating() || arrow_gating()))
   {
       ss << '[';
       if (holdings != "-")
@@ -1164,7 +1164,7 @@ bool Position::pseudo_legal(const Move m) const {
       return   piece_drops()
             && pc != NO_PIECE
             && color_of(pc) == us
-            && (count_in_hand(us, in_hand_piece_type(m)) > 0 || (two_boards() && allow_virtual_drop(us, type_of(pc))))
+            && (can_drop(us, in_hand_piece_type(m)) || (two_boards() && allow_virtual_drop(us, type_of(pc))))
             && (drop_region(us, type_of(pc)) & ~pieces() & to)
             && (   type_of(pc) == in_hand_piece_type(m)
                 || (drop_promoted() && type_of(pc) == promoted_piece_type(in_hand_piece_type(m))));
index 30c73a4..f06566c 100644 (file)
@@ -161,6 +161,7 @@ public:
   bool captures_to_hand() const;
   bool first_rank_pawn_drops() const;
   bool drop_on_top() const;
+  bool can_drop(Color c, PieceType pt) const;
   EnclosingRule enclosing_drop() const;
   Bitboard drop_region(Color c) const;
   Bitboard drop_region(Color c, PieceType pt) const;
@@ -1214,7 +1215,7 @@ inline bool Position::capture(Move m) const {
 
 inline bool Position::virtual_drop(Move m) const {
   assert(is_ok(m));
-  return type_of(m) == DROP && count_in_hand(side_to_move(), in_hand_piece_type(m)) <= 0;
+  return type_of(m) == DROP && !can_drop(side_to_move(), in_hand_piece_type(m));
 }
 
 inline Piece Position::captured_piece() const {
@@ -1340,19 +1341,21 @@ inline Value Position::material_counting_result() const {
 }
 
 inline void Position::add_to_hand(Piece pc) {
+  if (variant()->freeDrops) return;
   pieceCountInHand[color_of(pc)][type_of(pc)]++;
   pieceCountInHand[color_of(pc)][ALL_PIECES]++;
   psq += PSQT::psq[pc][SQ_NONE];
 }
 
 inline void Position::remove_from_hand(Piece pc) {
+  if (variant()->freeDrops) return;
   pieceCountInHand[color_of(pc)][type_of(pc)]--;
   pieceCountInHand[color_of(pc)][ALL_PIECES]--;
   psq -= PSQT::psq[pc][SQ_NONE];
 }
 
 inline void Position::drop_piece(Piece pc_hand, Piece pc_drop, Square s) {
-  assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] > 0 || var->twoBoards);
+  assert(can_drop(color_of(pc_hand), type_of(pc_hand)) || var->twoBoards);
   put_piece(pc_drop, s, pc_drop != pc_hand, pc_drop != pc_hand ? pc_hand : NO_PIECE);
   remove_from_hand(pc_hand);
   virtualPieces += (pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] < 0);
@@ -1363,7 +1366,11 @@ inline void Position::undrop_piece(Piece pc_hand, Square s) {
   remove_piece(s);
   board[s] = NO_PIECE;
   add_to_hand(pc_hand);
-  assert(pieceCountInHand[color_of(pc_hand)][type_of(pc_hand)] > 0 || var->twoBoards);
+  assert(can_drop(color_of(pc_hand), type_of(pc_hand)) || var->twoBoards);
+}
+
+inline bool Position::can_drop(Color c, PieceType pt) const {
+  return variant()->freeDrops || count_in_hand(c, pt) > 0;
 }
 
 } // namespace Stockfish
index 4d64c71..632272e 100644 (file)
@@ -857,7 +857,7 @@ namespace {
         v->maxFile = FILE_G;
         v->reset_pieces();
         v->add_piece(CUSTOM_PIECES, 'p', "mDmNmA");
-        v->startFen = "P5p/7/7/7/7/7/p5P[PPPPPPPPPPPPPPPPPPPPPPPPPppppppppppppppppppppppppp] w 0 1";
+        v->startFen = "P5p/7/7/7/7/7/p5P w 0 1";
         v->promotionPieceTypes = {};
         v->pieceDrops = true;
         v->doubleStep = false;
@@ -870,6 +870,7 @@ namespace {
         v->flipEnclosedPieces = ATAXX;
         v->materialCounting = UNWEIGHTED_MATERIAL;
         v->nMoveRule = 0;
+        v->freeDrops = true;
         return v;
     }
     // Flipersi
index 65fb1f6..aba01a7 100644 (file)
@@ -105,6 +105,7 @@ struct Variant {
   bool flyingGeneral = false;
   Rank soldierPromotionRank = RANK_1;
   EnclosingRule flipEnclosedPieces = NO_ENCLOSING;
+  bool freeDrops = false;
 
   // game end
   int nMoveRule = 50;
index c87a7f5..9f00fa7 100755 (executable)
@@ -114,7 +114,7 @@ if [[ $1 == "all" || $1 == "variant" ]]; then
   expect perft.exp torishogi startpos 4 103857 > /dev/null
   # non-chess
   expect perft.exp ataxx startpos 4 155888 > /dev/null
-  expect perft.exp ataxx "fen 7/7/7/7/ppppppp/ppppppp/PPPPPPP[PPPPPPPPPPPPPPPPPPPPPppppppppppppppppppppp] w 0 1" 5 452980 > /dev/null
+  expect perft.exp ataxx "fen 7/7/7/7/ppppppp/ppppppp/PPPPPPP w 0 1" 5 452980 > /dev/null
   expect perft.exp breakthrough startpos 4 256036 > /dev/null
   expect perft.exp breakthrough "fen 1p2pp1p/2p2ppp/2P5/8/8/3P2P1/1p1P2PP/1PP1PP1P w - - 1 26" 4 121264 > /dev/null
   expect perft.exp clobber startpos 3 80063 > /dev/null