Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# along with Scid. If not, see <http://www.gnu.org/licenses/>.

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")
Expand Down
59 changes: 59 additions & 0 deletions gtest/test_position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
50 changes: 35 additions & 15 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
14 changes: 12 additions & 2 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "common.h"
#include "movelist.h"
#include <climits>
#include <stdio.h>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -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.
//
Expand Down Expand Up @@ -248,7 +259,7 @@ class Position
template <bool check_legal = true> 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);
}
Expand Down Expand Up @@ -322,4 +333,3 @@ class Position
//////////////////////////////////////////////////////////////////////
// EOF: position.h
//////////////////////////////////////////////////////////////////////

8 changes: 4 additions & 4 deletions src/tkscid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
Expand Down