From aab2982a2d0ba8edee64538ca26240f89b8eca25 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Sun, 30 Aug 2026 09:45:06 -0700 Subject: Add small static penalties for trapped pieces in _EvalTrappedPieces. Eval() already detects trapped/attacked pieces (for search hints via RecordEnprisePieceAtPly/RecordTrappedPiece) but never penalized them in the static score. Add two named, DNA-visible constants: a larger flag for the opponent-to-move/imminently-capturable case, a smaller one for the own-move/still-might-escape case -- flat "this is bad" nudges, not an attempt to price the material outcome, which search still owns. Verified flat on ecm_ringers/ecm_confident_quick vs head_reference at sd 10; ecm_hard_quick's lone flip (ECM.370) is a search-instability artifact of that specific position (its true evaluation was still moving through depth 14 in independent runs), not a real regression. --- src/eval.c | 49 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/eval.c b/src/eval.c index 424637e..fc22127 100755 --- a/src/eval.c +++ b/src/eval.c @@ -643,6 +643,16 @@ static SCORE KING_QUEEN_PROXIMITY_DANGER[7] = }; static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23; + +// Flat "this is bad" flags for _EvalTrappedPieces -- not an attempt to +// price the material outcome (search resolves that), just a nudge away +// from positions with a piece that looks stuck. ENPRISE_AND_TRAPPED is +// larger because that case is opponent-to-move / imminently capturable; +// TRAPPED_WITH_MOVE is our own move, so there's still a chance to +// wriggle out. +static SCORE ENPRISE_AND_TRAPPED_PENALTY = -50; +static SCORE TRAPPED_WITH_MOVE_PENALTY = -10; + typedef struct _DNA_BASE_SIZE { SCORE *pBase; ULONG uCount; @@ -5434,9 +5444,7 @@ Return value: ULONG uColor; ULONG u; POSITION *pos = &ctx->sPosition; -#ifdef DEBUG PIECE p; -#endif FOREACH_COLOR(uColor) { @@ -5448,30 +5456,32 @@ Return value: ASSERT(IS_ON_BOARD(c)); if (_WhoControlsSquareFast(pos, c) == FLIP(uColor)) { -#ifdef DEBUG p = pos->rgSquare[c].pPiece; +#ifdef DEBUG ASSERT(p); ASSERT(!IS_PAWN(p)); ASSERT(GET_COLOR(p) == uColor); #endif + // See if the side who created the trap and controls + // the square the trapped piece is sitting on has the + // move too. If so, uColor moved at ply-1. if (OPPOSITE_COLORS(uColor, pos->uToMove)) { - // uColor is the mover at ctx->uPly - 1 (ply - // parity), not here -- see RecordEnprisePieceAtPly. if (ctx->uPly > 0) { RecordEnprisePieceAtPly(ctx, ctx->uPly - 1, c); } + EVAL_TERM(uColor, p, c, pos->iScore[uColor], + ENPRISE_AND_TRAPPED_PENALTY, + "en prise and trapped"); } - else - { - // - // ctx->cTrapped[uPly] (RecordTrappedPiece's target) - // is a single slot, not a list -- if more than one - // of our own pieces is genuinely trapped this ply, - // only report the most valuable one rather than - // whichever happened to be found last. - // + + // ctx->cTrapped[uPly] (RecordTrappedPiece's target) + // is a single slot, not a list -- if more than one + // of our own pieces is genuinely trapped this ply, + // only report the most valuable one. + else + { if (PIECE_VALUE(pos->rgSquare[c].pPiece) > uBestOwnTrappedValue) { @@ -5479,12 +5489,21 @@ Return value: PIECE_VALUE(pos->rgSquare[c].pPiece); cBestOwnTrapped = c; } - } + } } } + + // uColor has the move but we found at least one piece that + // seems to have no safe place to move and is actively under + // attack now. It may not be lost, at least uColor has the + // move. But this is still a bad thing. if (IS_ON_BOARD(cBestOwnTrapped)) { RecordTrappedPiece(ctx, cBestOwnTrapped); + p = pos->rgSquare[cBestOwnTrapped].pPiece; + EVAL_TERM(uColor, p, cBestOwnTrapped, pos->iScore[uColor], + TRAPPED_WITH_MOVE_PENALTY, + "trapped piece, our move"); } } } -- cgit v1.3