Precalculate endgameEval flag
authorFabian Fichter <ianfab@users.noreply.github.com>
Sun, 7 Mar 2021 17:42:06 +0000 (18:42 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Sun, 7 Mar 2021 18:38:21 +0000 (19:38 +0100)
This supersedes manual definition of the endgame evalution flag.

For a few variants the automatically calculated flag now enables
endgame evaluation where before it was explicitly disabled.

losalamos STC
LLR: 2.97 (-2.94,2.94) [-10.00,5.00]
Total: 1151 W: 292 L: 253 D: 606
http://www.variantfishtest.org:6543/tests/view/6044ea416e23db669974ea05

Closes #271.

src/material.cpp
src/parser.cpp
src/position.cpp
src/position.h
src/variant.cpp
src/variant.h
src/variants.ini

index 79feda2..5c4a055 100644 (file)
@@ -164,7 +164,7 @@ Entry* probe(const Position& pos) {
   {
       Value npm2 = VALUE_ZERO;
       for (PieceType pt : pos.piece_types())
-          npm2 += (pos.count_in_hand(WHITE, pt) + pos.count_in_hand(BLACK, pt)) * PieceValue[MG][make_piece(WHITE, pt)];
+          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.max_file() + 1) * (pos.max_rank() + 1));
index dbe3847..de075a5 100644 (file)
@@ -238,7 +238,6 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     parse_attribute("mandatoryPiecePromotion", v->mandatoryPiecePromotion);
     parse_attribute("pieceDemotion", v->pieceDemotion);
     parse_attribute("blastOnCapture", v->blastOnCapture);
-    parse_attribute("endgameEval", v->endgameEval);
     parse_attribute("doubleStep", v->doubleStep);
     parse_attribute("doubleStepRank", v->doubleStepRank);
     parse_attribute("doubleStepRankMin", v->doubleStepRankMin);
index 9f4bb90..d1a7e1d 100644 (file)
@@ -660,7 +660,7 @@ const string Position::fen(bool sfen, bool showPromoted, int countStarted, std::
                       ss << pieceCountInHand[c][pt];
                   ss << piece_to_char()[make_piece(c, pt)];
               }
-      if (!count_in_hand(WHITE, ALL_PIECES) && !count_in_hand(BLACK, ALL_PIECES))
+      if (!count_in_hand(ALL_PIECES))
           ss << '-';
       ss << " " << gamePly + 1;
       return ss.str();
index 59df07a..d47e61f 100644 (file)
@@ -192,6 +192,7 @@ public:
   CountingRule counting_rule() const;
 
   // Variant-specific properties
+  int count_in_hand(PieceType pt) const;
   int count_in_hand(Color c, PieceType pt) const;
   int count_with_hand(Color c, PieceType pt) const;
   bool bikjang() const;
@@ -440,7 +441,7 @@ inline bool Position::blast_on_capture() const {
 
 inline bool Position::endgame_eval() const {
   assert(var != nullptr);
-  return var->endgameEval && !count_in_hand(WHITE, ALL_PIECES) && !count_in_hand(BLACK, ALL_PIECES);
+  return var->endgameEval && !count_in_hand(ALL_PIECES) && count<KING>() == 2;
 }
 
 inline bool Position::double_step_enabled() const {
@@ -1227,6 +1228,10 @@ inline StateInfo* Position::state() const {
 
 // Variant-specific
 
+inline int Position::count_in_hand(PieceType pt) const {
+  return pieceCountInHand[WHITE][pt] + pieceCountInHand[BLACK][pt];
+}
+
 inline int Position::count_in_hand(Color c, PieceType pt) const {
   return pieceCountInHand[c][pt];
 }
index be41e42..07d3035 100644 (file)
@@ -31,15 +31,17 @@ VariantMap variants; // Global object
 
 namespace {
     // Define variant rules
-    Variant* fairy_variant_base() {
+    Variant* variant_base() {
         Variant* v = new Variant();
+        return v;
+    }
+    Variant* chess_variant_base() {
+        Variant* v = variant_base();
         v->pieceToCharTable = "PNBRQ................Kpnbrq................k";
-        v->endgameEval = false;
         return v;
     }
     Variant* chess_variant() {
-        Variant* v = fairy_variant_base();
-        v->endgameEval = true;
+        Variant* v = chess_variant_base();
         v->nnueFeatures = NNUE_CHESS;
         return v;
     }
@@ -57,19 +59,19 @@ namespace {
     // Armageddon Chess
     // https://en.wikipedia.org/wiki/Fast_chess#Armageddon
     Variant* armageddon_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant();
         v->materialCounting = BLACK_DRAW_ODDS;
         return v;
     }
     Variant* fairy_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->add_piece(SILVER, 's');
         v->add_piece(FERS, 'f');
         return v;
     }
     // Makruk (Thai Chess)
     Variant* makruk_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "makruk";
         v->pieceToCharTable = "PN.R.M....SKpn.r.m....sk";
         v->remove_piece(BISHOP);
@@ -106,7 +108,7 @@ namespace {
         return v;
     }
     Variant* asean_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->remove_piece(BISHOP);
         v->remove_piece(QUEEN);
         v->add_piece(KHON, 'b');
@@ -128,7 +130,7 @@ namespace {
         return v;
     }
     Variant* shatranj_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "shatranj";
         v->pieceToCharTable = "PN.R.QB....Kpn.r.qb....k";
         v->remove_piece(BISHOP);
@@ -158,7 +160,7 @@ namespace {
         return v;
     }
     Variant* amazon_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBR..............AKpnbr..............ak";
         v->remove_piece(QUEEN);
         v->add_piece(AMAZON, 'a');
@@ -167,7 +169,7 @@ namespace {
         return v;
     }
     Variant* hoppelpoppel_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->remove_piece(KNIGHT);
         v->remove_piece(BISHOP);
         v->add_piece(KNIBIS, 'n');
@@ -176,7 +178,7 @@ namespace {
         return v;
     }
     Variant* newzealand_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->remove_piece(ROOK);
         v->remove_piece(KNIGHT);
         v->add_piece(ROOKNI, 'r');
@@ -186,7 +188,7 @@ namespace {
         return v;
     }
     Variant* kingofthehill_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->flagPiece = KING;
         v->whiteFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
         v->blackFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
@@ -194,7 +196,7 @@ namespace {
         return v;
     }
     Variant* racingkings_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->startFen = "8/8/8/8/8/8/krbnNBRK/qrbnNBRQ w - - 0 1";
         v->flagPiece = KING;
         v->whiteFlag = Rank8BB;
@@ -205,7 +207,7 @@ namespace {
         return v;
     }
     Variant* knightmate_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->add_piece(COMMONER, 'm');
         v->remove_piece(KNIGHT);
         v->startFen = "rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1";
@@ -215,7 +217,7 @@ namespace {
         return v;
     }
     Variant* losers_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->checkmateValue = VALUE_MATE;
         v->stalemateValue = VALUE_MATE;
         v->extinctionValue = VALUE_MATE;
@@ -225,7 +227,7 @@ namespace {
         return v;
     }
     Variant* giveaway_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "giveaway";
         v->remove_piece(KING);
         v->add_piece(COMMONER, 'k');
@@ -255,7 +257,7 @@ namespace {
         return v;
     }
     Variant* extinction_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->remove_piece(KING);
         v->add_piece(COMMONER, 'k');
         v->castlingKingPiece = COMMONER;
@@ -273,7 +275,7 @@ namespace {
     // Three Kings Chess
     // https://github.com/cutechess/cutechess/blob/master/projects/lib/src/board/threekingsboard.h
     Variant* threekings_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->remove_piece(KING);
         v->add_piece(COMMONER, 'k');
         v->castlingKingPiece = COMMONER;
@@ -286,7 +288,7 @@ namespace {
     // Horde chess
     // https://en.wikipedia.org/wiki/Dunsany%27s_chess#Horde_chess
     Variant* horde_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->startFen = "rnbqkbnr/pppppppp/8/1PP2PP1/PPPPPPPP/PPPPPPPP/PPPPPPPP/PPPPPPPP w kq - 0 1";
         v->doubleStepRankMin = RANK_1;
         v->enPassantRegion = Rank3BB | Rank6BB; // exclude en passant on second rank
@@ -297,7 +299,7 @@ namespace {
     // Atomic chess without checks (ICC rules)
     // https://www.chessclub.com/help/atomic
     Variant* nocheckatomic_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "atomic";
         v->remove_piece(KING);
         v->add_piece(COMMONER, 'k');
@@ -315,7 +317,7 @@ namespace {
         return v;
     }
     Variant* threecheck_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 3+3 0 1";
         v->checkCounting = true;
         return v;
@@ -326,7 +328,7 @@ namespace {
         return v;
     }
     Variant* crazyhouse_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "crazyhouse";
         v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[] w KQkq - 0 1";
         v->pieceDrops = true;
@@ -366,7 +368,7 @@ namespace {
         return v;
     }
     Variant* pocketknight_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "bughouse";
         v->pocketSize = 2;
         v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[Nn] w KQkq - 0 1";
@@ -375,7 +377,7 @@ namespace {
         return v;
     }
     Variant* placement_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "bughouse";
         v->startFen = "8/pppppppp/8/8/8/8/PPPPPPPP/8[KQRRBBNNkqrrbbnn] w - - 0 1";
         v->mustDrop = true;
@@ -409,7 +411,7 @@ namespace {
         return v;
     }
     Variant* seirawan_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "seirawan";
         v->pieceToCharTable = "PNBRQ.E..........H...Kpnbrq.e..........h...k";
         v->add_piece(ARCHBISHOP, 'h');
@@ -428,7 +430,7 @@ namespace {
         return v;
     }
     Variant* minishogi_variant_base() {
-        Variant* v = fairy_variant_base();
+        Variant* v = variant_base();
         v->variantTemplate = "shogi";
         v->maxRank = RANK_5;
         v->maxFile = FILE_E;
@@ -561,7 +563,7 @@ namespace {
         return v;
     }
     Variant* losalamos_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PN.RQ................Kpn.rq................k";
         v->maxRank = RANK_6;
         v->maxFile = FILE_F;
@@ -574,7 +576,7 @@ namespace {
         return v;
     }
     Variant* gardner_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->maxRank = RANK_5;
         v->maxFile = FILE_E;
         v->startFen = "rnbqk/ppppp/5/PPPPP/RNBQK w - - 0 1";
@@ -584,7 +586,7 @@ namespace {
         return v;
     }
     Variant* almost_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBR............CKpnbr............ck";
         v->remove_piece(QUEEN);
         v->add_piece(CHANCELLOR, 'c');
@@ -593,7 +595,7 @@ namespace {
         return v;
     }
     Variant* chigorin_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBR............CKpnbrq............k";
         v->add_piece(CHANCELLOR, 'c');
         v->startFen = "rbbqkbbr/pppppppp/8/8/8/8/PPPPPPPP/RNNCKNNR w KQkq - 0 1";
@@ -601,7 +603,7 @@ namespace {
         return v;
     }
     Variant* shatar_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBR..........J......Kpnbr..........j......k";
         v->remove_piece(QUEEN);
         v->add_piece(BERS, 'j');
@@ -616,7 +618,7 @@ namespace {
         return v;
     }
     Variant* coregal_variant() {
-        Variant* v = fairy_variant();
+        Variant* v = chess_variant_base();
         v->extinctionValue = -VALUE_MATE;
         v->extinctionPieceTypes = {QUEEN};
         v->extinctionPseudoRoyal = true;
@@ -624,7 +626,7 @@ namespace {
         return v;
     }
     Variant* clobber_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "P.................p.................";
         v->maxRank = RANK_6;
         v->maxFile = FILE_E;
@@ -639,7 +641,7 @@ namespace {
         return v;
     }
     Variant* breakthrough_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "P.................p.................";
         v->reset_pieces();
         v->add_piece(BREAKTHROUGH_PIECE, 'p');
@@ -654,7 +656,7 @@ namespace {
         return v;
     }
     Variant* ataxx_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "P.................p.................";
         v->maxRank = RANK_7;
         v->maxFile = FILE_G;
@@ -675,7 +677,7 @@ namespace {
         return v;
     }
     Variant* minixiangqi_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->variantTemplate = "xiangqi";
         v->pieceToCharTable = "PN.R.....K.C.pn.r.....k.c.";
         v->maxRank = RANK_7;
@@ -714,7 +716,7 @@ namespace {
         return v;
     }
     Variant* capablanca_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ..AC............Kpnbrq..ac............k";
         v->maxRank = RANK_8;
         v->maxFile = FILE_J;
@@ -731,7 +733,6 @@ namespace {
         v->startFen = "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR[] w KQkq - 0 1";
         v->pieceDrops = true;
         v->capturesToHand = true;
-        v->endgameEval = false;
         return v;
     }
     Variant* caparandom_variant() {
@@ -745,7 +746,7 @@ namespace {
         return v;
     }
     Variant* janus_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ............J...Kpnbrq............j...k";
         v->maxRank = RANK_8;
         v->maxFile = FILE_J;
@@ -757,7 +758,7 @@ namespace {
         return v;
     }
     Variant* modern_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ..M.............Kpnbrq..m.............k";
         v->maxRank = RANK_9;
         v->maxFile = FILE_I;
@@ -770,7 +771,7 @@ namespace {
         return v;
     }
     Variant* chancellor_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ...........CKpnbrq...........ck";
         v->maxRank = RANK_9;
         v->maxFile = FILE_I;
@@ -790,7 +791,7 @@ namespace {
         return v;
     }
     Variant* centaur_variant() {
-        Variant* v = chess_variant();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ...............CKpnbrq...............ck";
         v->maxRank = RANK_8;
         v->maxFile = FILE_J;
@@ -802,7 +803,7 @@ namespace {
         return v;
     }
     Variant* jesonmor_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->maxRank = RANK_9;
         v->maxFile = FILE_I;
         v->reset_pieces();
@@ -819,7 +820,7 @@ namespace {
         return v;
     }
     Variant* courier_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->maxRank = RANK_8;
         v->maxFile = FILE_L;
         v->remove_piece(QUEEN);
@@ -840,7 +841,7 @@ namespace {
         return v;
     }
     Variant* grand_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ..AC............Kpnbrq..ac............k";
         v->maxRank = RANK_10;
         v->maxFile = FILE_J;
@@ -863,7 +864,7 @@ namespace {
         return v;
     }
     Variant* shako_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "PNBRQ.E....C.........Kpnbrq.e....c.........k";
         v->maxRank = RANK_10;
         v->maxFile = FILE_J;
@@ -891,7 +892,7 @@ namespace {
     // Game of the Amazons
     // https://en.wikipedia.org/wiki/Game_of_the_Amazons
     Variant* amazons_variant() {
-        Variant* v = fairy_variant_base();
+        Variant* v = chess_variant_base();
         v->pieceToCharTable = "P...Q.................p...q.................";
         v->maxRank = RANK_10;
         v->maxFile = FILE_J;
index 5200a3f..3575ee9 100644 (file)
@@ -55,7 +55,6 @@ struct Variant {
   bool mandatoryPiecePromotion = false;
   bool pieceDemotion = false;
   bool blastOnCapture = false;
-  bool endgameEval = false;
   bool doubleStep = true;
   Rank doubleStepRank = RANK_2;
   Rank doubleStepRankMin = RANK_2;
@@ -134,6 +133,7 @@ struct Variant {
   bool fastAttacks = true;
   bool fastAttacks2 = true;
   PieceType nnueKing = KING;
+  bool endgameEval = false;
 
   void add_piece(PieceType pt, char c, char c2 = ' ') {
       pieceToChar[make_piece(WHITE, pt)] = toupper(c);
@@ -181,6 +181,24 @@ struct Variant {
       nnueKing =  pieceTypes.find(KING) != pieceTypes.end() ? KING
                 : extinctionPieceTypes.find(COMMONER) != extinctionPieceTypes.end() ? COMMONER
                 : NO_PIECE_TYPE;
+      // 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
+                    && checkmateValue == -VALUE_MATE
+                    && stalemateValue == VALUE_DRAW
+                    && !materialCounting
+                    && !flagPiece
+                    && !mustCapture
+                    && !checkCounting
+                    && !makpongRule
+                    && !connectN
+                    && !blastOnCapture
+                    && !capturesToHand
+                    && !twoBoards
+                    && kingType == KING;
       return this;
   }
 };
index d6a2d1f..f5e966e 100644 (file)
 # mandatoryPiecePromotion: piece promotion (and demotion if enabled) is mandatory [bool] (default: false)
 # pieceDemotion: enable demotion of pieces (e.g., Kyoto shogi) [bool] (default: false)
 # blastOnCapture: captures explode all adjacent non-pawn pieces (e.g., atomic chess) [bool] (default: false)
-# endgameEval: enable special endgame evaluation (for very chess-like variants only) [bool] (default: false)
 # doubleStep: enable pawn double step [bool] (default: true)
 # doubleStepRank: relative rank from where pawn double steps are allowed [Rank] (default: 2)
 # doubleStepRankMin: earlist relative rank from where pawn double steps are allowed [Rank] (default: 2)