diff options
Diffstat (limited to 'src/recogn.c')
| -rw-r--r-- | src/recogn.c | 388 |
1 files changed, 224 insertions, 164 deletions
diff --git a/src/recogn.c b/src/recogn.c index 0ab9514..32a618d 100644 --- a/src/recogn.c +++ b/src/recogn.c @@ -12,7 +12,7 @@ Abstract: Interior-Node Recognition" * ICCA Journal Volume 21, No. 3, pp 156-167 (also "Scalable Search in Computer Chess" pp 65-81). This code also borrows ideas from Thorsten Greiner's AMY chess program. - + Author: Scott Gasch ([email protected]) 16 Oct 2005 @@ -24,10 +24,10 @@ Revision History: #include "chess.h" extern ULONG g_uIterateDepth; -static COOR QUEENING_SQUARE_BY_COLOR_FILE[2][8] = -{ +static COOR QUEENING_SQUARE_BY_COLOR_FILE[2][8] = +{ { A1, B1, C1, D1, E1, F1, G1, H1 }, - { A8, B8, C8, D8, E8, F8, G8, H8 } + { A8, B8, C8, D8, E8, F8, G8, H8 } }; #define RECOGN_INDEX(w, b) \ @@ -38,12 +38,12 @@ typedef ULONG RECOGNIZER(SEARCHER_THREAD_CONTEXT *ctx, SCORE *piScore); static RECOGNIZER *g_pRecognizers[64]; static BITV g_bvRecognizerAvailable[32]; -static ULONG -_MakeMaterialSig(IN FLAG fPawn, +static ULONG +_MakeMaterialSig(IN FLAG fPawn, IN FLAG fKnight, IN FLAG fBishop, - IN FLAG fRook, - IN FLAG fQueen) + IN FLAG fRook, + IN FLAG fQueen) /** Routine description: @@ -66,7 +66,7 @@ Return value: **/ { ULONG x; - + ASSERT(IS_VALID_FLAG(fPawn)); ASSERT(IS_VALID_FLAG(fKnight)); ASSERT(IS_VALID_FLAG(fBishop)); @@ -74,7 +74,7 @@ Return value: ASSERT(IS_VALID_FLAG(fQueen)); x = fPawn | (fKnight << 1) | (fBishop << 2) | (fRook << 3) | (fQueen << 4); - + ASSERT((0 <= x) && (x <= 31)); return(x); } @@ -84,29 +84,29 @@ Return value: static FLAG -_TablebasesSaySideWins(IN SEARCHER_THREAD_CONTEXT *ctx, +_TablebasesSaySideWins(IN SEARCHER_THREAD_CONTEXT *ctx, IN ULONG uSide) { SCORE iScore; if (TRUE == ProbeEGTB(ctx, &iScore)) { - if (ctx->sPosition.uToMove == uSide) + if (ctx->sPosition.uToMove == uSide) { return iScore > 0; - } - else + } + else { return iScore < 0; } - } + } return TRUE; } static FLAG -_TablebasesSayDraw(IN SEARCHER_THREAD_CONTEXT *ctx) +_TablebasesSayDraw(IN SEARCHER_THREAD_CONTEXT *ctx) { SCORE iScore; - if (TRUE == ProbeEGTB(ctx, &iScore)) + if (TRUE == ProbeEGTB(ctx, &iScore)) { return iScore == 0; } @@ -114,13 +114,13 @@ _TablebasesSayDraw(IN SEARCHER_THREAD_CONTEXT *ctx) } static FLAG -_TablebasesSayDrawOrWin(IN SEARCHER_THREAD_CONTEXT *ctx, - IN ULONG uSide) +_TablebasesSayDrawOrWin(IN SEARCHER_THREAD_CONTEXT *ctx, + IN ULONG uSide) { SCORE iScore; - if (TRUE == ProbeEGTB(ctx, &iScore)) + if (TRUE == ProbeEGTB(ctx, &iScore)) { - return ((iScore == 0) || + return ((iScore == 0) || ((iScore > 0) && (ctx->sPosition.uToMove == uSide)) || ((iScore < 0) && (ctx->sPosition.uToMove != uSide))); } @@ -128,9 +128,9 @@ _TablebasesSayDrawOrWin(IN SEARCHER_THREAD_CONTEXT *ctx, } -static FLAG -_SanityCheckRecognizers(IN SEARCHER_THREAD_CONTEXT *ctx, - IN SCORE iScore, +static FLAG +_SanityCheckRecognizers(IN SEARCHER_THREAD_CONTEXT *ctx, + IN SCORE iScore, IN ULONG uVal) { ULONG uToMove = ctx->sPosition.uToMove; switch(uVal) { @@ -146,22 +146,34 @@ _SanityCheckRecognizers(IN SEARCHER_THREAD_CONTEXT *ctx, return _TablebasesSaySideWins(ctx, !uToMove); } case RECOGN_LOWER: + // + // iScore is only a LOWER bound: the true score is >= + // iScore, so we can only make a directional claim when the + // bound itself pins one down. iScore > 0 forces a genuine + // win for uToMove; iScore == 0 forces at least a draw. A + // negative lower bound ("at least this bad, could be + // better or worse") licenses no claim about who's actually + // winning, so don't assert one. if (iScore == 0) { return _TablebasesSayDrawOrWin(ctx, uToMove); } else if (iScore > 0) { return _TablebasesSaySideWins(ctx, uToMove); } else { - ASSERT(iScore < 0); - return _TablebasesSaySideWins(ctx, !uToMove); + return TRUE; } case RECOGN_UPPER: + // + // Symmetric reasoning: iScore is only an UPPER bound (true + // score <= iScore). iScore < 0 forces a genuine win for the + // opponent; iScore == 0 forces at least a draw for the + // opponent. A positive upper bound doesn't preclude uToMove + // still winning by less than iScore, so no claim there. if (iScore == 0) { return _TablebasesSayDrawOrWin(ctx, !uToMove); - } else if (iScore > 0) { - return _TablebasesSayDrawOrWin(ctx, !uToMove); - } else { - ASSERT(iScore < 0); + } else if (iScore < 0) { return _TablebasesSaySideWins(ctx, !uToMove); + } else { + return TRUE; } default: ASSERT(FALSE); @@ -169,9 +181,9 @@ _SanityCheckRecognizers(IN SEARCHER_THREAD_CONTEXT *ctx, } } -static FLAG -_NothingBut(IN POSITION *pos, - IN PIECE p, +static FLAG +_NothingBut(IN POSITION *pos, + IN PIECE p, IN ULONG uColor) /** @@ -196,7 +208,7 @@ static FLAG { static PIECE q[] = { KNIGHT, BISHOP, ROOK, QUEEN }; ULONG u; - + if (!(p & PAWN)) { if (pos->uPawnCount[uColor] > 0) return(FALSE); @@ -213,8 +225,8 @@ static FLAG } #endif -static ULONG -_RecognizeKK(IN SEARCHER_THREAD_CONTEXT *ctx, +static ULONG +_RecognizeKK(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** @@ -237,8 +249,8 @@ Return value: return(RECOGN_EXACT); } -static ULONG -_RecognizeKBK(IN SEARCHER_THREAD_CONTEXT *ctx, +static ULONG +_RecognizeKBK(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** @@ -263,7 +275,7 @@ Return value: ULONG u; ULONG uAdjacent; POSITION *pos = &ctx->sPosition; - + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && (pos->uNonPawnCount[BLACK][0] <= 3)); ASSERT(_NothingBut(pos, BISHOP, WHITE)); @@ -272,7 +284,7 @@ Return value: // // Recognize KBKB as a draw unless there's a cornered king (in // which case it may be a mate-in-1) - // + // if ((pos->uNonPawnCount[WHITE][0] == 2) && (pos->uNonPawnCount[BLACK][0] == 2)) { @@ -283,20 +295,20 @@ Return value: return(RECOGN_EXACT); } } - + // // Otherwise we want to deal with KB+ vs lone K. KBKBB etc are // too hard to recognize. - // + // if ((pos->uNonPawnCount[WHITE][0] != 1) && (pos->uNonPawnCount[BLACK][0] != 1)) { return(UNRECOGNIZED); } - + // // If we get here then one side has no pieces (except the king). - // + // uStrong = BLACK; if (pos->uNonPawnCount[WHITE][0] > 1) { @@ -308,7 +320,7 @@ Return value: // // KB vs K is a draw, KB+ vs K is still a draw if all bishops are the // same color. - // + // uBishops = pos->uNonPawnCount[uStrong][BISHOP]; if ((uBishops == 1) || (pos->uWhiteSqBishopCount[uStrong] == 0) || @@ -317,18 +329,18 @@ Return value: *piScore = 0; return(RECOGN_EXACT); } - + // // If we get here the strong side has more than one bishop and has // at least one bishop on each color. - // + // // // If the weak king is next to a strong side piece, fail to // recognize since the weak king may take the bishop with the // move. Note: we allow the weak king to be adjacent to up to one // enemy bishop as long as it's the strong side's turn to move. - // + // cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; ASSERT(DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]) > 1); uAdjacent = 0; @@ -366,8 +378,8 @@ Return value: // This is a recognized win for the strong side. Compute a score // that encourages cornering the weak king and making progress // towards a checkmate. - // - *piScore = (pos->iMaterialBalance[uStrong] + VALUE_QUEEN - + // + *piScore = (pos->iMaterialBalance[uStrong] + VALUE_QUEEN - (u * 16) - (CORNER_DISTANCE(cWeakKing) * 32)); ASSERT(IS_VALID_SCORE(*piScore)); if (pos->uToMove != uStrong) @@ -378,8 +390,8 @@ Return value: return(RECOGN_LOWER); } -static ULONG -_RecognizeKNK(IN SEARCHER_THREAD_CONTEXT *ctx, +static ULONG +_RecognizeKNK(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** @@ -405,11 +417,11 @@ Return value: (pos->uNonPawnCount[BLACK][0] <= 3)); ASSERT(_NothingBut(pos, KNIGHT, WHITE)); ASSERT(_NothingBut(pos, KNIGHT, BLACK)); - + // // KNKN is a draw unless someone has a K in the corner (in which case, // with the friend knight in the way, there's a possible mate) - // + // if ((pos->uNonPawnCount[WHITE][0] == 2) && (pos->uNonPawnCount[BLACK][0] == 2)) { @@ -421,20 +433,20 @@ Return value: } return(UNRECOGNIZED); } - + // // KNNKN etc... unrecognized. Heinz says "exceptional wins possible for // any side by mates in seven or less moves." TODO: add this knowledge. - // + // if ((pos->uNonPawnCount[WHITE][0] != 1) || (pos->uNonPawnCount[BLACK][0] != 1)) { return(UNRECOGNIZED); } - + // // If we get here somebody has no pieces (except a lone king). - // + // uStrong = WHITE; if (pos->uNonPawnCount[BLACK][0] > 1) { @@ -449,7 +461,7 @@ Return value: // Everything else in here is a draw. // ASSERT(pos->uNonPawnCount[uStrong][0] < 4); - if (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0])) + if (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0])) { return(UNRECOGNIZED); } @@ -458,8 +470,18 @@ Return value: } -static ULONG -_RecognizeKBNK(IN SEARCHER_THREAD_CONTEXT *ctx, +// +// DISABLED -- not currently registered in InitializeInteriorNodeRecognizers +// (see the comment there). testrecogn.c's EGTB cross-check found a +// counterexample in the bare-lone-king mating branch below (1k6/8/8/8/ +// 1bn5/8/1K6/8 w): the classic KBN-vs-K "wrong corner" subtlety -- +// mate is only forceable in the corner matching the bishop's square +// color, and this function's bail-out conditions don't fully capture +// that. Left in place as a starting point; validate any future fix +// against testrecogn.c's KNBK case before re-registering. +// +static ULONG __attribute__((unused)) +_RecognizeKBNK(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** @@ -488,22 +510,22 @@ Return value: (pos->uNonPawnCount[BLACK][0] <= 3)); ASSERT(_NothingBut(pos, BISHOP | KNIGHT, WHITE)); ASSERT(_NothingBut(pos, BISHOP | KNIGHT, BLACK)); - + if ((pos->uNonPawnCount[WHITE][0] > 1) && (pos->uNonPawnCount[BLACK][0] > 1)) { // // Do not recognize stuff like KNNKB or KNKBB etc... - // + // if (pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0] > 4) { return(UNRECOGNIZED); } - + // // This is KNKB; unless someone's king is on the edge, // recognize a draw. - // + // ASSERT((pos->uNonPawnCount[WHITE][0] == 2) && (pos->uNonPawnCount[BLACK][0] == 2)); if (ON_EDGE(pos->cNonPawns[WHITE][0]) || @@ -517,7 +539,7 @@ Return value: // // If we get here we are in a KBNK endgame. - // + // uStrong = WHITE; if (pos->uNonPawnCount[BLACK][0] > 1) { @@ -540,11 +562,11 @@ Return value: { return(UNRECOGNIZED); } - + // // Don't recognize anything if the weak king is next to a strong side's // piece. - // + // uAdjacent = 0; for (u = 1; u < pos->uNonPawnCount[uStrong][0]; u++) { @@ -559,7 +581,7 @@ Return value: // Don't recognize if the two kings are close enough to each other // that there might be a stalemate if the weak side is on move and // on the edge. - // + // ASSERT(IS_ON_BOARD(pos->cNonPawns[uStrong][0])); ASSERT(IS_KING(pos->rgSquare[pos->cNonPawns[uStrong][0]].pPiece)); u = DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]); @@ -577,7 +599,7 @@ Return value: // Calculate a score that grabs the search's attention and makes // progress towards driving the weak king to the correct corner to // mate him. - // + // if (pos->uWhiteSqBishopCount[uStrong] > 0) { uDist = WHITE_CORNER_DISTANCE(cWeakKing); @@ -587,7 +609,7 @@ Return value: uDist = BLACK_CORNER_DISTANCE(cWeakKing); } ASSERT((0 <= uDist) && (uDist <= 7)); - + *piScore = (pos->iMaterialBalance[uStrong] + (7 * VALUE_PAWN) - (uDist * 32) - (u * 16)); ASSERT(IS_VALID_SCORE(*piScore)); @@ -600,14 +622,35 @@ Return value: } -static ULONG -_RecognizeKNKP(IN SEARCHER_THREAD_CONTEXT *ctx, +// +// RE-ENABLED for the exact single-knight/single-pawn case only, backed +// by exhaustive (not sampled) proof: testrecogn.c's +// TestRecognExhaustiveKNKP enumerates every legal KNKP position with +// exactly one knight and one pawn -- 10.2M raw square placements, 5.2M +// of them actually checked against real Syzygy EGTB data (the rest +// UNRECOGNIZED or outside coverage) -- and found zero disagreements. +// The claim this function makes ("at best a draw for the pawn side") +// genuinely is a two-knights-can't-force-mate fact in that exact +// sub-case. +// +// It is FALSE once a second knight or a second pawn enters the +// picture, though -- testrecogn.c's random sampling found two live +// counterexamples in those cases (a real forced loss for the pawn +// side despite its king being right next to its own pawn) before this +// function was tightened to exclude them via the count==1 checks +// below. Don't loosen those checks back to "<=2 knights" / "any pawn +// count" without first extending the exhaustive verifier to cover +// whatever case is being added and confirming zero disagreements the +// same way. +// +ULONG +_RecognizeKNKP(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** Routine description: - Recognize KN+KP+ positions. + Recognize KNKP positions: exactly one knight vs exactly one pawn. Parameters: @@ -622,14 +665,14 @@ Return value: { ULONG uStrong; POSITION *pos = &ctx->sPosition; - + ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && (pos->uNonPawnCount[BLACK][0] <= 3)); ASSERT(_NothingBut(pos, PAWN | KNIGHT, WHITE)); ASSERT(_NothingBut(pos, PAWN | KNIGHT, BLACK)); // - // Call the side with knight(s) "strong" + // Call the side with the knight "strong" // uStrong = WHITE; if (pos->uNonPawnCount[BLACK][0] > 1) @@ -640,18 +683,21 @@ Return value: ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); // - // Don't recognize KNNKP or KNKP with K on edge - // - if ((pos->uNonPawnCount[uStrong][KNIGHT] > 2) || + // Exhaustively proven correct only for exactly one knight and + // exactly one pawn (see the comment above) -- also still exclude + // K on the edge, per the original ON_EDGE reasoning. + // + if ((pos->uNonPawnCount[uStrong][KNIGHT] != 1) || + (pos->uPawnCount[FLIP(uStrong)] != 1) || (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0]))) { return(UNRECOGNIZED); } // - // This is at least a draw for the side with the pawn(s) and at - // best a draw for the side with the knight(s) - // + // This is at least a draw for the side with the pawn and at best a + // draw for the side with the knight + // *piScore = 0; if (pos->uToMove == uStrong) { @@ -662,7 +708,7 @@ Return value: static ULONG -_RecognizeKBKP(IN SEARCHER_THREAD_CONTEXT *ctx, +_RecognizeKBKP(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** @@ -711,7 +757,7 @@ Return value: // // Construct a strong side bitboard of pawn locations - // + // bb = 0ULL; for (u = 0; u < pos->uPawnCount[uStrong]; u++) { @@ -719,25 +765,25 @@ Return value: ASSERT(IS_ON_BOARD(c)); bb |= COOR_TO_BB(c); } - + if ((pos->uNonPawnCount[BLACK][0] + pos->uPawnCount[BLACK] > 1) && (pos->uNonPawnCount[WHITE][0] + pos->uPawnCount[WHITE] > 1)) { // // Neither side has a lone king. This is either KBKP+ or // KBP+KP+. - // + // if (pos->uPawnCount[uStrong] > 0) { // // Strong side can maybe take an adjacent pawn and survive the // bad bishop. - // + // if (uStrong == pos->uToMove) { return(UNRECOGNIZED); } - + // // Make sure the strong side has the right color bishop // for his pawns. @@ -750,9 +796,9 @@ Return value: { goto at_best_draw_for_strong; } - + if (!(bb & ~BBFILE[H]) && - (pos->uWhiteSqBishopCount[WHITE] == + (pos->uWhiteSqBishopCount[WHITE] == pos->uNonPawnCount[WHITE][BISHOP]) && (DISTANCE(cWeakKing, H8) <= 1)) { @@ -762,13 +808,13 @@ Return value: else { if (!(bb & ~BBFILE[A]) && - (pos->uWhiteSqBishopCount[BLACK] == + (pos->uWhiteSqBishopCount[BLACK] == pos->uNonPawnCount[BLACK][BISHOP]) && (DISTANCE(cWeakKing, A1) <= 1)) { goto at_best_draw_for_strong; } - + if (!(bb & ~BBFILE[H]) && (pos->uWhiteSqBishopCount[BLACK] == 0) && (DISTANCE(cWeakKing, H1) <= 1)) @@ -791,13 +837,13 @@ Return value: } goto at_best_draw_for_strong; } - } - else + } + else { // // KBPK: make sure the bishop is the right color. This time // there is no need to check for on-move. - // + // ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); ASSERT(pos->uNonPawnCount[uStrong][0] > 1); @@ -816,11 +862,11 @@ Return value: { goto draw; } - } - else + } + else { if (!(bb & ~BBFILE[A]) && - (pos->uWhiteSqBishopCount[BLACK] == + (pos->uWhiteSqBishopCount[BLACK] == pos->uNonPawnCount[BLACK][BISHOP]) && (DISTANCE(cWeakKing, A1) <= 1)) { @@ -836,7 +882,7 @@ Return value: return(UNRECOGNIZED); } #ifdef DEBUG - UtilPanic(SHOULD_NOT_GET_HERE, + UtilPanic(SHOULD_NOT_GET_HERE, NULL, NULL, NULL, NULL, __FILE__, __LINE__); #endif @@ -856,7 +902,7 @@ Return value: static void _GetPassersCriticalSquares(IN ULONG uColor, - IN COOR cPawn, + IN COOR cPawn, IN OUT COOR *cSquare) /** @@ -884,9 +930,9 @@ Return value: **/ { - static COOR cCriticalSquare[2][128] = + static COOR cCriticalSquare[2][128] = { - { + { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, 0x61, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x66, 0,0,0,0,0,0,0,0, 0x61, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x66, 0,0,0,0,0,0,0,0, @@ -896,7 +942,7 @@ Return value: 0x61, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x66, 0,0,0,0,0,0,0,0, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, }, - { + { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, 0x11, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x16, 0,0,0,0,0,0,0,0, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x16, 0,0,0,0,0,0,0,0, @@ -905,14 +951,14 @@ Return value: 0x11, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x16, 0,0,0,0,0,0,0,0, 0x11, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x16, 0,0,0,0,0,0,0,0, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0,0,0,0,0,0,0,0, - } + } }; ULONG uFile = FILE(cPawn); ASSERT(IS_VALID_COLOR(uColor)); ASSERT(IS_ON_BOARD(cPawn)); ASSERT((RANK(cPawn) != 1) && (RANK(cPawn) != 8)); - + if ((uFile == A) || (uFile == H)) { cSquare[0] = cCriticalSquare[uColor][cPawn]; @@ -923,19 +969,22 @@ Return value: cSquare[1] = cCriticalSquare[uColor][cPawn]; cSquare[0] = cSquare[1] - 1; cSquare[2] = cSquare[1] + 1; - + end: - ASSERT(cSquare[0] != 0); - ASSERT(cSquare[1] != 0); - ASSERT(cSquare[2] != 0); + // + // Note: don't assert cSquare[n] != 0 here -- COOR value 0 is A8, a + // perfectly legal critical square (e.g. a black pawn's B-file + // critical square at rank 7 has its adjacent/rook-file neighbor at + // A8), not a sentinel for "uninitialized". IS_ON_BOARD is the + // correct validity check. ASSERT(IS_ON_BOARD(cSquare[0])); ASSERT(IS_ON_BOARD(cSquare[1])); ASSERT(IS_ON_BOARD(cSquare[2])); } -static ULONG -_RecognizeKPK(IN SEARCHER_THREAD_CONTEXT *ctx, +static ULONG +_RecognizeKPK(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) /** @@ -976,7 +1025,7 @@ Return value: if (pHash->u64Key == pos->u64PawnSig) { pos->iScore[BLACK] = pos->iScore[WHITE] = 0; - if (TRUE == EvalPasserRaces(pos, pHash)) + if (TRUE == _EvalPasserRacesAgainstLoneKings(pos, pHash)) { // // Someone wins. @@ -1034,7 +1083,7 @@ Return value: ASSERT(pos->uPawnCount[uStrong] > 0); uWeak = FLIP(uStrong); ASSERT(pos->uPawnCount[uWeak] == 0); - + if (pos->uPawnCount[uStrong] > 1) { *piScore = 0; @@ -1049,12 +1098,12 @@ Return value: // The side with pawns has only one pawn, do some more // sophisticated analysis here to spot winning KPK // configurations earlier by using "critical squares" - // + // ASSERT(pos->uPawnCount[uStrong] == 1); cPawn = pos->cPawns[uStrong][0]; ASSERT(IS_ON_BOARD(cPawn)); ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); - + // // Step 1: the strong king must be closer to the pawn than // the weak king. @@ -1072,12 +1121,12 @@ Return value: _GetPassersCriticalSquares(uStrong, cPawn, cCritical); for (u = 0; u < 3; u++) { - uDist[uStrong] = DISTANCE(pos->cNonPawns[uStrong][0], + uDist[uStrong] = DISTANCE(pos->cNonPawns[uStrong][0], cCritical[u]); ASSERT((0 <= uDist[uStrong]) && (uDist[uStrong] <= 7)); - uDist[uWeak] = DISTANCE(pos->cNonPawns[uWeak][0], + uDist[uWeak] = DISTANCE(pos->cNonPawns[uWeak][0], cCritical[u]); - + // // Assume if the weak side is on move he will move // towards the critical square. Also assume that @@ -1094,7 +1143,7 @@ Return value: ASSERT((0 <= uDist[uWeak]) && (uDist[uWeak] <= 7)); if (uDist[uStrong] < uDist[uWeak]) { - cQueen = + cQueen = QUEENING_SQUARE_BY_COLOR_FILE[uStrong][FILE(cPawn)]; *piScore = (pos->iMaterialBalance[uStrong] + VALUE_QUEEN + (2 * VALUE_PAWN) - @@ -1127,9 +1176,9 @@ Return value: } -static void -_NewRecognizer(IN RECOGNIZER *pFunct, - IN ULONG uWhiteSig, +static void +_NewRecognizer(IN RECOGNIZER *pFunct, + IN ULONG uWhiteSig, IN ULONG uBlackSig) /** @@ -1154,7 +1203,7 @@ Return value: g_pRecognizers[RECOGN_INDEX(uWhiteSig, uBlackSig)] = pFunct; } -void +void InitializeInteriorNodeRecognizers(void) /** @@ -1180,7 +1229,7 @@ Return value: _MakeMaterialSig(0, 0, 0, 0, 0), _MakeMaterialSig(0, 0, 0, 0, 0)); - // KB+K P N B R Q + // KB+K P N B R Q _NewRecognizer(_RecognizeKBK, _MakeMaterialSig(0, 0, 1, 0, 0), _MakeMaterialSig(0, 0, 0, 0, 0)); @@ -1189,61 +1238,67 @@ Return value: _NewRecognizer(_RecognizeKBK, _MakeMaterialSig(0, 0, 1, 0, 0), _MakeMaterialSig(0, 0, 1, 0, 0)); - - // KN+K P N B R Q + + // KN+K P N B R Q _NewRecognizer(_RecognizeKNK, - _MakeMaterialSig(0, 1, 0, 0, 0), + _MakeMaterialSig(0, 1, 0, 0, 0), _MakeMaterialSig(0, 0, 0, 0, 0)); - // KN+KN+ P N B R Q - _NewRecognizer(_RecognizeKNK, - _MakeMaterialSig(0, 1, 0, 0, 0), + // KN+KN+ P N B R Q + _NewRecognizer(_RecognizeKNK, + _MakeMaterialSig(0, 1, 0, 0, 0), _MakeMaterialSig(0, 1, 0, 0, 0)); - // KN+KB+ P N B R Q - _NewRecognizer(_RecognizeKBNK, - _MakeMaterialSig(0, 1, 0, 0, 0), - _MakeMaterialSig(0, 0, 1, 0, 0)); - - // KN+B+K P N B R Q - _NewRecognizer(_RecognizeKBNK, - _MakeMaterialSig(0, 1, 1, 0, 0), - _MakeMaterialSig(0, 0, 0, 0, 0)); + // KN+KB+ and KN+B+K disabled: _RecognizeKBNK's lone-king mating + // branch was found wrong by testrecogn.c's EGTB cross-check (the + // KBN-vs-K "wrong corner" subtlety -- see the comment on + // _RecognizeKBNK above). Not registered until that's fixed. + // + // _NewRecognizer(_RecognizeKBNK, + // _MakeMaterialSig(0, 1, 0, 0, 0), + // _MakeMaterialSig(0, 0, 1, 0, 0)); + // + // _NewRecognizer(_RecognizeKBNK, + // _MakeMaterialSig(0, 1, 1, 0, 0), + // _MakeMaterialSig(0, 0, 0, 0, 0)); - // KN+KP+ P N B R Q - _NewRecognizer(_RecognizeKNKP, - _MakeMaterialSig(1, 0, 0, 0, 0), + // KN+KP+ -- re-enabled for exactly one knight vs exactly one pawn + // only (the function itself bails to UNRECOGNIZED for anything + // else); see the comment on _RecognizeKNKP for the exhaustive + // proof backing this. P N B R Q + _NewRecognizer(_RecognizeKNKP, + _MakeMaterialSig(1, 0, 0, 0, 0), _MakeMaterialSig(0, 1, 0, 0, 0)); - // KB+KP+ P N B R Q - _NewRecognizer(_RecognizeKBKP, - _MakeMaterialSig(1, 0, 0, 0, 0), + // KB+KP+ P N B R Q + _NewRecognizer(_RecognizeKBKP, + _MakeMaterialSig(1, 0, 0, 0, 0), _MakeMaterialSig(0, 0, 1, 0, 0)); - - // KP+B+KP+ P N B R Q - _NewRecognizer(_RecognizeKBKP, - _MakeMaterialSig(1, 0, 1, 0, 0), + + // KP+B+KP+ P N B R Q + _NewRecognizer(_RecognizeKBKP, + _MakeMaterialSig(1, 0, 1, 0, 0), _MakeMaterialSig(1, 0, 0, 0, 0)); - + // KP+B+K P N B R Q - _NewRecognizer(_RecognizeKBKP, - _MakeMaterialSig(1, 0, 1, 0, 0), + _NewRecognizer(_RecognizeKBKP, + _MakeMaterialSig(1, 0, 1, 0, 0), _MakeMaterialSig(0, 0, 0, 0, 0)); - // KP+K P N B R Q - _NewRecognizer(_RecognizeKPK, - _MakeMaterialSig(0, 0, 0, 0, 0), + // KP+K P N B R Q + _NewRecognizer(_RecognizeKPK, + _MakeMaterialSig(0, 0, 0, 0, 0), _MakeMaterialSig(1, 0, 0, 0, 0)); // KP+KP+ P N B R Q - _NewRecognizer(_RecognizeKPK, - _MakeMaterialSig(1, 0, 0, 0, 0), + _NewRecognizer(_RecognizeKPK, + _MakeMaterialSig(1, 0, 0, 0, 0), _MakeMaterialSig(1, 0, 0, 0, 0)); } -ULONG +ULONG RecognLookup(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore, IN FLAG fProbeEGTB) @@ -1277,7 +1332,7 @@ Return value: // // Try interior node recognizers - // + // if ((pos->uNonPawnCount[WHITE][0] <= 3) && (pos->uNonPawnCount[BLACK][0] <= 3)) { @@ -1309,11 +1364,16 @@ Return value: } // - // Try EGTB probe as long as some conditions are met - // - if ((FALSE != fProbeEGTB) && - ((pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0] + - pos->uPawnCount[WHITE] + pos->uPawnCount[BLACK]) <= 5)) + // Try EGTB probe. No piece-count gate here: ProbeEGTB already + // checks the position's piece count against the dynamic + // TB_LARGEST (set from whatever tablebase files Fathom actually + // found at init), so hardcoding a ceiling here would only ever + // make this stricter than what's really installed, silently + // capping us below the on-disk tables (e.g. if 6-man WDL files are + // ever added alongside the 5-man set already present -- see + // CLAUDE.md). + // + if (FALSE != fProbeEGTB) { if (TRUE == ProbeEGTB(ctx, &iScore)) { |
