From 5088465738684caa596a68f9ed49a35ef65529ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Sat, 21 Oct 2023 19:08:38 +0200 Subject: [PATCH 1/3] Make 'gloss of dager' use relative piece values See https://github.com/benini/scid/issues/139 --- src/position.cpp | 50 +++++++++++++++++++++++++++++++++--------------- src/position.h | 14 ++++++++++++-- src/tkscid.cpp | 8 ++++---- 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index a822adc7..48856572 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1114,29 +1114,49 @@ Position::GenCheckEvasions (MoveList * mlist, pieceT mask, genMovesT genType, // Position::TreeCalcAttacks(): // Calculate attack score for a side on a square, // using a recursive tree search. -int -Position::TreeCalcAttacks(colorT side, squareT target) +bool +Position::TreeCalcAttacks(int* dResult, squareT target) { - int maxScore = -2; - uint moveCount = 0, zeroCount = 0; + int worstScore = PV_KING; + bool hasCaptures = false; MoveList moveList; GenerateCaptures(&moveList); - for (uint i=0; i < moveList.Size(); i++) { - simpleMoveT *smPtr = moveList.Get(i); + + for (auto smPtr = moveList.begin(); smPtr < moveList.end(); smPtr++) { if (smPtr->to == target) { - if (piece_IsKing(Board[target])) return -1; - moveCount++; + int score = 0; + pieceT piece = smPtr->capturedPiece; + hasCaptures = true; + if (piece_IsKing(piece)) { + worstScore = -PV_KING; + break; + } DoSimpleMove(*smPtr); - int score = TreeCalcAttacks(color_Flip(side), target); + TreeCalcAttacks(&score, target); + switch (piece_Type(piece)) { + case QUEEN: + score = -score - PV_QUEEN; + break; + case ROOK: + score = -score - PV_ROOK; + break; + case KNIGHT: + score = -score - PV_KNIGHT; + break; + case BISHOP: + score = -score - PV_BISHOP; + break; + case PAWN: + score = -score - PV_PAWN; + break; + } UndoSimpleMove(smPtr); - if (!score && ++zeroCount > 1) return -2; - if (score > maxScore) maxScore = score; + if (score < worstScore) worstScore = score; } } - - if (!moveCount) return 0; - if (!maxScore) return -1; - return -maxScore; + + if (hasCaptures) *dResult = worstScore; + return hasCaptures; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/position.h b/src/position.h index 9a585de1..585e6839 100644 --- a/src/position.h +++ b/src/position.h @@ -18,6 +18,7 @@ #include "common.h" #include "movelist.h" +#include #include #include #include @@ -51,6 +52,16 @@ const genMovesT GEN_NON_CAPS = 2, GEN_ALL_MOVES = (GEN_CAPTURES | GEN_NON_CAPS); +// Piece values for Position::CalcTreeAttacks() +typedef int8_t pieceValueT; +const pieceValueT + PV_PAWN = 1, + PV_KNIGHT = 3, + PV_BISHOP = 3, + PV_ROOK = 5, + PV_QUEEN = 9, + PV_KING = INT8_MAX; + // SANList: list of legal move strings in SAN. // @@ -248,7 +259,7 @@ class Position template bool canCastle(bool king_side) const; uint CalcAttacks (colorT toMove, squareT kingSq, SquareList * squares) const; - int TreeCalcAttacks (colorT toMove, squareT target); + bool TreeCalcAttacks (int* dResult, squareT target); uint CalcNumChecks () const { return CalcAttacks (1-ToMove, GetKingSquare(), NULL); } @@ -322,4 +333,3 @@ class Position ////////////////////////////////////////////////////////////////////// // EOF: position.h ////////////////////////////////////////////////////////////////////// - diff --git a/src/tkscid.cpp b/src/tkscid.cpp index a25e0f23..6cb9c2ba 100644 --- a/src/tkscid.cpp +++ b/src/tkscid.cpp @@ -4677,13 +4677,13 @@ sc_pos (ClientData cd, Tcl_Interp * ti, int argc, const char ** argv) Position pos(*db->game->GetCurrentPos()); for (colorT c = WHITE; c <= BLACK; c++) { for (uint i = 0; i < pos.GetCount(c); i++) { + int att; squareT sq = pos.GetList(c)[i]; pos.SetToMove(color_Flip(c)); - int att = pos.TreeCalcAttacks(color_Flip(c), sq); - if (att) { + if (pos.TreeCalcAttacks(&att, sq)) { appendUintElement(ti, sq); - if (att > 1) Tcl_AppendElement(ti, "green"); - else if (att > 0) Tcl_AppendElement(ti, "yellow"); + if (att > 0) Tcl_AppendElement(ti, "green"); + else if (att == 0) Tcl_AppendElement(ti, "yellow"); else Tcl_AppendElement(ti, "red"); } } From b9884067f589861c2cba9cfa0aa401ae301c8b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Fri, 27 Oct 2023 18:21:52 +0200 Subject: [PATCH 2/3] Make tests compile when using CMake --- gtest/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtest/CMakeLists.txt b/gtest/CMakeLists.txt index d5abf0cd..596283f0 100644 --- a/gtest/CMakeLists.txt +++ b/gtest/CMakeLists.txt @@ -14,7 +14,7 @@ # along with Scid. If not, see . cmake_minimum_required(VERSION 3.2) -set(CMAKE_CXX_STANDARD 17 CACHE STRING "") +set(CMAKE_CXX_STANDARD 20 CACHE STRING "") # googletest if(NOT IS_DIRECTORY "${CMAKE_BINARY_DIR}/googletest") From 577a3fd8a44aa217d324687d9a5e8b37e43c1537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Fri, 27 Oct 2023 18:22:28 +0200 Subject: [PATCH 3/3] Test Position::TreeCalcAttacks --- gtest/test_position.cpp | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/gtest/test_position.cpp b/gtest/test_position.cpp index 1ae19e20..d6f0f204 100644 --- a/gtest/test_position.cpp +++ b/gtest/test_position.cpp @@ -866,3 +866,62 @@ TEST(Test_PrintFen, illegal_castling_flag) { } } } + +struct treeCalcAttacksTestCaseT { + const char* position; + const squareT square; + const int expectedScore; + const bool hasCaptures; +}; + +TEST(Test_TreeCalcAttacks, positions) { + // clang-format off + static const treeCalcAttacksTestCaseT cases[] = { + { "r2qkbnr/pppb1ppp/2n1p3/3p4/3P4/1QP3P1/PP2PPBP/RNB1K1NR w KQkq - 0 1", D5, + 1, true }, + { "r2qkbnr/pppb1ppp/2n1p3/3p4/3P1B2/1QP3P1/PP2PPBP/RN2K1NR b KQkq - 1 1", D4, + 2, true }, + { "rnbqk1nr/ppp1ppbp/3p2p1/4P3/3P4/8/PPP2PPP/RNBQKBNR w KQkq - 0 4", D6, + 0, true }, + { "rnbqk1nr/ppp1ppbp/3p2p1/4P3/3P1P2/8/PPP3PP/RNBQKBNR b KQkq - 0 4", E5, + 2, true }, + { "3rk1nr/pp2ppbp/1q4p1/4P3/3n4/1P2BB2/P5PP/RN1QK2R w KQk - 0 13", D4, + 4, true }, + { "rnb1k2r/ppp2ppp/5n2/2b2N2/2p1P3/4B3/PP3PPP/RN1qKB1R w KQkq - 0 8", D1, + -9, true }, + { "rnb1k2r/ppp2ppp/5n2/2b2N2/2p1P3/4B3/PP3PPP/RN1qKB1R w KQkq - 0 8", C5, + 0, false }, + { "rnQqkb1r/pp2pBpp/2p5/6B1/3P4/2P2N2/P1b3PP/R3K2R b KQkq - 0 12", C8, + 0, false }, + { "2r1kb1r/1b3ppp/p3pP2/7n/Np1p4/4B2N/PP2QP1q/1K1R1BR1 b k - 2 20", H3, + 6, true }, + { "2r1kb1r/1b3ppp/p3pP2/7n/Np1p4/4B2N/PP2QP1q/1K1R1BR1 b k - 2 20", G1, + 4, true }, + { "2r1kb1r/1b3ppp/p3pP2/7n/Np1p4/4B2N/PP2QP1q/1K1R1BR1 b k - 2 20", F2, + 8, true }, + { "2r1kb1r/1b3ppp/p3pP2/7n/Np1p4/4B2N/PP2QP1q/1K1R1BR1 b k - 2 20", E3, + -2, true }, + { "r3kb1r/pp1nqppp/2p1p1b1/6N1/2BP1BP1/2P2Q2/P6P/4RRK1 w kq - 4 14", E6, + -5, true } + }; + // clang-format on + + Position pos; + char buf[64]; + auto it = std::begin(cases); + for (; it != std::end(cases); ++it) { + int score = 0; + bool result; + colorT toMove; + pieceT pieceUnderCapture; + + pos.ReadFromFEN(it->position); + toMove = pos.GetToMove(); + pieceUnderCapture = pos.GetPiece(it->square); + ASSERT_TRUE(pieceUnderCapture == EMPTY || piece_Color(pieceUnderCapture) == color_Flip(toMove)); + + result = pos.TreeCalcAttacks(&score, it->square); + EXPECT_EQ(result, it->hasCaptures); + EXPECT_EQ(score, it->expectedScore); + } +}