summaryrefslogtreecommitdiff
path: root/src/movesup.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-07 08:17:51 -0700
committerScott Gasch <[email protected]>2026-10-07 08:17:51 -0700
commitd14dbe2a50c220b2adac758c3d4f1a1fa67ece0b (patch)
treecbf6d9928bcc41ef833b62b70479113f7d419925 /src/movesup.c
parent447130db3df87b40d080dc26429d45e9213c19f7 (diff)
Speed up eval/move-gen/SEE; remove EvalLookForDanger, overhaul _EvalKing
SPRT-confirmed improvement (1814 games, st 4.0): candidate nps=1498118 vs head=1378448, avg depth 14.30 vs 13.87, ELO=+24.2+/-16.0, LOWER95=0.5118, LLR=+3.126 crossing the H1 bound. Most of the diff is non-semantic speed work across eval.c, generate.c, movesup.c, see.c, probe.c and friends. Two real semantic changes: removed EvalLookForDanger and overhauled _EvalKing's king-safety scoring. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/movesup.c')
-rw-r--r--src/movesup.c275
1 files changed, 136 insertions, 139 deletions
diff --git a/src/movesup.c b/src/movesup.c
index e39e52a..d3d2a74 100644
--- a/src/movesup.c
+++ b/src/movesup.c
@@ -385,10 +385,10 @@ Return value:
ASSERT(IS_ON_BOARD(cLocation));
ASSERT(!IS_EMPTY(pos->rgSquare[cLocation].pPiece));
ASSERT(0 != (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) & (1 << QUEEN)));
-
+
iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
ASSERT(iDelta != 0);
-
+
for (cBlockIndex = cRemove + iDelta;
IS_ON_BOARD(cBlockIndex);
cBlockIndex += iDelta)
@@ -400,12 +400,12 @@ Return value:
{
//
// Is it an enemy?
- //
+ //
if (OPPOSITE_COLORS(xPiece, uColor))
{
//
// Does it move the right way to attack?
- //
+ //
iIndex = (int)cBlockIndex - (int)cLocation;
if (0 != (CHECK_VECTOR_WITH_INDEX(iIndex, WHITE) &
(1 << (PIECE_TYPE(xPiece)))))
@@ -413,26 +413,26 @@ Return value:
return(cBlockIndex);
}
}
-
+
//
// Either we found another friendly piece or an enemy piece
// that was unable to reach us. Either way we are safe.
- //
+ //
return(ILLEGAL_COOR);
}
}
-
+
//
// We fell off the board without seeing another piece.
- //
+ //
return(ILLEGAL_COOR);
}
-COOR
-ExposesCheck(POSITION *pos,
- COOR cRemove,
+COOR
+ExposesCheck(POSITION *pos,
+ COOR cRemove,
COOR cLocation)
/**
@@ -452,7 +452,7 @@ Parameters:
Return value:
- COOR : the location of an attacker piece or 0x88 (!IS_ON_BOARD) if
+ COOR : the location of an attacker piece or 0x88 (!IS_ON_BOARD) if
the removal of cRemove does not expose check.
**/
@@ -461,13 +461,13 @@ Return value:
register COOR cBlockIndex;
int iIndex;
PIECE xPiece;
-
+
ASSERT(IS_ON_BOARD(cRemove));
ASSERT(IS_ON_BOARD(cLocation));
ASSERT(!IS_EMPTY(pos->rgSquare[cLocation].pPiece));
-
+
iIndex = (int)cLocation - (int)cRemove;
-
+
//
// If there is no way for a queen sitting at the square removed to
// reach the square we are testing (i.e. the two squares are not
@@ -479,11 +479,11 @@ Return value:
return(ILLEGAL_COOR);
}
iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
- for (cBlockIndex = cLocation + iDelta;
+ for (cBlockIndex = cLocation + iDelta;
IS_ON_BOARD(cBlockIndex);
cBlockIndex += iDelta)
{
- if (cBlockIndex == cRemove)
+ if (cBlockIndex == cRemove)
{
continue;
}
@@ -492,12 +492,12 @@ Return value:
{
//
// Is it an enemy?
- //
+ //
if (OPPOSITE_COLORS(xPiece, pos->rgSquare[cLocation].pPiece))
{
//
// Does it move the right way to attack?
- //
+ //
iIndex = (int)cBlockIndex - (int)cLocation;
if (0 != (CHECK_VECTOR_WITH_INDEX(iIndex, GET_COLOR(xPiece)) &
(1 << (PIECE_TYPE(xPiece)))))
@@ -505,28 +505,28 @@ Return value:
return(cBlockIndex);
}
}
-
+
//
// Either we found another friendly piece or an enemy piece
// that was unable to reach us. Either way we are safe.
- //
+ //
return(ILLEGAL_COOR);
}
}
-
+
//
// We fell off the board without seeing another piece.
- //
+ //
return(ILLEGAL_COOR);
}
-COOR
-ExposesCheckEp(POSITION *pos,
- COOR cTest,
- COOR cIgnore,
- COOR cBlock,
- COOR cKing)
+COOR
+ExposesCheckEp(POSITION *pos,
+ COOR cTest,
+ COOR cIgnore,
+ COOR cBlock,
+ COOR cKing)
/**
Routine description:
@@ -562,13 +562,13 @@ Return value:
// reach the square we are testing (i.e. the two squares are not
// on the same rank, file, or diagonal) then no problemo.
//
- if (0 == (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) & (1 << QUEEN)))
+ if (0 == (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) & (1 << QUEEN)))
{
return(ILLEGAL_COOR);
}
-
+
iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
- for (cBlockIndex = cKing + iDelta;
+ for (cBlockIndex = cKing + iDelta;
IS_ON_BOARD(cBlockIndex);
cBlockIndex += iDelta)
{
@@ -583,7 +583,7 @@ Return value:
{
//
// Does it move the right way to attack?
- //
+ //
iIndex = (int)cBlockIndex - (int)cKing;
if (0 != (CHECK_VECTOR_WITH_INDEX(iIndex, GET_COLOR(xPiece)) &
(1 << (PIECE_TYPE(xPiece)))))
@@ -595,14 +595,14 @@ Return value:
//
// Either we found another friendly piece or an enemy piece
// that was unable to reach us. Either way we are safe.
- //
+ //
return(ILLEGAL_COOR);
}
}
-
+
//
// We fell off the board without seeing another piece.
- //
+ //
return(ILLEGAL_COOR);
}
@@ -657,7 +657,7 @@ Return value:
return(0 != (g_PawnAttackOriginBB[uSide][cTest] & pos->bbPawns[uSide]));
}
-FLAG
+FLAG FORCEINLINE
InCheckBB(IN POSITION *pos, IN ULONG uSide)
/**
@@ -740,7 +740,7 @@ Return value:
{
continue;
}
-
+
//
// These pieces do not slide, we don't need to look for
// blockers. If they can get there then there is nothing
@@ -775,16 +775,16 @@ Return value:
//
// Check the locations a pawn could attack cTest from.
- //
+ //
cLocation = cTest + iPawnLoc[uSide];
- if (IS_ON_BOARD(cLocation) &&
+ if (IS_ON_BOARD(cLocation) &&
(pos->rgSquare[cLocation].pPiece == pPawn[uSide]))
{
return(TRUE);
}
-
+
cLocation += 2;
- if (IS_ON_BOARD(cLocation) &&
+ if (IS_ON_BOARD(cLocation) &&
(pos->rgSquare[cLocation].pPiece == pPawn[uSide]))
{
return(TRUE);
@@ -793,7 +793,7 @@ Return value:
}
-FLAG
+FLAG
InCheck(POSITION *pos, ULONG uSide)
/**
@@ -818,7 +818,7 @@ Return value:
#endif
ASSERT(IS_VALID_COLOR(uSide));
-
+
cKingLoc = KingPosition(pos, uSide);
#ifdef DEBUG
pKing = pos->rgSquare[cKingLoc].pPiece;
@@ -830,7 +830,7 @@ Return value:
}
-static FLAG
+static FLAG
_SanityCheckPawnMove(POSITION *pos, MOVE mv)
/**
@@ -856,15 +856,15 @@ Return value:
//
// General sanity checking...
- //
- if (!IS_ON_BOARD(cFrom) || !IS_ON_BOARD(cTo))
+ //
+ if (!IS_ON_BOARD(cFrom) || !IS_ON_BOARD(cTo))
{
return(FALSE);
}
-
+
//
// Sanity check promotions.
- //
+ //
if (mv.pPromoted)
{
if (GET_COLOR(p) == WHITE)
@@ -875,43 +875,43 @@ Return value:
{
if ((RANK(cTo) != 1) && (RANK(cFrom) != 2)) return(FALSE);
}
-
+
if (!IS_KNIGHT(mv.pPromoted) &&
!IS_BISHOP(mv.pPromoted) &&
!IS_ROOK(mv.pPromoted) &&
!IS_QUEEN(mv.pPromoted))
{
return(FALSE);
- }
+ }
}
-
+
//
// Handle capture moves.
- //
+ //
if (mv.pCaptured)
{
//
// Must be capturing something
- //
+ //
if (IS_SPECIAL_MOVE(mv) && (!mv.pPromoted))
{
//
// en passant
- //
+ //
if (!IS_EMPTY(pos->rgSquare[cTo].pPiece)) return(FALSE);
}
else
{
//
// normal capture
- //
+ //
if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) return(FALSE);
if (!OPPOSITE_COLORS(mv.pMoved, mv.pCaptured)) return(FALSE);
}
//
// Must be in the capturing position.
- //
+ //
if (WHITE == GET_COLOR(p))
{
if (((cFrom - 17) != cTo) && ((cFrom - 15) != cTo)) return(FALSE);
@@ -920,27 +920,27 @@ Return value:
{
if (((cFrom + 17) != cTo) && ((cFrom + 15) != cTo)) return(FALSE);
}
-
- if ((IS_SPECIAL_MOVE(mv)) &&
+
+ if ((IS_SPECIAL_MOVE(mv)) &&
(!mv.pPromoted) &&
(pos->cEpSquare != cTo)) return(FALSE);
return(TRUE);
}
else
{
- //
+ //
// If the pawn is not capturing, the TO square better be empty.
- //
+ //
if (!IS_EMPTY(pos->rgSquare[cTo].pPiece)) return(FALSE);
-
- //
+
+ //
// A non-capturing pawn can only push forward. One or two squares
// based on whether or not its the initial move.
//
if (WHITE == GET_COLOR(p))
{
if (cTo == cFrom - 16) return(TRUE);
- if ((cTo == cFrom - 32) &&
+ if ((cTo == cFrom - 32) &&
(RANK2(cFrom)) &&
(IS_EMPTY(pos->rgSquare[cFrom - 16].pPiece))) return(TRUE);
}
@@ -982,42 +982,42 @@ Return value:
COOR cAttack;
COOR cKing;
int iDelta;
-
+
ASSERT(!IS_PAWN(p));
//
// Handle castling
- //
+ //
if (IS_CASTLE(mv))
{
ASSERT(IS_KING(mv.pMoved));
ASSERT(IS_SPECIAL_MOVE(mv));
return(TRUE);
}
-
+
//
// Make sure the piece moves in the way the move describes.
- //
+ //
iIndex = (int)cFrom - (int)cTo;
if (0 == (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) &
(1 << pPieceType)))
{
return(FALSE);
}
-
+
//
// If we get here the piece can move in the way described by the
// move but we still have to check to see if there are any other
// pieces in the way if the piece moved is not a king or knight
// (pawns handled in their own routine, see above).
- //
+ //
if ((pPieceType == QUEEN) ||
(pPieceType == ROOK) ||
(pPieceType == BISHOP))
{
iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
for (cBlock = cFrom + iDelta;
- cBlock != cTo;
+ cBlock != cTo;
cBlock += iDelta)
{
if (!IS_EMPTY(pos->rgSquare[cBlock].pPiece))
@@ -1027,15 +1027,15 @@ Return value:
}
if (cBlock != cTo) return(FALSE);
}
-
+
//
// Ok the piece can move the way they asked and there are no other
// pieces in the way... but it cannot expose its own king to check
// by doing so!
//
cKing = KingPosition(pos, pos->uToMove);
- cAttack = ExposesCheck(pos,
- cFrom,
+ cAttack = ExposesCheck(pos,
+ cFrom,
cKing);
if ((ILLEGAL_COOR != cAttack) && (cTo != cAttack))
{
@@ -1044,7 +1044,7 @@ Return value:
// does expose the king to check and the move does not capture the
// checking piece. But it's still ok as long as the TO location
// still blocks the checking piece.
- //
+ //
iIndex = cAttack - cKing;
iDelta = CHECK_DELTA_WITH_INDEX(iIndex);
for (cBlock = cAttack + iDelta;
@@ -1059,12 +1059,12 @@ Return value:
//
// Legal!
- //
+ //
return(TRUE);
}
-FLAG
+FLAG
SanityCheckMove(POSITION *pos, MOVE mv)
/**
@@ -1084,7 +1084,7 @@ Return value:
**/
{
register PIECE p = mv.pMoved;
-
+
if (0 == mv.uMove)
{
return(FALSE);
@@ -1106,7 +1106,7 @@ Return value:
{
return(FALSE);
}
-
+
if (!IS_SPECIAL_MOVE(mv))
{
if (mv.pCaptured)
@@ -1122,7 +1122,7 @@ Return value:
return(FALSE);
}
}
-
+
if (!IS_EMPTY(pos->rgSquare[mv.cTo].pPiece) &&
!mv.pCaptured)
{
@@ -1136,7 +1136,7 @@ Return value:
return(FALSE);
}
}
-
+
if (IS_PAWN(p))
{
return(_SanityCheckPawnMove(pos, mv));
@@ -1148,7 +1148,7 @@ Return value:
}
-FLAG
+FLAG
LooksLikeFile(CHAR c)
/**
@@ -1198,9 +1198,9 @@ Return value:
}
return(FALSE);
}
-
-FLAG
+
+FLAG
LooksLikeCoor(char *szData)
/**
@@ -1220,7 +1220,7 @@ Return value:
{
char f = szData[0];
char r = szData[1];
-
+
if (LooksLikeFile(f) &&
LooksLikeRank(r))
{
@@ -1252,7 +1252,7 @@ Return value:
static CHAR szStripped[10];
ULONG y;
CHAR *p;
-
+
memset(szStripped, 0, sizeof(szStripped));
p = szMove;
y = 0;
@@ -1289,11 +1289,11 @@ Return value:
CHAR *szStripped = StripMove(szData);
ULONG u;
static CHAR cPieces[] = { 'P', 'N', 'B', 'R', 'Q', 'K', '\0' };
-
+
//
// A (stripped) move must be at least two characters long even in
// SAN.
- //
+ //
if ((strlen(szStripped) < 2) || (strlen(szStripped) > 7))
{
return(NOT_MOVE);
@@ -1309,7 +1309,7 @@ Return value:
{
return(MOVE_SAN);
}
-
+
//
// COOR COOR (d2d4)
//
@@ -1318,37 +1318,37 @@ Return value:
{
return(MOVE_ICS);
}
-
+
//
// COOR (d4)
- //
+ //
if (LooksLikeCoor(szStripped) &&
strlen(szStripped) == 2)
{
return(MOVE_SAN);
}
-
+
//
// RANK COOR
- //
+ //
if ((LooksLikeRank(szStripped[0]) ||
LooksLikeFile(szStripped[0])) &&
LooksLikeCoor(&szStripped[1]))
{
return(MOVE_SAN);
}
-
+
//
// PIECE COOR COOR
// PIECE COOR
// PIECE FILE COOR
// PIECE RANK COOR
- //
+ //
for (u = 0; u < ARRAY_LENGTH(cPieces); u++)
{
if (szStripped[0] == cPieces[u]) // must be uppercase!
{
- if (LooksLikeCoor(&szStripped[1]))
+ if (LooksLikeCoor(&szStripped[1]))
{
return(MOVE_SAN);
}
@@ -1367,7 +1367,7 @@ Return value:
//
// (COORD)=(PIECE)
- //
+ //
if ((strlen(szStripped) == 4) &&
LooksLikeCoor(szStripped) &&
szStripped[2] == '=')
@@ -1392,7 +1392,7 @@ Return value:
}
-void FASTCALL
+void FORCEINLINE
SelectBestWithHistory(SEARCHER_THREAD_CONTEXT *ctx,
ULONG u)
/**
@@ -1424,7 +1424,7 @@ Return value:
MOVE mvLast;
ULONG uPrevContKey = 0;
FLAG fHaveContinuation = FALSE;
- COOR cEnprise;
+ // COOR cEnprise;
ASSERT(ctx->sMoveStack.uBegin[ctx->uPly] <= uEnd);
ASSERT(u >= ctx->sMoveStack.uBegin[ctx->uPly]);
@@ -1442,7 +1442,7 @@ Return value:
// answer than an earlier one. Recomputed every call (2026-09-16:
// briefly hoisted to the caller, reverted after a DEBUG-build
// ASSERT here caught the mismatch -- see git log).
- cEnprise = FindEnprisePiece(ctx, ctx->sPosition.uToMove);
+ // cEnprise = FindEnprisePiece(ctx, ctx->sPosition.uToMove);
// Continuation history: "did a move like this tend to fail high
// right after a move like that?", keyed by (previous move, this
@@ -1468,16 +1468,14 @@ Return value:
if (!IS_CAPTURE_OR_PROMOTION(mv))
{
iBestVal += g_HistoryCounters[mv.pMoved][mv.cTo];
- if (TRUE == fHaveContinuation)
- {
- iBestVal += CONTINUATION_SCALE *
- g_ContinuationHistory[(uPrevContKey * CONT_KEY_RANGE) +
- MOVE_TO_CONT_KEY(mv)];
- }
- if (mv.cFrom == cEnprise)
- {
- iBestVal += FLEE_BONUS;
- }
+ iBestVal += (
+ fHaveContinuation *
+ CONTINUATION_SCALE *
+ g_ContinuationHistory[(uPrevContKey * CONT_KEY_RANGE) + MOVE_TO_CONT_KEY(mv)]);
+ //if (mv.cFrom == cEnprise)
+ //{
+ // iBestVal += FLEE_BONUS;
+ //}
}
uLoc = u;
@@ -1488,15 +1486,14 @@ Return value:
if (!IS_CAPTURE_OR_PROMOTION(mv))
{
iVal += g_HistoryCounters[mv.pMoved][mv.cTo];
- if (TRUE == fHaveContinuation)
- {
- iVal += g_ContinuationHistory[(uPrevContKey * CONT_KEY_RANGE) +
- MOVE_TO_CONT_KEY(mv)];
- }
- if (mv.cFrom == cEnprise)
- {
- iVal += FLEE_BONUS;
- }
+ iVal += (
+ fHaveContinuation *
+ CONTINUATION_SCALE *
+ g_ContinuationHistory[(uPrevContKey * CONT_KEY_RANGE) + MOVE_TO_CONT_KEY(mv)]);
+// if (mv.cFrom == cEnprise)
+// {
+// iVal += FLEE_BONUS;
+// }
}
if (iVal > iBestVal)
{
@@ -1514,7 +1511,7 @@ Return value:
}
-void FASTCALL
+void FORCEINLINE
SelectBestNoHistory(SEARCHER_THREAD_CONTEXT *ctx,
ULONG u)
/**
@@ -1542,11 +1539,11 @@ Return value:
ULONG uLoc;
SCORE iVal;
MOVE_STACK_MOVE_VALUE_FLAGS mvfTemp;
-
+
ASSERT(ctx->sMoveStack.uBegin[ctx->uPly] <= uEnd);
ASSERT(u >= ctx->sMoveStack.uBegin[ctx->uPly]);
ASSERT(u < uEnd);
-
+
//
// Linear search from u..ctx->sMoveStack.uEnd[ctx->uPly] for the
// move with the best value.
@@ -1571,7 +1568,7 @@ Return value:
ctx->sMoveStack.mvf[uLoc] = mvfTemp;
}
-void FASTCALL
+void FASTCALL
SelectMoveAtRoot(SEARCHER_THREAD_CONTEXT *ctx,
ULONG u)
/**
@@ -1595,12 +1592,12 @@ Return value:
ULONG uLoc = v;
SCORE iVal;
MOVE_STACK_MOVE_VALUE_FLAGS mvfTemp;
-
+
ASSERT(ctx->sMoveStack.uBegin[ctx->uPly] <= uEnd);
ASSERT(u >= ctx->sMoveStack.uBegin[ctx->uPly]);
ASSERT(u < uEnd);
ASSERT(MOVE_COUNT(ctx, ctx->uPly) >= 1);
-
+
//
// Find the first move that we have not already searched. It will
// provide our initial iBestVal.
@@ -1648,7 +1645,7 @@ static UINT64 g_uPerftNodeCount;
static UINT64 g_uPerftTotalNodes;
static UINT64 g_uPerftGenerates;
-void
+void
Perft(SEARCHER_THREAD_CONTEXT *ctx,
ULONG uDepth)
{
@@ -1658,16 +1655,16 @@ Perft(SEARCHER_THREAD_CONTEXT *ctx,
g_uPerftTotalNodes += 1;
ASSERT(uDepth < MAX_PLY_PER_SEARCH);
- if (uDepth == 0)
+ if (uDepth == 0)
{
g_uPerftNodeCount += 1;
return;
}
-
+
mv.uMove = 0;
g_uPerftGenerates += 1;
GenerateMoves(ctx, mv, GENERATE_DONT_SCORE);
-
+
uPly = ctx->uPly;
for (u = ctx->sMoveStack.uBegin[uPly];
u < ctx->sMoveStack.uEnd[uPly];
@@ -1688,13 +1685,13 @@ COMMAND(PerftCommand)
Routine description:
This function implements the 'perft' engine command. I have no
- idea what 'perft' means but some people use this command to
+ idea what 'perft' means but some people use this command to
benchmark the speed of the move generator code. Note: the way
this is implemented here does nothing whatsoever with the move
scoring code (i.e. the SEE)
Usage:
-
+
perft <depth>
Parameters:
@@ -1718,30 +1715,30 @@ Return value:
double dBegin, dTime;
double dNps;
- if (argc < 2)
+ if (argc < 2)
{
Trace("Usage: perft <required_depth>\n");
return;
}
uDepth = atoi(argv[1]);
- if (uDepth >= MAX_DEPTH_PER_SEARCH)
+ if (uDepth >= MAX_DEPTH_PER_SEARCH)
{
Trace("Error: invalid depth.\n");
return;
}
InitializeLightweightSearcherContext(pos, &ctx);
-
+
g_uPerftTotalNodes = g_uPerftGenerates = 0ULL;
dBegin = SystemTimeStamp();
- for (u = 1; u <= uDepth; u++)
+ for (u = 1; u <= uDepth; u++)
{
g_uPerftNodeCount = 0ULL;
Perft((SEARCHER_THREAD_CONTEXT *)&ctx, u);
Trace("%u. %" COMPILER_LONGLONG_UNSIGNED_FORMAT " node%s, "
"%" COMPILER_LONGLONG_UNSIGNED_FORMAT " generate%s.\n",
- u,
- g_uPerftNodeCount,
+ u,
+ g_uPerftNodeCount,
(g_uPerftNodeCount > 1) ? "s" : "",
g_uPerftGenerates,
(g_uPerftGenerates > 1) ? "s" : "");
@@ -1751,8 +1748,8 @@ Return value:
dNps /= dTime;
Trace("%" COMPILER_LONGLONG_UNSIGNED_FORMAT " total nodes, "
"%" COMPILER_LONGLONG_UNSIGNED_FORMAT " total generates "
- "in %6.2f seconds.\n",
- g_uPerftTotalNodes,
+ "in %6.2f seconds.\n",
+ g_uPerftTotalNodes,
g_uPerftGenerates,
dTime);
Trace("That's approx %.0f moves/sec\n", dNps);