Replace set<PieceType> by PieceSet enum (#602)
authorFabian Fichter <ianfab@users.noreply.github.com>
Fri, 24 Mar 2023 22:27:55 +0000 (23:27 +0100)
committerGitHub <noreply@github.com>
Fri, 24 Mar 2023 22:27:55 +0000 (23:27 +0100)
Simplifies the code and improves performance.

No functional change.

16 files changed:
src/apiutil.h
src/bitboard.h
src/endgame.cpp
src/evaluate.cpp
src/material.cpp
src/movegen.cpp
src/nnue/features/half_ka_v2_variants.cpp
src/parser.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/search.cpp
src/types.h
src/ucioption.cpp
src/variant.cpp
src/variant.h

index 34e7729..3cdbf4d 100644 (file)
@@ -375,18 +375,21 @@ inline bool has_insufficient_material(Color c, const Position& pos) {
     // Restricted pieces
     Bitboard restricted = pos.pieces(~c, KING);
     // Atomic kings can not help checkmating
-    if (pos.extinction_pseudo_royal() && pos.blast_on_capture() && pos.extinction_piece_types().find(COMMONER) != pos.extinction_piece_types().end())
+    if (pos.extinction_pseudo_royal() && pos.blast_on_capture() && (pos.extinction_piece_types() & COMMONER))
         restricted |= pos.pieces(c, COMMONER);
-    for (PieceType pt : pos.piece_types())
+    for (PieceSet ps = pos.piece_types(); ps;)
+    {
+        PieceType pt = pop_lsb(ps);
         if (pt == KING || !(pos.board_bb(c, pt) & pos.board_bb(~c, KING)))
             restricted |= pos.pieces(c, pt);
         else if (is_custom(pt) && pos.count(c, pt) > 0)
             // to be conservative, assume any custom piece has mating potential
             return false;
+    }
 
     // Mating pieces
     for (PieceType pt : { ROOK, QUEEN, ARCHBISHOP, CHANCELLOR, SILVER, GOLD, COMMONER, CENTAUR })
-        if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, pos.promotion_pawn_type(c)) && pos.promotion_piece_types(c).find(pt) != pos.promotion_piece_types(c).end()))
+        if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, pos.promotion_pawn_type(c)) && (pos.promotion_piece_types(c) & pt)))
             return false;
 
     // Color-bound pieces
@@ -895,7 +898,7 @@ inline std::string get_valid_special_chars(const Variant* v) {
     // Whether or not '-', '+', '~', '[', ']' are valid depends on the variant being played.
     if (v->shogiStylePromotions)
         validSpecialCharactersFirstField += '+';
-    if (!v->promotionPieceTypes[WHITE].empty() || !v->promotionPieceTypes[BLACK].empty())
+    if (v->promotionPieceTypes[WHITE] || v->promotionPieceTypes[BLACK])
         validSpecialCharactersFirstField += '~';
     if (!v->freeDrops && (v->pieceDrops || v->seirawanGating))
         validSpecialCharactersFirstField += "[-]";
@@ -948,7 +951,7 @@ inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool
     }
 
     // check for number of kings
-    if (v->pieceTypes.find(KING) != v->pieceTypes.end())
+    if (v->pieceTypes & KING)
     {
         // we have a royal king in this variant,
         // ensure that each side has exactly as many kings as in the starting position
@@ -1021,7 +1024,7 @@ inline FenValidation validate_fen(const std::string& fen, const Variant* v, bool
     // check en-passant square
     if (fenParts.size() >= 4 && !skipCastlingAndEp)
     {
-        if (v->doubleStep && v->pieceTypes.find(PAWN) != v->pieceTypes.end())
+        if (v->doubleStep && (v->pieceTypes & PAWN))
         {
             if (check_en_passant_square(fenParts[3]) == NOK)
                 return FEN_INVALID_EN_PASSANT_SQ;
index b1b6f3d..ce575b5 100644 (file)
@@ -678,6 +678,110 @@ inline Square frontmost_sq(Color c, Bitboard b) {
   return c == WHITE ? msb(b) : lsb(b);
 }
 
+
+/// popcount() counts the number of non-zero bits in a piece set
+
+inline int popcount(PieceSet ps) {
+
+#ifndef USE_POPCNT
+
+  union { uint64_t bb; uint16_t u[4]; } v = { (uint64_t)ps };
+  return PopCnt16[v.u[0]] + PopCnt16[v.u[1]] + PopCnt16[v.u[2]] + PopCnt16[v.u[3]];
+
+#elif defined(_MSC_VER) || defined(__INTEL_COMPILER)
+
+  return (int)_mm_popcnt_u64(ps);
+
+#else // Assumed gcc or compatible compiler
+
+  return __builtin_popcountll(ps);
+
+#endif
+}
+
+/// lsb() and msb() return the least/most significant bit in a non-zero piece set
+
+#if defined(__GNUC__)  // GCC, Clang, ICC
+
+inline PieceType lsb(PieceSet ps) {
+  assert(ps);
+  return PieceType(__builtin_ctzll(ps));
+}
+
+inline PieceType msb(PieceSet ps) {
+  assert(ps);
+  return PieceType((PIECE_TYPE_NB - 1) ^ __builtin_clzll(ps));
+}
+
+#elif defined(_MSC_VER)  // MSVC
+
+#ifdef _WIN64  // MSVC, WIN64
+
+inline PieceType lsb(PieceSet ps) {
+  assert(ps);
+  unsigned long idx;
+  _BitScanForward64(&idx, ps);
+  return (PieceType) idx;
+}
+
+inline PieceType msb(PieceSet ps) {
+  assert(ps);
+  unsigned long idx;
+  _BitScanReverse64(&idx, ps);
+  return (PieceType) idx;
+}
+
+#else  // MSVC, WIN32
+
+inline PieceType lsb(PieceSet ps) {
+  assert(ps);
+  unsigned long idx;
+
+  if (ps & 0xffffffff) {
+      _BitScanForward(&idx, uint32_t(ps));
+      return PieceType(idx);
+  } else {
+      _BitScanForward(&idx, uint32_t(ps >> 32));
+      return PieceType(idx + 32);
+  }
+}
+
+inline PieceType msb(PieceSet ps) {
+  assert(ps);
+  unsigned long idx;
+  if (ps >> 32) {
+      _BitScanReverse(&idx, uint32_t(ps >> 32));
+      return PieceType(idx + 32);
+  } else {
+      _BitScanReverse(&idx, uint32_t(ps));
+      return PieceType(idx);
+  }
+}
+
+#endif
+
+#else  // Compiler is neither GCC nor MSVC compatible
+
+#error "Compiler not supported."
+
+#endif
+
+/// pop_lsb() and pop_msb() find and clear the least/most significant bit in a non-zero piece set
+
+inline PieceType pop_lsb(PieceSet& ps) {
+  assert(ps);
+  const PieceType pt = lsb(ps);
+  ps &= PieceSet(ps - 1);
+  return pt;
+}
+
+inline PieceType pop_msb(PieceSet& ps) {
+  assert(ps);
+  const PieceType pt = msb(ps);
+  ps &= ~piece_set(pt);
+  return pt;
+}
+
 } // namespace Stockfish
 
 #endif // #ifndef BITBOARD_H_INCLUDED
index 186258a..a95ff37 100644 (file)
@@ -185,7 +185,7 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
   // Non-standard promotion, evaluation unclear
   if (   pos.promotion_zone(us) != rank_bb(relative_rank(us, RANK_8, pos.max_rank()))
       || RANK_MAX != RANK_8
-      || pos.promotion_piece_types(us).find(QUEEN) == pos.promotion_piece_types(us).end())
+      || !(pos.promotion_piece_types(us) & QUEEN))
   {
       Value result = PawnValueEg + Value(rank_of(strongPawn));
       return strongSide == pos.side_to_move() ? result : -result;
@@ -939,7 +939,7 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
   // it's probably at least a draw even with the pawn.
   if (   pos.promotion_zone(us) != rank_bb(relative_rank(us, RANK_8, pos.max_rank()))
       || RANK_MAX != RANK_8
-      || pos.promotion_piece_types(us).find(QUEEN) == pos.promotion_piece_types(us).end())
+      || !(pos.promotion_piece_types(us) & QUEEN))
       return SCALE_FACTOR_NONE;
 
   return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
index 24ca187..b2986fe 100644 (file)
@@ -694,8 +694,9 @@ namespace {
     std::function <Bitboard (Color, PieceType)> get_attacks = [this](Color c, PieceType pt) {
         return attackedBy[c][pt] | (pos.piece_drops() && pos.count_in_hand(c, pt) > 0 ? pos.drop_region(c, pt) & ~pos.pieces() : Bitboard(0));
     };
-    for (PieceType pt : pos.piece_types())
+    for (PieceSet ps = pos.piece_types(); ps;)
     {
+        PieceType pt = pop_lsb(ps);
         switch (pt)
         {
         case QUEEN:
@@ -755,7 +756,9 @@ namespace {
     // Virtual piece drops
     if (pos.two_boards() && pos.piece_drops())
     {
-        for (PieceType pt : pos.piece_types())
+        for (PieceSet ps = pos.piece_types(); ps;)
+        {
+            PieceType pt = pop_lsb(ps);
             if (pos.count_in_hand(Them, pt) <= 0 && (attacks_bb(Us, pt, ksq, pos.pieces()) & safe & pos.drop_region(Them, pt) & ~pos.pieces()))
             {
                 kingDanger += VirtualCheck * 500 / (500 + PieceValue[MG][pt]);
@@ -763,6 +766,7 @@ namespace {
                 if (!(attackedBy[Us][KING] & ~(attackedBy[Them][ALL_PIECES] | pos.pieces(Us))))
                     kingDanger += 2000;
             }
+        }
     }
 
     if (pos.check_counting())
@@ -863,8 +867,9 @@ namespace {
     if (pos.extinction_value() == -VALUE_MATE)
     {
         Bitboard bExt = attackedBy[Us][ALL_PIECES] & pos.pieces(Them);
-        for (PieceType pt : pos.extinction_piece_types())
+        for (PieceSet ps = pos.extinction_piece_types(); ps;)
         {
+            PieceType pt = pop_lsb(ps);
             if (pt == ALL_PIECES)
                 continue;
             int denom = std::max(pos.count_with_hand(Them, pt) - pos.extinction_piece_count(), 1);
@@ -1067,8 +1072,9 @@ namespace {
 
     // Scale by maximum promotion piece value
     Value maxMg = VALUE_ZERO, maxEg = VALUE_ZERO;
-    for (PieceType pt : pos.promotion_piece_types(Us))
+    for (PieceSet ps = pos.promotion_piece_types(Us); ps;)
     {
+        PieceType pt = pop_lsb(ps);
         maxMg = std::max(maxMg, PieceValue[MG][pt]);
         maxEg = std::max(maxEg, PieceValue[EG][pt]);
     }
@@ -1213,7 +1219,9 @@ namespace {
     // Extinction
     if (pos.extinction_value() != VALUE_NONE)
     {
-        for (PieceType pt : pos.extinction_piece_types())
+        for (PieceSet ps = pos.extinction_piece_types(); ps;)
+        {
+            PieceType pt = pop_lsb(ps);
             if (pt != ALL_PIECES)
             {
                 // Single piece type extinction bonus
@@ -1250,6 +1258,7 @@ namespace {
                     dist = std::min(dist, popcount(pos.pieces(PAWN) & file_bb(f)));
                 score += make_score(70, 70) * pos.count<PAWN>(Them) / (1 + dist * dist) / (pos.pieces(Us, QUEEN) ? 2 : 4);
             }
+        }
     }
 
     // Connect-n
@@ -1471,14 +1480,20 @@ namespace {
 
     // Pieces evaluated first (also populates attackedBy, attackedBy2).
     // For unused piece types, we still need to set attack bitboard to zero.
-    for (PieceType pt : pos.piece_types())
+    for (PieceSet ps = pos.piece_types(); ps;)
+    {
+        PieceType pt = pop_lsb(ps);
         if (pt != SHOGI_PAWN && pt != PAWN && pt != KING)
             score += pieces<WHITE>(pt) - pieces<BLACK>(pt);
+    }
 
     // Evaluate pieces in hand once attack tables are complete
     if (pos.piece_drops() || pos.seirawan_gating())
-        for (PieceType pt : pos.piece_types())
+        for (PieceSet ps = pos.piece_types(); ps;)
+        {
+            PieceType pt = pop_lsb(ps);
             score += hand<WHITE>(pt) - hand<BLACK>(pt);
+        }
 
     score += (mobility[WHITE] - mobility[BLACK]) * (1 + pos.captures_to_hand() + pos.must_capture() + pos.check_counting());
 
index e806093..36f664e 100644 (file)
@@ -69,8 +69,7 @@ namespace {
 
   // Helper used to detect a given material distribution
   bool is_KFsPsK(const Position& pos, Color us) {
-    return    pos.promotion_piece_types(us).size() == 1
-          &&  pos.promotion_piece_types(us).find(FERS) != pos.promotion_piece_types(us).end()
+    return    (pos.promotion_piece_types(us) == piece_set(FERS))
           && !more_than_one(pos.pieces(~us))
           && (pos.count<FERS>(us) || pos.count<PAWN>(us))
           && !(pos.count<ALL_PIECES>(us) - pos.count<FERS>(us) - pos.count<PAWN>(us) - pos.count<KING>(us));
@@ -165,8 +164,11 @@ Entry* probe(const Position& pos) {
   if (pos.captures_to_hand() || pos.two_boards())
   {
       Value npm2 = VALUE_ZERO;
-      for (PieceType pt : pos.piece_types())
+      for (PieceSet ps = pos.piece_types(); ps;)
+      {
+          PieceType pt = pop_lsb(ps);
           npm2 += pos.count_in_hand(pt) * PieceValue[MG][make_piece(WHITE, pt)];
+      }
       e->gamePhase = Phase(PHASE_MIDGAME * npm / std::max(int(npm + npm2), 1));
       int countAll = pos.count_with_hand(WHITE, ALL_PIECES) + pos.count_with_hand(BLACK, ALL_PIECES);
       e->materialDensity = (npm + npm2 + pos.count<PAWN>() * PawnValueMg) * countAll / (pos.files() * pos.ranks());
index e713f8e..60a1efd 100644 (file)
@@ -57,13 +57,19 @@ namespace {
 
     // Gating moves
     if (pos.seirawan_gating() && (pos.gates(us) & from))
-        for (PieceType pt_gating : pos.piece_types())
+        for (PieceSet ps = pos.piece_types(); ps;)
+        {
+            PieceType pt_gating = pop_lsb(ps);
             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())
+        for (PieceSet ps = pos.piece_types(); ps;)
+        {
+            PieceType pt_gating = pop_lsb(ps);
             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;
   }
@@ -73,9 +79,12 @@ namespace {
 
     if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
     {
-        for (PieceType pt : pos.promotion_piece_types(c))
+        for (PieceSet promotions = pos.promotion_piece_types(c); promotions;)
+        {
+            PieceType pt = pop_msb(promotions);
             if (!pos.promotion_limit(pt) || pos.promotion_limit(pt) > pos.count(c, pt))
                 moveList = make_move_and_gating<PROMOTION>(pos, moveList, pos.side_to_move(), to - D, to, pt);
+        }
         PieceType pt = pos.promoted_piece_type(PAWN);
         if (pt && !(pos.piece_promotion_on_capture() && pos.empty(to)))
             moveList = make_move_and_gating<PIECE_PROMOTION>(pos, moveList, pos.side_to_move(), to - D, to);
@@ -213,8 +222,9 @@ namespace {
         while (promotionPawns)
         {
             Square from = pop_lsb(promotionPawns);
-            for (PieceType pt : pos.promotion_piece_types(Us))
+            for (PieceSet ps = pos.promotion_piece_types(Us); ps;)
             {
+                PieceType pt = pop_msb(ps);
                 if (pos.promotion_limit(pt) && pos.promotion_limit(pt) <= pos.count(Us, pt))
                     continue;
                 Bitboard b = ((pos.attacks_from(Us, pt, from) & ~pos.pieces()) | from) & target;
@@ -332,10 +342,13 @@ namespace {
 
         // Pawn-style promotions
         if ((Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) && pawnPromotions)
-            for (PieceType ptP : pos.promotion_piece_types(Us))
+            for (PieceSet ps = pos.promotion_piece_types(Us); ps;)
+            {
+                PieceType ptP = pop_msb(ps);
                 if (!pos.promotion_limit(ptP) || pos.promotion_limit(ptP) > pos.count(Us, ptP))
                     for (Bitboard promotions = pawnPromotions; promotions; )
                         moveList = make_move_and_gating<PROMOTION>(pos, moveList, pos.side_to_move(), from, pop_lsb(promotions), ptP);
+            }
 
         // En passant captures
         if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
@@ -378,13 +391,12 @@ namespace {
         target &= pos.board_bb();
 
         moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
-        for (PieceType pt : pos.piece_types())
-            if (pt != PAWN && pt != KING)
-                moveList = generate_moves<Us, Type>(pos, moveList, pt, target);
+        for (PieceSet ps = pos.piece_types() & ~(piece_set(PAWN) | KING); ps;)
+            moveList = generate_moves<Us, Type>(pos, moveList, pop_lsb(ps), target);
         // generate drops
         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));
+            for (PieceSet ps = pos.piece_types(); ps;)
+                moveList = generate_drops<Us, Type>(pos, moveList, pop_lsb(ps), target & ~pos.pieces(~Us));
 
         // Castling with non-king piece
         if (!pos.count<KING>(Us) && Type != CAPTURES && pos.can_castle(Us & ANY_CASTLING))
index 0807241..4d23de0 100644 (file)
@@ -63,9 +63,12 @@ namespace Stockfish::Eval::NNUE::Features {
     // Indices for pieces in hand
     if (pos.nnue_use_pockets())
       for (Color c : {WHITE, BLACK})
-          for (PieceType pt : pos.piece_types())
+          for (PieceSet ps = pos.piece_types(); ps;)
+          {
+              PieceType pt = pop_lsb(ps);
               for (int i = 0; i < pos.count_in_hand(c, pt); i++)
                   active.push_back(make_index(perspective, i, make_piece(c, pt), oriented_ksq, pos));
+          }
 
   }
 
index 90a5406..6ca9385 100644 (file)
@@ -174,7 +174,7 @@ template <bool Current, class T> bool VariantParser<DoCheck>::parse_attribute(co
         char token;
         size_t idx;
         std::stringstream ss(it->second);
-        while (ss >> token && (idx = pieceToChar.find(toupper(token))) != std::string::npos)
+        while (ss >> token && (idx = token == '*' ? size_t(ALL_PIECES) : pieceToChar.find(toupper(token))) != std::string::npos)
             set(PieceType(idx), target);
         if (DoCheck && idx == std::string::npos && token != '-')
             std::cerr << key << " - Invalid piece type: " << token << std::endl;
@@ -433,18 +433,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("extinctionPseudoRoyal", v->extinctionPseudoRoyal);
     parse_attribute("dupleCheck", v->dupleCheck);
     // extinction piece types
-    const auto& it_ext = config.find("extinctionPieceTypes");
-    if (it_ext != config.end())
-    {
-        v->extinctionPieceTypes = {};
-        char token;
-        size_t idx = 0;
-        std::stringstream ss(it_ext->second);
-        while (ss >> token && (idx = token == '*' ? size_t(ALL_PIECES) : v->pieceToChar.find(toupper(token))) != std::string::npos)
-            v->extinctionPieceTypes.insert(PieceType(idx));
-        if (DoCheck && idx == std::string::npos)
-            std::cerr << "extinctionPieceTypes - Invalid piece type: " << token << std::endl;
-    }
+    parse_attribute("extinctionPieceTypes", v->extinctionPieceTypes, v->pieceToChar);
     parse_attribute("extinctionPieceCount", v->extinctionPieceCount);
     parse_attribute("extinctionOpponentPieceCount", v->extinctionOpponentPieceCount);
     parse_attribute("flagPiece", v->flagPiece, v->pieceToChar);
@@ -468,8 +457,9 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     if (DoCheck)
     {
         // pieces
-        for (PieceType pt : v->pieceTypes)
+        for (PieceSet ps = v->pieceTypes; ps;)
         {
+            PieceType pt = pop_lsb(ps);
             for (Color c : {WHITE, BLACK})
                 if (std::count(v->pieceToChar.begin(), v->pieceToChar.end(), v->pieceToChar[make_piece(c, pt)]) != 1)
                     std::cerr << piece_name(pt) << " - Ambiguous piece character: " << v->pieceToChar[make_piece(c, pt)] << std::endl;
@@ -490,8 +480,9 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
             while (ss >> token)
                 if (isalpha(token) && v->pieceToChar.find(toupper(token)) == std::string::npos)
                     std::cerr << "pieceToCharTable - Invalid piece type: " << token << std::endl;
-            for (PieceType pt : v->pieceTypes)
+            for (PieceSet ps = v->pieceTypes; ps;)
             {
+                PieceType pt = pop_lsb(ps);
                 char ptl = tolower(v->pieceToChar[pt]);
                 if (v->pieceToCharTable.find(ptl) == std::string::npos && fenBoard.find(ptl) != std::string::npos)
                     std::cerr << "pieceToCharTable - Missing piece type: " << ptl << std::endl;
@@ -513,7 +504,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
         if (v->pieceDrops && (v->arrowGating || v->duckGating || v->staticGating || v->pastGating))
             std::cerr << "pieceDrops and arrowGating/duckGating are incompatible." << std::endl;
         // Options incompatible with royal kings
-        if (v->pieceTypes.find(KING) != v->pieceTypes.end())
+        if (v->pieceTypes & KING)
         {
             if (v->blastOnCapture)
                 std::cerr << "Can not use kings with blastOnCapture." << std::endl;
index 99966b3..a934c6b 100644 (file)
@@ -553,8 +553,9 @@ void Position::set_check_info(StateInfo* si) const {
 
   // For unused piece types, the check squares are left uninitialized
   si->nonSlidingRiders = 0;
-  for (PieceType pt : piece_types())
+  for (PieceSet ps = piece_types(); ps;)
   {
+      PieceType pt = pop_lsb(ps);
       si->checkSquares[pt] = ksq != SQ_NONE ? attacks_bb(~sideToMove, pt, ksq, pieces()) : Bitboard(0);
       // Collect special piece types that require slower check and evasion detection
       if (AttackRiderTypes[pt] & NON_SLIDING_RIDERS)
@@ -568,8 +569,9 @@ void Position::set_check_info(StateInfo* si) const {
   {
       si->pseudoRoyalCandidates = 0;
       si->pseudoRoyals = 0;
-      for (PieceType pt : extinction_piece_types())
+      for (PieceSet ps = extinction_piece_types(); ps;)
       {
+          PieceType pt = pop_lsb(ps);
           si->pseudoRoyalCandidates |= pieces(pt);
           if (count(sideToMove, pt) <= var->extinctionPieceCount + 1)
               si->pseudoRoyals |= pieces(sideToMove, pt);
@@ -835,8 +837,9 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
   }
   else
   {
-      for (PieceType pt : piece_types())
+      for (PieceSet ps = piece_types(); ps;)
       {
+          PieceType pt = pop_lsb(ps);
           Bitboard b = sliders & (PseudoAttacks[~c][pt][s] ^ LeaperAttacks[~c][pt][s]) & pieces(c, pt);
           if (b)
           {
@@ -925,7 +928,9 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c, Bitboard j
   }
 
   Bitboard b = 0;
-  for (PieceType pt : piece_types())
+  for (PieceSet ps = piece_types(); ps;)
+  {
+      PieceType pt = pop_lsb(ps);
       if (board_bb(c, pt) & s)
       {
           PieceType move_pt = pt == KING ? king_type() : pt;
@@ -945,6 +950,7 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c, Bitboard j
           else
               b |= attacks_bb(~c, move_pt, s, occupied) & pieces(c, pt);
       }
+  }
 
   // Janggi palace moves
   if (diagonal_lines() & s)
@@ -1093,7 +1099,7 @@ bool Position::legal(Move m) const {
       Bitboard pseudoRoyalsTheirs = st->pseudoRoyals & pieces(~sideToMove);
       if (is_ok(from) && (pseudoRoyals & from))
           pseudoRoyals ^= square_bb(from) ^ kto;
-      if (type_of(m) == PROMOTION && extinction_piece_types().find(promotion_type(m)) != extinction_piece_types().end())
+      if (type_of(m) == PROMOTION && (extinction_piece_types() & promotion_type(m)))
       {
           if (count(sideToMove, promotion_type(m)) > extinction_piece_count())
               // increase in count leads to loss of pseudo-royalty
@@ -1121,7 +1127,7 @@ bool Position::legal(Move m) const {
           Bitboard pseudoRoyalCandidates = st->pseudoRoyalCandidates & pieces(sideToMove);
           if (is_ok(from) && (pseudoRoyalCandidates & from))
               pseudoRoyalCandidates ^= square_bb(from) ^ kto;
-          if (type_of(m) == PROMOTION && extinction_piece_types().find(promotion_type(m)) != extinction_piece_types().end())
+          if (type_of(m) == PROMOTION && (extinction_piece_types() & promotion_type(m)))
               pseudoRoyalCandidates |= kto;
           bool allCheck = bool(pseudoRoyalCandidates);
           while (allCheck && pseudoRoyalCandidates)
@@ -2282,7 +2288,7 @@ Value Position::blast_see(Move m) const {
       while (attackers)
       {
           Square s = pop_lsb(attackers);
-          if (extinction_piece_types().find(type_of(piece_on(s))) == extinction_piece_types().end())
+          if (!(extinction_piece_types() & type_of(piece_on(s))))
               minAttacker = std::min(minAttacker, blast & s ? VALUE_ZERO : CapturePieceValue[MG][piece_on(s)]);
       }
 
@@ -2298,7 +2304,7 @@ Value Position::blast_see(Move m) const {
   while (blast)
   {
       Piece bpc = piece_on(pop_lsb(blast));
-      if (extinction_piece_types().find(type_of(bpc)) != extinction_piece_types().end())
+      if (extinction_piece_types() & type_of(bpc))
           return color_of(bpc) == us ?  extinction_value()
                         : capture(m) ? -extinction_value()
                                      : VALUE_ZERO;
@@ -2334,9 +2340,9 @@ bool Position::see_ge(Move m, Value threshold) const {
   // Extinction
   if (   extinction_value() != VALUE_NONE
       && piece_on(to)
-      && (   (   extinction_piece_types().find(type_of(piece_on(to))) != extinction_piece_types().end()
+      && (   (   (extinction_piece_types() & type_of(piece_on(to)))
               && pieceCount[piece_on(to)] == extinction_piece_count() + 1)
-          || (   extinction_piece_types().find(ALL_PIECES) != extinction_piece_types().end()
+          || (   (extinction_piece_types() & ALL_PIECES)
               && count<ALL_PIECES>(~sideToMove) == extinction_piece_count() + 1)))
       return extinction_value() < VALUE_ZERO;
 
@@ -2608,13 +2614,16 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
   if (extinction_value() != VALUE_NONE && (!var->extinctionPseudoRoyal || blast_on_capture()))
   {
       for (Color c : { ~sideToMove, sideToMove })
-          for (PieceType pt : extinction_piece_types())
+          for (PieceSet ps = extinction_piece_types(); ps;)
+          {
+              PieceType pt = pop_lsb(ps);
               if (   count_with_hand( c, pt) <= var->extinctionPieceCount
                   && count_with_hand(~c, pt) >= var->extinctionOpponentPieceCount + (extinction_claim() && c == sideToMove))
               {
                   result = c == sideToMove ? extinction_value(ply) : -extinction_value(ply);
                   return true;
               }
+          }
   }
   // capture the flag
   if (   capture_the_flag_piece()
@@ -2687,8 +2696,8 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
   if (two_boards() && !checkers())
   {
       int virtualCount = 0;
-      for (PieceType pt : piece_types())
-          virtualCount += std::max(-count_in_hand(~sideToMove, pt), 0);
+      for (PieceSet ps = piece_types(); ps;)
+          virtualCount += std::max(-count_in_hand(~sideToMove, pop_lsb(ps)), 0);
 
       if (virtualCount > 0)
       {
index fa8ffd6..2c2c94b 100644 (file)
@@ -125,13 +125,13 @@ public:
   bool two_boards() const;
   Bitboard board_bb() const;
   Bitboard board_bb(Color c, PieceType pt) const;
-  const std::set<PieceType>& piece_types() const;
+  PieceSet piece_types() const;
   const std::string& piece_to_char() const;
   const std::string& piece_to_char_synonyms() const;
   Bitboard promotion_zone(Color c) const;
   Square promotion_square(Color c, Square s) const;
   PieceType promotion_pawn_type(Color c) const;
-  const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types(Color c) const;
+  PieceSet promotion_piece_types(Color c) const;
   bool sittuyin_promotion() const;
   int promotion_limit(PieceType pt) const;
   PieceType promoted_piece_type(PieceType pt) const;
@@ -192,7 +192,7 @@ public:
   Value checkmate_value(int ply = 0) const;
   Value extinction_value(int ply = 0) const;
   bool extinction_claim() const;
-  const std::set<PieceType>& extinction_piece_types() const;
+  PieceSet extinction_piece_types() const;
   bool extinction_single_piece() const;
   int extinction_piece_count() const;
   int extinction_opponent_piece_count() const;
@@ -408,7 +408,7 @@ inline Bitboard Position::board_bb(Color c, PieceType pt) const {
   return var->mobilityRegion[c][pt] ? var->mobilityRegion[c][pt] & board_bb() : board_bb();
 }
 
-inline const std::set<PieceType>& Position::piece_types() const {
+inline PieceSet Position::piece_types() const {
   assert(var != nullptr);
   return var->pieceTypes;
 }
@@ -439,7 +439,7 @@ inline PieceType Position::promotion_pawn_type(Color c) const {
   return var->promotionPawnType[c];
 }
 
-inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types(Color c) const {
+inline PieceSet Position::promotion_piece_types(Color c) const {
   assert(var != nullptr);
   return var->promotionPieceTypes[c];
 }
@@ -866,8 +866,11 @@ inline Value Position::checkmate_value(int ply) const {
   if (two_boards() && var->checkmateValue < VALUE_ZERO)
   {
       Value virtualMaterial = VALUE_ZERO;
-      for (PieceType pt : piece_types())
+      for (PieceSet ps = piece_types(); ps;)
+      {
+          PieceType pt = pop_lsb(ps);
           virtualMaterial += std::max(-count_in_hand(~sideToMove, pt), 0) * PieceValue[MG][pt];
+      }
 
       if (virtualMaterial > 0)
           return -VALUE_VIRTUAL_MATE + virtualMaterial / 20 + ply;
@@ -886,7 +889,7 @@ inline bool Position::extinction_claim() const {
   return var->extinctionClaim;
 }
 
-inline const std::set<PieceType>& Position::extinction_piece_types() const {
+inline PieceSet Position::extinction_piece_types() const {
   assert(var != nullptr);
   return var->extinctionPieceTypes;
 }
@@ -894,9 +897,7 @@ inline const std::set<PieceType>& Position::extinction_piece_types() const {
 inline bool Position::extinction_single_piece() const {
   assert(var != nullptr);
   return   var->extinctionValue == -VALUE_MATE
-        && std::any_of(var->extinctionPieceTypes.begin(),
-                       var->extinctionPieceTypes.end(),
-                       [](PieceType pt) { return pt != ALL_PIECES; });
+        && (var->extinctionPieceTypes & ~piece_set(ALL_PIECES));
 }
 
 inline int Position::extinction_piece_count() const {
index afa1fde..c753c6a 100644 (file)
@@ -183,8 +183,9 @@ Score psq[PIECE_NB][SQUARE_NB + 1];
 void init(const Variant* v) {
 
   PieceType strongestPiece = NO_PIECE_TYPE;
-  for (PieceType pt : v->pieceTypes)
+  for (PieceSet ps = v->pieceTypes; ps;)
   {
+      PieceType pt = pop_lsb(ps);
       if (is_custom(pt))
       {
           PieceValue[MG][pt] = piece_value(MG, pt);
@@ -196,8 +197,8 @@ void init(const Variant* v) {
   }
 
   Value maxPromotion = VALUE_ZERO;
-  for (PieceType pt : v->promotionPieceTypes[WHITE])
-      maxPromotion = std::max(maxPromotion, PieceValue[EG][pt]);
+  for (PieceSet ps = v->promotionPieceTypes[WHITE]; ps;)
+      maxPromotion = std::max(maxPromotion, PieceValue[EG][pop_lsb(ps)]);
 
   for (PieceType pt = PAWN; pt <= KING; ++pt)
   {
@@ -261,7 +262,7 @@ void init(const Variant* v) {
       // In variants such as horde where all pieces need to be captured, weak pieces such as pawns are more useful
       if (   v->extinctionValue == -VALUE_MATE
           && v->extinctionPieceCount == 0
-          && v->extinctionPieceTypes.find(ALL_PIECES) != v->extinctionPieceTypes.end())
+          && (v->extinctionPieceTypes & ALL_PIECES))
           score += make_score(0, std::max(KnightValueEg - PieceValue[EG][pt], VALUE_ZERO) / 20);
 
       // The strongest piece of a variant usually has some dominance, such as rooks in Makruk and Xiangqi.
@@ -311,7 +312,7 @@ void init(const Variant* v) {
                                  : pt == KING  ? KingBonus[std::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)] * (1 + v->capturesToHand)
                                  : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)] * (1 + v->blastOnCapture)
                                  : pt == HORSE ? Bonus[KNIGHT][std::min(r, RANK_8)][std::min(f, FILE_D)]
-                                 : pt == COMMONER && v->extinctionValue == -VALUE_MATE && v->extinctionPieceTypes.find(COMMONER) != v->extinctionPieceTypes.end() ? KingBonus[std::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)]
+                                 : pt == COMMONER && v->extinctionValue == -VALUE_MATE && (v->extinctionPieceTypes & COMMONER) ? KingBonus[std::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)]
                                  : isSlider    ? make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile - 1)
                                  : isPawn      ? make_score(5, 5) * (2 * f - v->maxFile)
                                                : make_score(10, 10) * (1 + isSlowLeaper) * (f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile / 2));
index 67ee805..e1c34c9 100644 (file)
@@ -1646,7 +1646,7 @@ moves_loop: // When in check, search starts from here
           && !givesCheck
           && !(   pos.extinction_value() == -VALUE_MATE
                && pos.piece_on(to_sq(move))
-               && pos.extinction_piece_types().find(type_of(pos.piece_on(to_sq(move)))) != pos.extinction_piece_types().end())
+               && (pos.extinction_piece_types() & type_of(pos.piece_on(to_sq(move)))))
           &&  futilityBase > -VALUE_KNOWN_WIN
           &&  type_of(move) != PROMOTION)
       {
index fbf7367..ef99c4a 100644 (file)
@@ -419,7 +419,9 @@ enum Piece {
   PIECE_NB = 2 * PIECE_TYPE_NB
 };
 
-enum PieceSet : uint64_t {};
+enum PieceSet : uint64_t {
+  NO_PIECE_SET = 0
+};
 
 enum RiderType : int {
   NO_RIDER = 0,
@@ -629,11 +631,14 @@ constexpr PieceSet piece_set(PieceType pt) {
   return PieceSet(1ULL << pt);
 }
 
+constexpr PieceSet operator~ (PieceSet ps) { return (PieceSet)~(uint64_t)ps; }
 constexpr PieceSet operator| (PieceSet ps1, PieceSet ps2) { return (PieceSet)((uint64_t)ps1 | (uint64_t)ps2); }
+constexpr PieceSet operator| (PieceSet ps, PieceType pt) { return ps | piece_set(pt); }
 constexpr PieceSet operator& (PieceSet ps1, PieceSet ps2) { return (PieceSet)((uint64_t)ps1 & (uint64_t)ps2); }
 constexpr PieceSet operator& (PieceSet ps, PieceType pt) { return ps & piece_set(pt); }
 inline PieceSet& operator|= (PieceSet& ps1, PieceSet ps2) { return (PieceSet&)((uint64_t&)ps1 |= (uint64_t)ps2); }
 inline PieceSet& operator|= (PieceSet& ps, PieceType pt) { return ps |= piece_set(pt); }
+inline PieceSet& operator&= (PieceSet& ps1, PieceSet ps2) { return (PieceSet&)((uint64_t&)ps1 &= (uint64_t)ps2); }
 
 static_assert(piece_set(PAWN) & PAWN);
 static_assert(piece_set(KING) & KING);
index 2d24c0f..0326e04 100644 (file)
@@ -92,7 +92,7 @@ void on_variant_change(const Option &o) {
     // Do not send setup command for known variants
     if (standard_variants.find(o) != standard_variants.end())
         return;
-    int pocketsize = v->pieceDrops ? (v->pocketSize ? v->pocketSize : v->pieceTypes.size()) : 0;
+    int pocketsize = v->pieceDrops ? (v->pocketSize ? v->pocketSize : popcount(v->pieceTypes)) : 0;
     if (CurrentProtocol == XBOARD)
     {
         // Overwrite setup command for Janggi variants
@@ -115,8 +115,9 @@ void on_variant_change(const Option &o) {
                   << sync_endl;
         // Send piece command with Betza notation
         // https://www.gnu.org/software/xboard/Betza.html
-        for (PieceType pt : v->pieceTypes)
+        for (PieceSet ps = v->pieceTypes; ps;)
         {
+            PieceType pt = pop_lsb(ps);
             string suffix =   pt == PAWN && v->doubleStep     ? "ifmnD"
                             : pt == KING && v->cambodianMoves ? "ismN"
                             : pt == FERS && v->cambodianMoves ? "ifD"
index cb2ea69..c29ce1e 100644 (file)
@@ -161,8 +161,8 @@ namespace {
         v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w - - 0 1";
         v->promotionRegion[WHITE] = Rank6BB | Rank7BB | Rank8BB;
         v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
-        v->promotionPieceTypes[WHITE] = {MET};
-        v->promotionPieceTypes[BLACK] = {MET};
+        v->promotionPieceTypes[WHITE] = piece_set(MET);
+        v->promotionPieceTypes[BLACK] = piece_set(MET);
         v->doubleStep = false;
         v->castling = false;
         v->nMoveRule = 0;
@@ -206,8 +206,8 @@ namespace {
         v->add_piece(KHON, 'b');
         v->add_piece(MET, 'q');
         v->startFen = "rnbqkbnr/8/pppppppp/8/8/PPPPPPPP/8/RNBQKBNR w - - 0 1";
-        v->promotionPieceTypes[WHITE] = {ROOK, KNIGHT, KHON, MET};
-        v->promotionPieceTypes[BLACK] = {ROOK, KNIGHT, KHON, MET};
+        v->promotionPieceTypes[WHITE] = piece_set(ROOK) | KNIGHT | KHON | MET;
+        v->promotionPieceTypes[BLACK] = piece_set(ROOK) | KNIGHT | KHON | MET;
         v->doubleStep = false;
         v->castling = false;
         v->countingRule = ASEAN_COUNTING;
@@ -221,8 +221,8 @@ namespace {
         v->remove_piece(MET);
         v->add_piece(AIWOK, 'a');
         v->startFen = "rnsaksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKASNR w - - 0 1";
-        v->promotionPieceTypes[WHITE] = {AIWOK};
-        v->promotionPieceTypes[BLACK] = {AIWOK};
+        v->promotionPieceTypes[WHITE] = piece_set(AIWOK);
+        v->promotionPieceTypes[BLACK] = piece_set(AIWOK);
         return v;
     }
     // Shatranj
@@ -237,13 +237,13 @@ namespace {
         v->add_piece(ALFIL, 'b');
         v->add_piece(FERS, 'q');
         v->startFen = "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR w - - 0 1";
-        v->promotionPieceTypes[WHITE] = {FERS};
-        v->promotionPieceTypes[BLACK] = {FERS};
+        v->promotionPieceTypes[WHITE] = piece_set(FERS);
+        v->promotionPieceTypes[BLACK] = piece_set(FERS);
         v->doubleStep = false;
         v->castling = false;
         v->extinctionValue = -VALUE_MATE;
         v->extinctionClaim = true;
-        v->extinctionPieceTypes = {ALL_PIECES};
+        v->extinctionPieceTypes = piece_set(ALL_PIECES);
         v->extinctionPieceCount = 1;
         v->extinctionOpponentPieceCount = 2;
         v->stalemateValue = -VALUE_MATE;
@@ -269,8 +269,8 @@ namespace {
         v->remove_piece(QUEEN);
         v->add_piece(AMAZON, 'a');
         v->startFen = "rnbakbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBAKBNR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {AMAZON, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {AMAZON, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(AMAZON) | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(AMAZON) | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Nightrider chess
@@ -280,8 +280,8 @@ namespace {
         Variant* v = chess_variant_base()->init();
         v->remove_piece(KNIGHT);
         v->add_piece(CUSTOM_PIECES, 'n', "NN");
-        v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, BISHOP, CUSTOM_PIECES};
-        v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISHOP, CUSTOM_PIECES};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOK | BISHOP | CUSTOM_PIECES;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | BISHOP | CUSTOM_PIECES;
         return v;
     }
     // Grasshopper chess
@@ -289,8 +289,8 @@ namespace {
     Variant* grasshopper_variant() {
         Variant* v = chess_variant_base()->init();
         v->add_piece(CUSTOM_PIECES, 'g', "gQ");
-        v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES);
-        v->promotionPieceTypes[BLACK].insert(CUSTOM_PIECES);
+        v->promotionPieceTypes[WHITE] |= CUSTOM_PIECES;
+        v->promotionPieceTypes[BLACK] |= CUSTOM_PIECES;
         v->startFen = "rnbqkbnr/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/RNBQKBNR w KQkq - 0 1";
         v->doubleStep = false;
         return v;
@@ -304,8 +304,8 @@ namespace {
         v->remove_piece(BISHOP);
         v->add_piece(KNIBIS, 'n');
         v->add_piece(BISKNI, 'b');
-        v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, BISKNI, KNIBIS};
-        v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISKNI, KNIBIS};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOK | BISKNI | KNIBIS;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | BISKNI | KNIBIS;
         return v;
     }
     // New Zealand
@@ -317,8 +317,8 @@ namespace {
         v->add_piece(ROOKNI, 'r');
         v->add_piece(KNIROO, 'n');
         v->castlingRookPiece = ROOKNI;
-        v->promotionPieceTypes[WHITE] = {QUEEN, ROOKNI, BISHOP, KNIROO};
-        v->promotionPieceTypes[BLACK] = {QUEEN, ROOKNI, BISHOP, KNIROO};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOKNI | BISHOP | KNIROO;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOKNI | BISHOP | KNIROO;
         return v;
     }
     // King of the Hill
@@ -353,8 +353,8 @@ namespace {
         v->startFen = "rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1";
         v->kingType = KNIGHT;
         v->castlingKingPiece = KING;
-        v->promotionPieceTypes[WHITE] = {COMMONER, QUEEN, ROOK, BISHOP};
-        v->promotionPieceTypes[BLACK] = {COMMONER, QUEEN, ROOK, BISHOP};
+        v->promotionPieceTypes[WHITE] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP;
+        v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP;
         return v;
     }
     // Losers chess
@@ -364,7 +364,7 @@ namespace {
         v->checkmateValue = VALUE_MATE;
         v->stalemateValue = VALUE_MATE;
         v->extinctionValue = VALUE_MATE;
-        v->extinctionPieceTypes = {ALL_PIECES};
+        v->extinctionPieceTypes = piece_set(ALL_PIECES);
         v->extinctionPieceCount = 1;
         v->mustCapture = true;
         return v;
@@ -378,11 +378,11 @@ namespace {
         v->remove_piece(KING);
         v->add_piece(COMMONER, 'k');
         v->castlingKingPiece = COMMONER;
-        v->promotionPieceTypes[WHITE] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
         v->stalemateValue = VALUE_MATE;
         v->extinctionValue = VALUE_MATE;
-        v->extinctionPieceTypes = {ALL_PIECES};
+        v->extinctionPieceTypes = piece_set(ALL_PIECES);
         v->mustCapture = true;
         v->nnueAlias = "antichess";
         return v;
@@ -409,9 +409,9 @@ namespace {
     // http://www.binnewirtz.com/Schlagschach1.htm
     Variant* codrus_variant() {
         Variant* v = giveaway_variant()->init();
-        v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISHOP, KNIGHT};
-        v->extinctionPieceTypes = {COMMONER};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | BISHOP | KNIGHT;
+        v->extinctionPieceTypes = piece_set(COMMONER);
         return v;
     }
     // Extinction chess
@@ -421,19 +421,19 @@ namespace {
         v->remove_piece(KING);
         v->add_piece(COMMONER, 'k');
         v->castlingKingPiece = COMMONER;
-        v->promotionPieceTypes[WHITE] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT;
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT, PAWN};
+        v->extinctionPieceTypes = piece_set(COMMONER) | QUEEN | ROOK | BISHOP | KNIGHT | PAWN;
         return v;
     }
     // Kinglet
     // https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_chess
     Variant* kinglet_variant() {
         Variant* v = extinction_variant()->init();
-        v->promotionPieceTypes[WHITE] = {COMMONER};
-        v->promotionPieceTypes[BLACK] = {COMMONER};
-        v->extinctionPieceTypes = {PAWN};
+        v->promotionPieceTypes[WHITE] = piece_set(COMMONER);
+        v->promotionPieceTypes[BLACK] = piece_set(COMMONER);
+        v->extinctionPieceTypes = piece_set(PAWN);
         return v;
     }
     // Three Kings Chess
@@ -445,7 +445,7 @@ namespace {
         v->castlingKingPiece = COMMONER;
         v->startFen = "knbqkbnk/pppppppp/8/8/8/8/PPPPPPPP/KNBQKBNK w - - 0 1";
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {COMMONER};
+        v->extinctionPieceTypes = piece_set(COMMONER);
         v->extinctionPieceCount = 2;
         return v;
     }
@@ -457,7 +457,7 @@ namespace {
         v->doubleStepRegion[WHITE] |= Rank1BB;
         v->enPassantRegion = Rank3BB | Rank6BB; // exclude en passant on second rank
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {ALL_PIECES};
+        v->extinctionPieceTypes = piece_set(ALL_PIECES);
         return v;
     }
     // Atomic chess without checks (ICC rules)
@@ -469,7 +469,7 @@ namespace {
         v->add_piece(COMMONER, 'k');
         v->castlingKingPiece = COMMONER;
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {COMMONER};
+        v->extinctionPieceTypes = piece_set(COMMONER);
         v->blastOnCapture = true;
         v->nnueAlias = "atomic";
         return v;
@@ -489,7 +489,7 @@ namespace {
         v->add_piece(COMMONER, 'k');
         v->castlingKingPiece = COMMONER;
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {COMMONER};
+        v->extinctionPieceTypes = piece_set(COMMONER);
         v->duckGating = true;
         v->stalemateValue = VALUE_MATE;
         return v;
@@ -607,7 +607,7 @@ namespace {
         v->mustDrop = true;
         v->mustDropType = COMMONER;
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {COMMONER};
+        v->extinctionPieceTypes = piece_set(COMMONER);
         v->extinctionOpponentPieceCount = 2; // own all kings/commoners
         return v;
     }
@@ -676,8 +676,8 @@ namespace {
         v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[HEhe] w KQBCDFGkqbcdfg - 0 1";
         v->gating = true;
         v->seirawanGating = true;
-        v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // S-House
@@ -715,8 +715,8 @@ namespace {
         Variant *v = chess_variant_base()->init();
         v->remove_piece(BISHOP);
         v->add_piece(CUSTOM_PIECES, 'b', "BnN");
-        v->promotionPieceTypes[WHITE] = {QUEEN, CUSTOM_PIECES, ROOK, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {QUEEN, CUSTOM_PIECES, ROOK, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | CUSTOM_PIECES | ROOK | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | CUSTOM_PIECES | ROOK | KNIGHT;
         return v;
     }
     // Base used for most shogi variants
@@ -931,8 +931,8 @@ namespace {
         v->startFen = "rnqknr/pppppp/6/6/PPPPPP/RNQKNR w - - 0 1";
         v->promotionRegion[WHITE] = Rank6BB;
         v->promotionRegion[BLACK] = Rank1BB;
-        v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOK | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | KNIGHT;
         v->doubleStep = false;
         v->castling = false;
         return v;
@@ -959,8 +959,8 @@ namespace {
         v->remove_piece(QUEEN);
         v->add_piece(CHANCELLOR, 'c');
         v->startFen = "rnbckbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBCKBNR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {CHANCELLOR, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {CHANCELLOR, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(CHANCELLOR) | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(CHANCELLOR) | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Chigorin chess
@@ -971,8 +971,8 @@ namespace {
         v->pieceToCharTable = "PNBR............CKpnbrq............k";
         v->add_piece(CHANCELLOR, 'c');
         v->startFen = "rbbqkbbr/pppppppp/8/8/8/8/PPPPPPPP/RNNCKNNR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {CHANCELLOR, ROOK, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISHOP};
+        v->promotionPieceTypes[WHITE] = piece_set(CHANCELLOR) | ROOK | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | BISHOP;
         return v;
     }
     // Spartan chess
@@ -988,7 +988,7 @@ namespace {
         v->promotionPawnType[BLACK] = CUSTOM_PIECES;
         v->promotionPawnTypes[BLACK] = piece_set(CUSTOM_PIECES);
         v->nMoveRuleTypes[BLACK] = piece_set(CUSTOM_PIECES);
-        v->promotionPieceTypes[BLACK] = {COMMONER, DRAGON, ARCHBISHOP, CUSTOM_PIECES + 1, CUSTOM_PIECES + 2};
+        v->promotionPieceTypes[BLACK] = piece_set(COMMONER) | DRAGON | ARCHBISHOP | (CUSTOM_PIECES + 1) | (CUSTOM_PIECES + 2);
         v->promotionLimit[COMMONER] = 2;
         v->enPassantRegion = 0;
         v->extinctionPieceCount = 0;
@@ -1004,12 +1004,12 @@ namespace {
         v->remove_piece(QUEEN);
         v->add_piece(BERS, 'j');
         v->startFen = "rnbjkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBJKBNR w - - 0 1";
-        v->promotionPieceTypes[WHITE] = {BERS};
-        v->promotionPieceTypes[BLACK] = {BERS};
+        v->promotionPieceTypes[WHITE] = piece_set(BERS);
+        v->promotionPieceTypes[BLACK] = piece_set(BERS);
         v->doubleStep = false;
         v->castling = false;
         v->extinctionValue = VALUE_DRAW; // Robado
-        v->extinctionPieceTypes = {ALL_PIECES};
+        v->extinctionPieceTypes = piece_set(ALL_PIECES);
         v->extinctionPieceCount = 1;
         v->shatarMateRule = true;
         return v;
@@ -1020,7 +1020,7 @@ namespace {
     Variant* coregal_variant() {
         Variant* v = chess_variant_base()->init();
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {QUEEN};
+        v->extinctionPieceTypes = piece_set(QUEEN);
         v->extinctionPseudoRoyal = true;
         v->extinctionPieceCount = 64; // no matter how many queens, all are royal
         return v;
@@ -1169,7 +1169,7 @@ namespace {
         v->promotedPieceType[CUSTOM_PIECES] = COMMONER;
         v->castlingKingPiece = COMMONER;
         v->extinctionValue = -VALUE_MATE;
-        v->extinctionPieceTypes = {COMMONER};
+        v->extinctionPieceTypes = piece_set(COMMONER);
         v->extinctionPseudoRoyal = true;
         v->extinctionPieceCount = 0;
         return v;
@@ -1239,8 +1239,8 @@ namespace {
         v->add_piece(ARCHBISHOP, 'a');
         v->add_piece(CHANCELLOR, 'c');
         v->startFen = "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Capahouse
@@ -1283,8 +1283,8 @@ namespace {
         v->castlingQueensideFile = FILE_B;
         v->add_piece(ARCHBISHOP, 'j');
         v->startFen = "rjnbkqbnjr/pppppppppp/10/10/10/10/PPPPPPPPPP/RJNBKQBNJR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(ARCHBISHOP) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(ARCHBISHOP) | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Modern chess
@@ -1303,8 +1303,8 @@ namespace {
         v->castlingQueensideFile = FILE_C;
         v->add_piece(ARCHBISHOP, 'm');
         v->startFen = "rnbqkmbnr/ppppppppp/9/9/9/9/9/PPPPPPPPP/RNBMKQBNR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(ARCHBISHOP) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(ARCHBISHOP) | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Chancellor chess
@@ -1323,8 +1323,8 @@ namespace {
         v->castlingQueensideFile = FILE_C;
         v->add_piece(CHANCELLOR, 'c');
         v->startFen = "rnbqkcnbr/ppppppppp/9/9/9/9/9/PPPPPPPPP/RNBQKCNBR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(CHANCELLOR) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(CHANCELLOR) | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Embassy chess
@@ -1350,8 +1350,8 @@ namespace {
         v->castlingQueensideFile = FILE_C;
         v->add_piece(CENTAUR, 'c');
         v->startFen = "rcnbqkbncr/pppppppppp/10/10/10/10/PPPPPPPPPP/RCNBQKBNCR w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {CENTAUR, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {CENTAUR, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(CENTAUR) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(CENTAUR) | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Gustav III chess
@@ -1366,8 +1366,8 @@ namespace {
         v->castlingQueensideFile = FILE_D;
         v->add_piece(AMAZON, 'a');
         v->startFen = "arnbqkbnra/*pppppppp*/*8*/*8*/*8*/*8*/*PPPPPPPP*/ARNBQKBNRA w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = {AMAZON, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {AMAZON, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(AMAZON) | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(AMAZON) | QUEEN | ROOK | BISHOP | KNIGHT;
         return v;
     }
     // Jeson mor
@@ -1402,13 +1402,13 @@ namespace {
         v->add_piece(COMMONER, 'm');
         v->add_piece(WAZIR, 'w');
         v->startFen = "rnebmk1wbenr/1ppppp1pppp1/6f5/p5p4p/P5P4P/6F5/1PPPPP1PPPP1/RNEBMK1WBENR w - - 0 1";
-        v->promotionPieceTypes[WHITE] = {FERS};
-        v->promotionPieceTypes[BLACK] = {FERS};
+        v->promotionPieceTypes[WHITE] = piece_set(FERS);
+        v->promotionPieceTypes[BLACK] = piece_set(FERS);
         v->doubleStep = false;
         v->castling = false;
         v->extinctionValue = -VALUE_MATE;
         v->extinctionClaim = true;
-        v->extinctionPieceTypes = {ALL_PIECES};
+        v->extinctionPieceTypes = piece_set(ALL_PIECES);
         v->extinctionPieceCount = 1;
         v->extinctionOpponentPieceCount = 2;
         v->stalemateValue = -VALUE_MATE;
@@ -1426,8 +1426,8 @@ namespace {
         v->add_piece(ARCHBISHOP, 'a');
         v->add_piece(CHANCELLOR, 'c');
         v->startFen = "r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R w - - 0 1";
-        v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN | ROOK | BISHOP | KNIGHT;
         v->promotionRegion[WHITE] = Rank8BB | Rank9BB | Rank10BB;
         v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
         v->promotionLimit[ARCHBISHOP] = 1;
@@ -1454,10 +1454,10 @@ namespace {
         v->add_piece(CUSTOM_PIECES + 1, 'w', "CF");
         v->add_piece(CUSTOM_PIECES + 2, 'l', "FDH");
         v->startFen = "rw6wr/clbnqknbla/pppppppppp/10/10/10/10/PPPPPPPPPP/CLBNQKNBLA/RW6WR w - - 0 1";
-        v->promotionPieceTypes[WHITE].erase(KNIGHT);
-        v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES);
-        v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES + 1);
-        v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES + 2);
+        v->promotionPieceTypes[WHITE] &= ~piece_set(KNIGHT);
+        v->promotionPieceTypes[WHITE] |= CUSTOM_PIECES;
+        v->promotionPieceTypes[WHITE] |= CUSTOM_PIECES + 1;
+        v->promotionPieceTypes[WHITE] |= CUSTOM_PIECES + 2;
         v->promotionPieceTypes[BLACK] = v->promotionPieceTypes[WHITE];
         v->promotionLimit[CUSTOM_PIECES] = 2;
         v->promotionLimit[CUSTOM_PIECES + 1] = 2;
@@ -1476,8 +1476,8 @@ namespace {
         v->add_piece(CHANCELLOR, 'm');
         v->add_piece(CUSTOM_PIECES, 'c', "DAW"); // Champion
         v->add_piece(CUSTOM_PIECES + 1, 'w', "CF"); // Wizard
-        v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN};
-        v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN};
+        v->promotionPieceTypes[WHITE] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN;
+        v->promotionPieceTypes[BLACK] = piece_set(ARCHBISHOP) | CHANCELLOR | QUEEN;
         v->promotionRegion[WHITE] = Rank10BB;
         v->promotionRegion[BLACK] = Rank1BB;
         v->doubleStepRegion[WHITE] = Rank3BB;
@@ -1501,8 +1501,8 @@ namespace {
         v->castlingRank = RANK_2;
         v->promotionRegion[WHITE] = Rank9BB | Rank10BB;
         v->promotionRegion[BLACK] = Rank2BB | Rank1BB;
-        v->promotionPieceTypes[WHITE] = {CUSTOM_PIECES + 1, CUSTOM_PIECES, QUEEN, ROOK, BISHOP, KNIGHT};
-        v->promotionPieceTypes[BLACK] = {CUSTOM_PIECES + 1, CUSTOM_PIECES, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionPieceTypes[WHITE] = piece_set(CUSTOM_PIECES + 1) | CUSTOM_PIECES | QUEEN | ROOK | BISHOP | KNIGHT;
+        v->promotionPieceTypes[BLACK] = piece_set(CUSTOM_PIECES + 1) | CUSTOM_PIECES | QUEEN | ROOK | BISHOP | KNIGHT;
         v->doubleStepRegion[WHITE] = Rank3BB;
         v->doubleStepRegion[BLACK] = Rank8BB;
         return v;
@@ -1535,8 +1535,8 @@ namespace {
         v->startFen = "qwfrbbnk/pssppssp/1pp2pp1/8/8/8/8/1PP2PP1/PSSPPSSP/KNBBRFWQ w - - 0 1";
         v->promotionPawnType[WHITE] = v->promotionPawnType[BLACK] = PAWN;
         v->promotionPawnTypes[WHITE] = v->promotionPawnTypes[BLACK] = piece_set(PAWN) | piece_set(CUSTOM_PIECES);
-        v->promotionPieceTypes[WHITE] = {QUEEN, CHANCELLOR, ARCHBISHOP, ROOK, BISHOP};
-        v->promotionPieceTypes[BLACK] = {QUEEN, CHANCELLOR, ARCHBISHOP, ROOK, BISHOP};
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | CHANCELLOR | ARCHBISHOP | ROOK | BISHOP;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | CHANCELLOR | ARCHBISHOP | ROOK | BISHOP;
         v->promotedPieceType[PAWN] = CUSTOM_PIECES + 2;
         v->promotionRegion[WHITE] = Rank10BB;
         v->promotionRegion[BLACK] = Rank1BB;
@@ -1558,8 +1558,8 @@ namespace {
         v->add_piece(FERS_ALFIL, 'e');
         v->add_piece(CANNON, 'c');
         v->startFen = "c8c/ernbqkbnre/pppppppppp/10/10/10/10/PPPPPPPPPP/ERNBQKBNRE/C8C w KQkq - 0 1";
-        v->promotionPieceTypes[WHITE] = { QUEEN, ROOK, BISHOP, KNIGHT, CANNON, FERS_ALFIL };
-        v->promotionPieceTypes[BLACK] = { QUEEN, ROOK, BISHOP, KNIGHT, CANNON, FERS_ALFIL };
+        v->promotionPieceTypes[WHITE] = piece_set(QUEEN) | ROOK | BISHOP | KNIGHT | CANNON | FERS_ALFIL ;
+        v->promotionPieceTypes[BLACK] = piece_set(QUEEN) | ROOK | BISHOP | KNIGHT | CANNON | FERS_ALFIL ;
         v->promotionRegion[WHITE] = Rank10BB;
         v->promotionRegion[BLACK] = Rank1BB;
         v->castlingKingsideFile = FILE_H;
index 2a9d277..3af6138 100644 (file)
@@ -19,6 +19,7 @@
 #ifndef VARIANT_H_INCLUDED
 #define VARIANT_H_INCLUDED
 
+#include <bitset>
 #include <set>
 #include <map>
 #include <vector>
@@ -44,7 +45,7 @@ struct Variant {
   bool twoBoards = false;
   int pieceValue[PHASE_NB][PIECE_TYPE_NB] = {};
   std::string customPiece[CUSTOM_PIECES_NB] = {};
-  std::set<PieceType> pieceTypes = { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING };
+  PieceSet pieceTypes = piece_set(PAWN) | KNIGHT | BISHOP | ROOK | QUEEN | KING;
   std::string pieceToChar =  " PNBRQ" + std::string(KING - QUEEN - 1, ' ') + "K" + std::string(PIECE_TYPE_NB - KING - 1, ' ')
                            + " pnbrq" + std::string(KING - QUEEN - 1, ' ') + "k" + std::string(PIECE_TYPE_NB - KING - 1, ' ');
   std::string pieceToCharSynonyms = std::string(PIECE_NB, ' ');
@@ -53,8 +54,8 @@ struct Variant {
   Bitboard promotionRegion[COLOR_NB] = {Rank8BB, Rank1BB};
   PieceType promotionPawnType[COLOR_NB] = {PAWN, PAWN};
   PieceSet promotionPawnTypes[COLOR_NB] = {piece_set(PAWN), piece_set(PAWN)};
-  std::set<PieceType, std::greater<PieceType> > promotionPieceTypes[COLOR_NB] = {{ QUEEN, ROOK, BISHOP, KNIGHT },
-                                                                                 { QUEEN, ROOK, BISHOP, KNIGHT }};
+  PieceSet promotionPieceTypes[COLOR_NB] = {piece_set(QUEEN) | ROOK | BISHOP | KNIGHT,
+                                            piece_set(QUEEN) | ROOK | BISHOP | KNIGHT};
   bool sittuyinPromotion = false;
   int promotionLimit[PIECE_TYPE_NB] = {}; // 0 means unlimited
   PieceType promotedPieceType[PIECE_TYPE_NB] = {};
@@ -135,7 +136,7 @@ struct Variant {
   bool extinctionClaim = false;
   bool extinctionPseudoRoyal = false;
   bool dupleCheck = false;
-  std::set<PieceType> extinctionPieceTypes = {};
+  PieceSet extinctionPieceTypes = NO_PIECE_SET;
   int extinctionPieceCount = 0;
   int extinctionOpponentPieceCount = 0;
   PieceType flagPiece = NO_PIECE_TYPE;
@@ -170,7 +171,7 @@ struct Variant {
       pieceToChar[make_piece(BLACK, pt)] = tolower(c);
       pieceToCharSynonyms[make_piece(WHITE, pt)] = toupper(c2);
       pieceToCharSynonyms[make_piece(BLACK, pt)] = tolower(c2);
-      pieceTypes.insert(pt);
+      pieceTypes |= pt;
       // Add betza notation for custom piece
       if (is_custom(pt))
           customPiece[pt - CUSTOM_PIECES] = betza;
@@ -185,19 +186,19 @@ struct Variant {
       pieceToChar[make_piece(BLACK, pt)] = ' ';
       pieceToCharSynonyms[make_piece(WHITE, pt)] = ' ';
       pieceToCharSynonyms[make_piece(BLACK, pt)] = ' ';
-      pieceTypes.erase(pt);
+      pieceTypes &= ~piece_set(pt);
       // erase from promotion types to ensure consistency
-      promotionPieceTypes[WHITE].erase(pt);
-      promotionPieceTypes[BLACK].erase(pt);
+      promotionPieceTypes[WHITE] &= ~piece_set(pt);
+      promotionPieceTypes[BLACK] &= ~piece_set(pt);
   }
 
   void reset_pieces() {
       pieceToChar = std::string(PIECE_NB, ' ');
       pieceToCharSynonyms = std::string(PIECE_NB, ' ');
-      pieceTypes.clear();
+      pieceTypes = NO_PIECE_SET;
       // clear promotion types to ensure consistency
-      promotionPieceTypes[WHITE].clear();
-      promotionPieceTypes[BLACK].clear();
+      promotionPieceTypes[WHITE] = NO_PIECE_SET;
+      promotionPieceTypes[BLACK] = NO_PIECE_SET;
   }
 
   // Reset values that always need to be redefined
@@ -214,29 +215,33 @@ struct Variant {
       if (!doubleStepRegion[WHITE] && !doubleStepRegion[BLACK])
           doubleStep = false;
 
-      fastAttacks = std::all_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
-                                    return (   pt < FAIRY_PIECES
-                                            || pt == COMMONER || pt == IMMOBILE_PIECE
-                                            || pt == ARCHBISHOP || pt == CHANCELLOR
-                                            || (pt == KING && kingType == KING))
-                                          && !(mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]);
-                                })
-                    && !cambodianMoves
-                    && !diagonalLines;
-      fastAttacks2 = std::all_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
-                                    return (   pt < FAIRY_PIECES
-                                            || pt == COMMONER || pt == FERS || pt == WAZIR || pt == BREAKTHROUGH_PIECE
-                                            || pt == SHOGI_PAWN || pt == GOLD || pt == SILVER || pt == SHOGI_KNIGHT
-                                            || pt == DRAGON || pt == DRAGON_HORSE || pt == LANCE
-                                            || (pt == KING && kingType == KING))
-                                          && !(mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]);
-                                })
-                    && !cambodianMoves
-                    && !diagonalLines;
+      fastAttacks = !cambodianMoves && !diagonalLines;
+      for (PieceSet ps = pieceTypes; fastAttacks && ps;)
+      {
+          PieceType pt = pop_lsb(ps);
+          if (!(   pt < FAIRY_PIECES
+                || pt == COMMONER || pt == IMMOBILE_PIECE
+                || pt == ARCHBISHOP || pt == CHANCELLOR
+                || (pt == KING && kingType == KING))
+              || (mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]))
+            fastAttacks = false;
+      }
+      fastAttacks2 = !cambodianMoves && !diagonalLines;
+      for (PieceSet ps = pieceTypes; fastAttacks2 && ps;)
+      {
+          PieceType pt = pop_lsb(ps);
+          if (!(   pt < FAIRY_PIECES
+                || pt == COMMONER || pt == FERS || pt == WAZIR || pt == BREAKTHROUGH_PIECE
+                || pt == SHOGI_PAWN || pt == GOLD || pt == SILVER || pt == SHOGI_KNIGHT
+                || pt == DRAGON || pt == DRAGON_HORSE || pt == LANCE
+                || (pt == KING && kingType == KING))
+              || (mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt]))
+            fastAttacks2 = false;
+      }
 
       // Initialize calculated NNUE properties
-      nnueKing =  pieceTypes.find(KING) != pieceTypes.end() ? KING
-                : extinctionPieceCount == 0 && extinctionPieceTypes.find(COMMONER) != extinctionPieceTypes.end() ? COMMONER
+      nnueKing =  pieceTypes & KING ? KING
+                : extinctionPieceCount == 0 && (extinctionPieceTypes & COMMONER) ? COMMONER
                 : NO_PIECE_TYPE;
       if (nnueKing != NO_PIECE_TYPE)
       {
@@ -246,14 +251,16 @@ struct Variant {
               || std::count(fenBoard.begin(), fenBoard.end(), pieceToChar[make_piece(BLACK, nnueKing)]) != 1)
               nnueKing = NO_PIECE_TYPE;
       }
+      // We can not use popcount here yet, as the lookup tables are initialized after the variants
       int nnueSquares = (maxRank + 1) * (maxFile + 1);
-      nnueUsePockets = (pieceDrops && (capturesToHand || (!mustDrop && pieceTypes.size() != 1))) || seirawanGating;
+      nnueUsePockets = (pieceDrops && (capturesToHand || (!mustDrop && std::bitset<64>(pieceTypes).count() != 1))) || seirawanGating;
       int nnuePockets = nnueUsePockets ? 2 * int(maxFile + 1) : 0;
-      int nnueNonDropPieceIndices = (2 * pieceTypes.size() - (nnueKing != NO_PIECE_TYPE)) * nnueSquares;
-      int nnuePieceIndices = nnueNonDropPieceIndices + 2 * (pieceTypes.size() - (nnueKing != NO_PIECE_TYPE)) * nnuePockets;
+      int nnueNonDropPieceIndices = (2 * std::bitset<64>(pieceTypes).count() - (nnueKing != NO_PIECE_TYPE)) * nnueSquares;
+      int nnuePieceIndices = nnueNonDropPieceIndices + 2 * (std::bitset<64>(pieceTypes).count() - (nnueKing != NO_PIECE_TYPE)) * nnuePockets;
       int i = 0;
-      for (PieceType pt : pieceTypes)
+      for (PieceSet ps = pieceTypes; ps;)
       {
+          PieceType pt = pop_lsb(ps);
           for (Color c : { WHITE, BLACK})
           {
               pieceSquareIndex[c][make_piece(c, pt)] = 2 * i * nnueSquares;
@@ -300,10 +307,7 @@ struct Variant {
 
       // For endgame evaluation to be applicable, no special win rules must apply.
       // Furthermore, rules significantly changing game mechanics also invalidate it.
-      endgameEval = std::none_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
-                                    return mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt];
-                                })
-                    && extinctionValue == VALUE_NONE
+      endgameEval = extinctionValue == VALUE_NONE
                     && checkmateValue == -VALUE_MATE
                     && stalemateValue == VALUE_DRAW
                     && !materialCounting
@@ -316,7 +320,13 @@ struct Variant {
                     && !capturesToHand
                     && !twoBoards
                     && kingType == KING;
-    
+      for (PieceSet ps = pieceTypes; endgameEval && ps;)
+      {
+          PieceType pt = pop_lsb(ps);
+          if (mobilityRegion[WHITE][pt] || mobilityRegion[BLACK][pt])
+              endgameEval = false;
+      }
+
       shogiStylePromotions = false;
       for (PieceType current: promotedPieceType)
           if (current != NO_PIECE_TYPE)