summaryrefslogtreecommitdiff
path: root/src/recogn.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/recogn.c')
-rw-r--r--src/recogn.c388
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))
{