From d2449ba4624dc20a0570e5ea39ce6f15a0d2ac2e Mon Sep 17 00:00:00 2001 From: Fabian Fichter Date: Sun, 7 Mar 2021 18:42:06 +0100 Subject: [PATCH] Precalculate endgameEval flag 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 | 2 +- src/parser.cpp | 1 - src/position.cpp | 2 +- src/position.h | 7 +++- src/variant.cpp | 97 +++++++++++++++++++++++++++-------------------------- src/variant.h | 20 ++++++++++- src/variants.ini | 1 - 7 files changed, 76 insertions(+), 54 deletions(-) diff --git a/src/material.cpp b/src/material.cpp index 79feda2..5c4a055 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -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() * PawnValueMg) * countAll / ((pos.max_file() + 1) * (pos.max_rank() + 1)); diff --git a/src/parser.cpp b/src/parser.cpp index dbe3847..de075a5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -238,7 +238,6 @@ Variant* VariantParser::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); diff --git a/src/position.cpp b/src/position.cpp index 9f4bb90..d1a7e1d 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -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(); diff --git a/src/position.h b/src/position.h index 59df07a..d47e61f 100644 --- a/src/position.h +++ b/src/position.h @@ -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() == 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]; } diff --git a/src/variant.cpp b/src/variant.cpp index be41e42..07d3035 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -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; diff --git a/src/variant.h b/src/variant.h index 5200a3f..3575ee9 100644 --- a/src/variant.h +++ b/src/variant.h @@ -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; } }; diff --git a/src/variants.ini b/src/variants.ini index d6a2d1f..f5e966e 100644 --- a/src/variants.ini +++ b/src/variants.ini @@ -120,7 +120,6 @@ # 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) -- 1.7.0.4